<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DolphinDB</title>
    <description>The latest articles on DEV Community by DolphinDB (@dolphindb).</description>
    <link>https://dev.to/dolphindb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F766486%2Fde2034e3-e2f4-433c-8a5b-4b1330770161.png</url>
      <title>DEV Community: DolphinDB</title>
      <link>https://dev.to/dolphindb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dolphindb"/>
    <language>en</language>
    <item>
      <title>Building an Event-Driven Execution Engine: Lessons from an Iceberg Algorithm</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Thu, 30 Jul 2026 08:36:54 +0000</pubDate>
      <link>https://dev.to/dolphindb/building-an-event-driven-execution-engine-lessons-from-an-iceberg-algorithm-2h38</link>
      <guid>https://dev.to/dolphindb/building-an-event-driven-execution-engine-lessons-from-an-iceberg-algorithm-2h38</guid>
      <description>&lt;p&gt;Suppose you need to execute a large order. Sending it to the market all at once can reveal your trading intent and lead to poor execution.&lt;/p&gt;

&lt;p&gt;That's why institutional traders use Iceberg algorithms, which split a large parent order into smaller child orders and expose only a fraction of the total volume to the market.&lt;/p&gt;

&lt;p&gt;The real challenge, however, begins after each child order is submitted.&lt;/p&gt;

&lt;p&gt;It may be fully filled, partially filled, canceled, or require repricing as market conditions change. Each outcome determines what should happen next.&lt;/p&gt;

&lt;p&gt;An Iceberg strategy is therefore not just about splitting orders—it's about continuously reacting to market feedback.&lt;/p&gt;

&lt;p&gt;That's exactly the focus of this article: How can we build an execution system that makes those decisions in real time?&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Traditional Backtesting Falls Short
&lt;/h1&gt;

&lt;p&gt;Traditional backtesting frameworks often simplify order execution by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Assuming every order is filled immediately at the expected price.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Polling order status at fixed intervals instead of reacting to events.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ignoring real execution feedback, such as partial fills, cancellations, and changing market depth.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these assumptions make simulations easier, they also create a significant gap between backtesting and live trading.&lt;/p&gt;

&lt;p&gt;For execution algorithms like Iceberg, performance depends not only on how orders are split, but also on how quickly the system detects market events and adapts its behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  A more realistic execution framework requires two key capabilities.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, it should reproduce how orders are actually matched based on real market data, order book dynamics, and exchange matching rules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Second, it should process execution events as they occur, rather than relying on inefficient polling.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DolphinDB addresses these requirements with two complementary components: an &lt;strong&gt;Order Matching Simulator&lt;/strong&gt;  and a &lt;strong&gt;Complex Event Processing (CEP) engine&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The matching engine simulator reproduces the complete execution lifecycle using real market data and exchange rules, while the CEP engine converts fills, cancellations, timeouts, and other trading feedback into real-time events that drive subsequent execution decisions.&lt;/p&gt;

&lt;p&gt;The following shows how the CEP engine drives an Iceberg execution workflow in real time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Does Trade Execution Need to Be Event-Driven
&lt;/h1&gt;

&lt;p&gt;The question is no longer what the algorithm should do, but how the execution system should do it.&lt;/p&gt;

&lt;p&gt;An execution engine must maintain the state of every parent order, react to execution feedback, and determine the next action as market conditions evolve.&lt;/p&gt;

&lt;p&gt;Traditional systems typically solve this by polling order status at fixed intervals. As the number of active orders grows, however, polling becomes increasingly inefficient while execution logic becomes harder to manage.&lt;/p&gt;

&lt;p&gt;A more natural approach is to let execution events drive the workflow. Instead of repeatedly checking every order, the system reacts only when something meaningful happens, such as a fill, a cancellation, or a price update.&lt;/p&gt;

&lt;p&gt;This is exactly the problem that Complex Event Processing (CEP) is designed to solve.&lt;/p&gt;

&lt;p&gt;In an execution system, typical events include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A parent order enters the system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A child order is filled or partially filled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A cancellation confirmation is received.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The market price changes.&lt;/p&gt;

&lt;p&gt;CEP provides a natural way to build this type of system. It continuously consumes business events, detects patterns and relationships between them, and triggers the appropriate actions based on predefined rules.&lt;/p&gt;

&lt;p&gt;For an Iceberg strategy, the CEP engine continuously monitors execution events such as fills, partial fills, cancellations, and price changes, enabling real-time execution decisions.&lt;/p&gt;

&lt;p&gt;The result is an execution workflow that more closely resembles how real trading systems operate, while keeping the strategy logic clear, responsive, and easy to extend.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Components Does an Event-Driven Execution System Need
&lt;/h1&gt;

&lt;p&gt;If we view trade execution as a continuous stream of events, the system must answer four questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Where does market data come from?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Where is the current order state stored?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Who simulates exchange matching and generates execution reports?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Who decides what to do after an execution event?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CEP engine orchestrates the execution workflow, but it is only one part of the system. A complete event-driven execution architecture also requires market data, order matching, and state management working together.&lt;/p&gt;

&lt;p&gt;The overall architecture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqa1qp2tt3rynoow8nas4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqa1qp2tt3rynoow8nas4.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;br&gt;
The system forms a continuous feedback loop. When a parent order enters the system, the CEP engine applies the Iceberg algorithm to split it into child orders.&lt;/p&gt;

&lt;p&gt;These child orders are then sent to the Order Matching Simulator, where they are matched against replayed market data. As orders are filled, partially filled, or canceled, the matching engine immediately generates the corresponding execution reports and sends them back to the CEP engine.&lt;/p&gt;

&lt;p&gt;Based on this feedback, the CEP engine determines the next action, such as submitting another child order, adjusting the execution strategy, or completing the parent order.&lt;/p&gt;

&lt;p&gt;This cycle repeats until the entire parent order has been executed, with every step driven by real-time market feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Order Matching Simulator
&lt;/h2&gt;

&lt;p&gt;An execution system can continuously generate child orders, but without execution feedback from an exchange, the workflow comes to a halt. To close the loop, we need a matching environment that behaves like a real exchange.&lt;/p&gt;

&lt;p&gt;In this example, we use DolphinDB's Order Matching Simulator plugin.&lt;/p&gt;

&lt;p&gt;It accepts two inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Replayed market data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User-submitted orders&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The simulator then applies exchange matching rules to process order queuing, fills, partial fills, and cancellations, ultimately producing execution reports for the CEP engine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2g6sgus6qz1b602g4b18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2g6sgus6qz1b602g4b18.png" alt=" " width="619" height="428"&gt;&lt;/a&gt;&lt;br&gt;
For an Iceberg strategy,what really matters is the execution feedback it produces.&lt;/p&gt;

&lt;p&gt;As orders are matched, the Order Matching Simulator continuously generates execution reports, including events such as partial fills and full fills. These reports are then subscribed to, converted into trading events, and published to the event stream monitored by the CEP engine.&lt;/p&gt;

&lt;p&gt;Whenever a new execution event arrives, the CEP engine immediately updates the state of the parent order and determines the next action—whether to submit another child order, adjust the order price, or complete the execution.&lt;/p&gt;

&lt;h1&gt;
  
  
  How CEP Drives the Order Lifecycle
&lt;/h1&gt;

&lt;p&gt;Market data continuously flows into the system, and the matching engine continuously produces execution reports. Neither component, however, makes execution decisions.&lt;/p&gt;

&lt;p&gt;Their job is simply to describe what is happening in the market. The logic that decides when to submit an order, cancel an order, or complete the execution is handled by the CEP Monitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Monitor per Parent Order
&lt;/h2&gt;

&lt;p&gt;In a traditional execution engine, the system is responsible for tracking every active parent order. As the number of orders grows, the execution engine must maintain an increasingly complex state machine while repeatedly scanning active orders to determine which ones require further processing.&lt;/p&gt;

&lt;p&gt;The CEP engine takes a different approach.&lt;/p&gt;

&lt;p&gt;Whenever a new parent order enters the system, it creates a dedicated Monitor for that order.&lt;/p&gt;

&lt;p&gt;The Monitor acts as the order's execution controller, managing its entire lifecycle—from the moment the parent order is received until the final child order is executed.&lt;/p&gt;

&lt;p&gt;Multiple execution workflows can run independently and in parallel without interfering with one another.&lt;br&gt;
`class IceBergSplitMonitor : SplitMonitor {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def startPlaceOrder(parentOrder){...}

def placeOrder(){...}

def cancelOrder(){...}

def updateSubOrder(){...}

def forkParentOrderMonitor(parentOrder){...}

def onload(){
    addEventListener(forkParentOrderMonitor,
                     "ParentOrder",
                     ,
                     "all")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;br&gt;
The complete implementation includes several supporting functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;onload() continuously listens for new parent orders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;forkParentOrderMonitor() creates a dedicated Monitor for each parent order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;startPlaceOrder() initializes the execution state and determines whether execution should start immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;placeOrder() generates the next child order based on the current execution state.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once these steps are complete, the Monitor enters the execution phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Is placeOrder() the Core of the System
&lt;/h2&gt;

&lt;p&gt;Although its name suggests otherwise, placeOrder() does much more than submit an order—it starts a new execution cycle.&lt;/p&gt;

&lt;p&gt;Each time placeOrder() is called, three things happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A new child order is generated based on the current state of the parent order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The child order is sent to the Order Matching Simulator.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An event listener is registered for that child order.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key is not the order submission itself, but telling the CEP engine: From this point on, notify me whenever something happens to this order.&lt;/p&gt;

&lt;p&gt;This is the fundamental difference between an event-driven system and a traditional execution workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Monitor Runs Only When Events Arrive
&lt;/h2&gt;

&lt;p&gt;A Monitor does not run continuously.&lt;/p&gt;

&lt;p&gt;After submitting a child order, it immediately suspends execution and consumes no computing resources while waiting for the next event.&lt;/p&gt;

&lt;p&gt;When an execution event—such as a fill, partial fill, or timeout—arrives, the CEP engine wakes the corresponding Monitor, which resumes execution exactly where it left off.&lt;/p&gt;

&lt;p&gt;In other words, each Monitor repeatedly follows the same pattern:&lt;/p&gt;

&lt;p&gt;Event arrives → Execute for a few milliseconds → Suspend → Wait for the next event&lt;/p&gt;

&lt;p&gt;From the strategy's perspective, only three types of events matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Child order fully filled: Submit the next child order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Child order partially filled: Update the execution state and continue waiting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Child order timed out: Cancel the current order and submit a new quote based on the latest market conditions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As fills, partial fills, and timeouts continue to occur, the Monitor keeps updating the execution state and advancing the next round of order splitting until the parent order is fully executed.&lt;/p&gt;

&lt;p&gt;At that point, the lifecycle of the Iceberg strategy comes to an end.&lt;/p&gt;

&lt;h1&gt;
  
  
  Walk Through the Complete Execution Flow
&lt;/h1&gt;

&lt;p&gt;Next, we'll walk through a complete Iceberg execution example to see how the CEP engine drives the lifecycle of a parent order while the Dashboard visualizes the execution state in real time.&lt;/p&gt;

&lt;p&gt;To reproduce the example, we prepared historical market data, a simulated matching environment, and a Dashboard configuration. &lt;/p&gt;

&lt;p&gt;Once the system is started, submitting a single parent order to the CEP engine is enough to trigger the entire execution workflow automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2u2jipzpq1n3i30u6tbe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2u2jipzpq1n3i30u6tbe.gif" alt=" " width="760" height="342"&gt;&lt;/a&gt;&lt;br&gt;
For the complete implementation, click&lt;a href="https://docs.dolphindb.cn/en/Tutorials/order_splitting_with_cep_advanced.html" rel="noopener noreferrer"&gt; CEP Engine Applications: Advanced Tutorial on Implementing an Algorithmic Order-Splitting System.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We used the Iceberg algorithm as an example to build an event-driven order execution system with the DolphinDB CEP engine. From shared state management and real-time market data to the matching engine simulator and CEP Monitors, each component plays a role in forming a complete execution workflow.&lt;/p&gt;

&lt;p&gt;The gap between backtesting and live trading is often not caused by the strategy itself, but by how the strategy reacts to market feedback during execution. Iceberg is just one execution algorithm. The broader value of CEP lies in providing a more natural way to manage the entire order lifecycle. Instead of relying on continuous polling, the system reacts to fills, market updates, timeouts, and other execution events as they occur, allowing the execution workflow to progress efficiently and naturally.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Stay connected with DolphinDB by following us on &lt;a href="https://x.com/DolphinDB_Inc" rel="noopener noreferrer"&gt;X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/14792031/admin/page-posts/published/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for the latest updates, technical articles, and product news. For more information, visit our &lt;a href="https://dolphindb.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>database</category>
      <category>dolphindb</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>From Research Report to Validated Alpha: Let AI Build, Debug, and Evaluate Your Factors</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 29 Jul 2026 08:47:10 +0000</pubDate>
      <link>https://dev.to/dolphindb/from-research-report-to-validated-alpha-let-ai-build-debug-and-evaluate-your-factors-4b09</link>
      <guid>https://dev.to/dolphindb/from-research-report-to-validated-alpha-let-ai-build-debug-and-evaluate-your-factors-4b09</guid>
      <description>&lt;p&gt;Reading a factor research report is rarely the hard part. Implementing it is.&lt;/p&gt;

&lt;p&gt;Before a factor can be validated, someone has to translate formulas into code, map variables to database fields, debug execution errors, and run evaluation with tools like Alphalens. If the results aren't satisfactory, the entire process starts over.Much of factor research is still repetitive engineering rather than research itself.&lt;/p&gt;

&lt;p&gt;The Research Report Analysis &amp;amp; Factor Derivation module in DolphinDB Starfish automates that entire pipeline—from report parsing to factor evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  One PDF Away from a Runnable Factor
&lt;/h2&gt;

&lt;p&gt;Turning a research paper into a production-ready factor traditionally involves three major hurdles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Writing implementation code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fixing execution errors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Running factor evaluation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Starfish, the workflow becomes three simple steps.&lt;/p&gt;

&lt;p&gt;Step 1. Upload a Research Report&lt;br&gt;
Simply upload a PDF. AI automatically extracts the factor name, mathematical formula, variables, and economic intuition from the report. What used to require manually reading and annotating the document can now be completed in minutes.&lt;/p&gt;

&lt;p&gt;Users can still review and edit the extracted content when necessary, combining AI automation with human verification.&lt;/p&gt;

&lt;p&gt;Step 2. Choose Your Data Sources&lt;br&gt;
Select the database tables AI is allowed to access, and let it handle the rest.&lt;/p&gt;

&lt;p&gt;Step 3. Let AI Build, Test, and Evaluate the Factor&lt;br&gt;
This is where most of the time savings come from.&lt;/p&gt;

&lt;p&gt;After you select the data tables, Starfish automatically understands the schema, maps report variables to database fields, generates factor code, and executes it in a real environment. If the code fails, the error is fed back to the language model, which fixes the implementation and retries automatically until it runs successfully. Once it does, Starfish immediately evaluates the factor with Alphalens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgg9wtiifinq9bvngkgm0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgg9wtiifinq9bvngkgm0.png" alt=" " width="800" height="1360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Doesn't Just Write Code—it Fixes Itself
&lt;/h2&gt;

&lt;p&gt;One question naturally comes up: Can AI-generated code really be trusted?&lt;/p&gt;

&lt;p&gt;The reality is that execution failures are expected. The important part is what happens next.&lt;/p&gt;

&lt;p&gt;Every implementation generated by Starfish is executed immediately. If it fails, the runtime error is fed back to the language model, which analyzes the problem, regenerates the code, and retries automatically until the implementation executes successfully.&lt;/p&gt;

&lt;p&gt;The process goes beyond code generation. If the model determines to adjust the factor definition itself, it also updates the formula and its documentation so that the implementation and the explanation remain consistent.&lt;/p&gt;

&lt;p&gt;The result is a self-correcting workflow that spans the entire factor development process—from extracting formulas and generating code to debugging and evaluation. Researchers only need to answer one question at the end: Is this factor worth using?&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically Generate Better Variants
&lt;/h2&gt;

&lt;p&gt;One of the most practical capabilities in Starfish is automatic factor derivation.&lt;/p&gt;

&lt;p&gt;Researchers typically spend hours tweaking parameters, rerunning evaluations, and comparing results.&lt;/p&gt;

&lt;p&gt;Starfish turns that into an automated loop. Set the number of iterations, and the AI generates new factor variants, evaluates each one, and keeps improving them until every round is complete. It then compares all candidates using metrics such as the Information Coefficient (IC) and recommends the strongest performer with visual reports, so researchers can focus on evaluating ideas instead of comparing results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make Existing Knowledge Work for You
&lt;/h2&gt;

&lt;p&gt;For teams with an existing library of factor implementations, Starfish also supports Retrieval-Augmented Generation (RAG).&lt;/p&gt;

&lt;p&gt;When generating factor code or fixing execution errors, Starfish first retrieves similar implementations, past debugging records, and other relevant knowledge from the team's knowledge base, providing the language model with the context it needs to generate code that follows the team's best practices.&lt;/p&gt;

&lt;p&gt;As the knowledge base grows, so does the quality of the AI's output. Instead of remaining locked away in documents and repositories, a team's accumulated expertise becomes part of every new factor development task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise-Ready Permission Control
&lt;/h2&gt;

&lt;p&gt;For enterprise deployment, Starfish provides fine-grained AI governance.&lt;/p&gt;

&lt;p&gt;Administrators can configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Task quotas to limit how many report-analysis or factor-derivation jobs each user can create, helping control AI usage costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data access permissions at the table level, ensuring AI only accesses explicitly authorized datasets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Usage monitoring to track AI consumption across users and teams.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After deployment, users can also choose from multiple supported large language models within the AI Chat interface, ensuring the quality of AI-generated output meets the team's standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rethinking the Entire Factor Research Workflow
&lt;/h2&gt;

&lt;p&gt;Building a factor is no longer the bottleneck. Researchers can upload multiple reports, let AI implement and evaluate them in parallel, and focus on what humans do best: deciding which ideas are worth pursuing.&lt;/p&gt;

&lt;p&gt;From report parsing to optimization, DolphinDB Starfish automates the repetitive steps of factor development, making the entire workflow faster, more scalable, and easier to iterate.&lt;/p&gt;

&lt;p&gt;Download the &lt;a href="https://dolphindb.com/product#downloads-top" rel="noopener noreferrer"&gt;Starfish&lt;/a&gt; now.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Stay connected with DolphinDB by following us on &lt;a href="https://x.com/DolphinDB_Inc" rel="noopener noreferrer"&gt;X &lt;/a&gt;and &lt;a href="https://www.linkedin.com/company/14792031/admin/page-posts/published/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for the latest updates, technical articles, and product news. For more information, visit our &lt;a href="https://dolphindb.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>database</category>
      <category>datascience</category>
    </item>
    <item>
      <title>We Rebuilt a Securities Firm's FICC Data Stack — Queries Got 10x Faster</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Thu, 16 Jul 2026 07:21:39 +0000</pubDate>
      <link>https://dev.to/dolphindb/we-rebuilt-a-securities-firms-ficc-data-stack-queries-got-10x-faster-7ib</link>
      <guid>https://dev.to/dolphindb/we-rebuilt-a-securities-firms-ficc-data-stack-queries-got-10x-faster-7ib</guid>
      <description>&lt;p&gt;The investment research data team at a leading publicly listed securities firm had long struggled with fragmented data, inconsistent data standards, and insufficient real-time computing capabilities across its FICC operations. The journey from strategy development to production deployment often took several months.&lt;/p&gt;

&lt;p&gt;To overcome these challenges, the firm partnered with DolphinDB to build a unified FICC multi-asset research data platform based on an integrated stream-batch architecture, achieving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;More than a 10× improvement in historical market data query performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Millisecond-level latency for intraday market data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A reduction in the strategy development-to-production cycle from three months to just two weeks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An overall efficiency improvement of more than 85%&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform eliminated data silos and fragmented system architectures, providing a solid technical foundation for FICC quantitative trading and market-making.&lt;/p&gt;

&lt;h2&gt;
  
  
  FICC Quantitative Trading: Speed Creates Alpha
&lt;/h2&gt;

&lt;p&gt;FICC markets have long been dominated by over-the-counter (OTC) trading, where research and trading workflows depend heavily on manual data handling. Quantitative teams spend significant time integrating and cleaning data before pricing, backtesting, and risk analysis. The result is slower research, less accurate pricing, and delayed hedging.&lt;/p&gt;

&lt;p&gt;FICC markets are becoming increasingly data-driven. Growing market complexity and stronger links between exchange-traded and OTC markets are driving demand for unified data management, real-time analytics, and faster strategy execution.&lt;/p&gt;

&lt;p&gt;As a result, low-latency processing of high-frequency market data and unified data governance have become essential capabilities for modern FICC quantitative platforms, serving as the technological foundation for high-precision pricing, algorithmic hedging, and automated market making at leading financial institutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Data Fragmentation Was Slowing Down the Business
&lt;/h2&gt;

&lt;p&gt;As business requirements evolved, the firm's traditional FICC data architecture could no longer support the growing demands of quantitative research and trading. Four major challenges emerged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Inconsistent Data Across Sources&lt;/strong&gt;&lt;br&gt;
FICC research data came from multiple heterogeneous sources, including CTP market data feeds, third-party vendors, and locally maintained Excel spreadsheets. Each used different schemas, field definitions, and transmission formats. The lack of standardized frequently resulted in typo records, missing sequences, and other data quality issues, reducing backtesting accuracy and reinforcing the classic “Garbage In, Garbage Out” (GIGO) cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Fragmented Architecture and Isolated Data Components&lt;/strong&gt;&lt;br&gt;
Years of incremental IT expansion had left the same data duplicated across multiple systems. Researchers had to retrieve, reconcile, and integrate data across systems before analysis, while the siloed architecture increased infrastructure costs through duplicated hardware, redundant storage, and higher maintenance overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Batch-Oriented Processing with Limited Real-Time Computing&lt;/strong&gt;&lt;br&gt;
The legacy architecture relied on end-of-day batch processing, with analytical results available only on T+1. High-frequency strategies simply weren't possible without live market data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. High Technical Barriers for Researchers&lt;/strong&gt;&lt;br&gt;
The fragmented architecture placed a heavy technical burden on researchers. Instead of focusing on strategy development, they spent much of their time writing data extraction scripts and glue code to integrate data from multiple sources. As a result, development cycles became longer and production deployment was significantly delayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2hxq19cuxn3dhmuurbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2hxq19cuxn3dhmuurbk.png" alt=" " width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: An FICC Research Data Platform Powered by DolphinDB
&lt;/h2&gt;

&lt;p&gt;The firm partnered with DolphinDB to build a unified, multi-asset FICC research data platform on a distributed time-series database, combining streaming and batch processing in a single framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdh3m7fz9thm1oearpvqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdh3m7fz9thm1oearpvqw.png" alt=" " width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxqeuhxd8p8wxbxl3yd0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxqeuhxd8p8wxbxl3yd0.png" alt=" " width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Consolidating Historical Data
&lt;/h2&gt;

&lt;p&gt;The team migrated nearly five years of cash bond market data from Hive into DolphinDB, along with historical futures and options data. With DolphinDB's multi-model storage, the platform optimizes storage for different data types while balancing storage efficiency and query performance.&lt;/p&gt;

&lt;p&gt;Multi-source data is cleansed and normalized into a unified trade and quote data model. A closed-loop validation process continuously checks for missing or abnormal records, enabling centralized management of historical data across all asset classes while significantly reducing system complexity and maintenance costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Real-Time Market Data Hub
&lt;/h2&gt;

&lt;p&gt;The system uses DolphinDB's Kafka plugin to build a low-latency data ingestion layer, enabling real-time access to market data from major exchanges and brokers through a unified data pipeline. Combined with DolphinDB's streaming engines, the system enables real-time calculation of derived indicators. As new data arrives, incremental computations are triggered automatically and results are generated with minimal latency.&lt;/p&gt;

&lt;p&gt;For FICC-specific needs, DolphinDB also provides a real-time one-minute candlestick generation module for futures and options. The module addresses common industry challenges such as inconsistent exchange timestamps and disconnected workflows between historical and real-time data processing. Manual and error-prone alignment processes are now automated within the system.The module now serves as a core infrastructure component, feeding low-latency, high-availability minute-level data to both production trading systems and quantitative strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;The unified platform now connects research, trading, and risk management on a single data foundation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd743vi2p9quzi9oug16c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd743vi2p9quzi9oug16c.png" alt=" " width="600" height="196"&gt;&lt;/a&gt;&lt;br&gt;
The project transformed fragmented, post-market data workflows into a unified real-time data foundation. By connecting research, trading, and risk management teams on a single data platform, it enables faster decision-making, accelerates quantitative research, and strengthens the firm's competitiveness in FICC market making.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next: AI-Powered Research and High-Fidelity Backtesting
&lt;/h2&gt;

&lt;p&gt;For both the firm and DolphinDB, this project marks a major milestone in modernizing FICC data infrastructure—but it is only the beginning.&lt;/p&gt;

&lt;p&gt;Looking ahead, we plan to further evolve the platform in two strategic directions.&lt;/p&gt;

&lt;p&gt;The first is deeper AI integration. DolphinDB will build a high-performance feature store designed for AI workloads, enabling factor selection, model training, and online inference directly within the data platform to accelerate the deployment of AI-driven investment research.&lt;/p&gt;

&lt;p&gt;The second is the development of a high-fidelity backtesting platform capable of dynamically simulating intraday margin requirements and real-time risk exposure for futures and options. By more accurately replicating live trading conditions, the platform will provide a more realistic environment for strategy validation and improve the reliability of quantitative research before production deployment.&lt;/p&gt;

&lt;p&gt;As financial markets continue to evolve, DolphinDB will keep innovating at speed—empowering intelligent finance with higher performance, greater reliability, and technology built for the future.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>database</category>
      <category>capitalmarkets</category>
    </item>
    <item>
      <title>What’s New in DolphinDB：Introducing DolphinX, an Enterprise-Grade Platform for Agent Development and Governance</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 15 Jul 2026 08:33:01 +0000</pubDate>
      <link>https://dev.to/dolphindb/whats-new-in-dolphindbintroducing-dolphinx-an-enterprise-grade-platform-for-agent-development-4emo</link>
      <guid>https://dev.to/dolphindb/whats-new-in-dolphindbintroducing-dolphinx-an-enterprise-grade-platform-for-agent-development-4emo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fccdbexqnj6vyu4xofmoy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fccdbexqnj6vyu4xofmoy.webp" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;br&gt;
_This release is a Compatibility Level 2 update. For details, please refer to the V3.00.6 Release Notes.&lt;/p&gt;

&lt;p&gt;Features marked [V3.0] are available exclusively in DolphinDB 3.00.6._&lt;/p&gt;

&lt;p&gt;As enterprises race to turn AI from pilot projects into production systems, the real bottleneck is often integration — connecting data, compute, and workflows into something secure and reusable. With DolphinDB V3.00.6 &amp;amp; V2.00.19, we’re tackling this head-on.&lt;/p&gt;

&lt;p&gt;Central to this release is DolphinX, an enterprise-grade platform that deeply integrates DolphinDB’s native capabilities with an Agent platform — turning data, computing, and business logic into secure, reusable, and governable AI agent capabilities with minimal effort.&lt;/p&gt;

&lt;p&gt;This release also strengthens core capabilities across FeatureDB, FICC, JIT compilation, dynamic script optimization, high-availability stream computing, and system operations — spanning the full workflow from low-latency feature storage and complex derivatives pricing to high-performance script execution and enterprise-grade governance.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. DolphinX: An Enterprise-Grade AI Development and Governance Platform&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As demand grows for natural language data querying, intelligent analytics, and automated workflows, the traditional “database + external AI framework” approach is increasingly challenged by concerns around security, compatibility, operational complexity, and enterprise governance. DolphinX, an enterprise-grade platform for AI agent development and governance, enables organizations to transform their data, computing, and business capabilities into secure, governable, and reusable AI agent services with a single integration, empowering faster business innovation and accelerating AI deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Natural Language-Driven Data Workflows&lt;/strong&gt;&lt;br&gt;
With the built-in Coding Agent, users can interact with DolphinDB’s core capabilities using natural language, eliminating the need to learn Dlang. From script generation and debugging to SQL query analysis, data import/export, stream processing, and real-time engine development, Coding Agent makes advanced data capabilities accessible to a much broader range of business users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out-of-the-Box AI Toolkit with Flexible Extensibility&lt;/strong&gt;&lt;br&gt;
DolphinX works out of the box with zero configuration required. Just log in through the web interface to get started. It also includes a web-based management console for configuring prompts, models, and MCP servers, and attaching Skills to build dedicated AI agents.&lt;/p&gt;

&lt;p&gt;DolphinX comes with a rich set of built-in Skills covering coding standards, data import, financial services, stream processing, operations and maintenance, machine learning, data visualization, testing, and more (see the table below).&lt;/p&gt;

&lt;p&gt;For organizations requiring deeper customization, DolphinX exposes APIs that enable seamless integration of its AI capabilities into existing enterprise systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0l5yz2j8n5mtyr6jhtsr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0l5yz2j8n5mtyr6jhtsr.webp" alt=" " width="640" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eliminate External Dependencies and Build an Enterprise-Grade Intelligent Closed Loop&lt;/strong&gt;&lt;br&gt;
AI agent capabilities are built directly into the DolphinDB Server, eliminating the need for third-party orchestration frameworks. This enables a complete, end-to-end workflow — from understanding and analysis to execution — entirely within a single system. For scripts generated by AI models but not yet reviewed by users, DolphinX parses and validates them before execution, blocking high-risk operations such as deletion and cleanup. When invoking DolphinDB capabilities, AI agents inherit the current user’s permissions, eliminating the need for a separate access control system. This significantly reduces the security risks, compatibility issues, and operational complexity associated with external dependencies, making enterprise AI applications more secure, reliable, and efficient.&lt;/p&gt;

&lt;p&gt;To further enhance AI’s understanding of database environments, DolphinDB has also expanded its semantic metadata framework for database objects. Building on existing support for descriptions of tables, table columns, and user-defined functions, this release introduces description support for both the Catalog and Schema levels. In addition, the maximum length of table column descriptions has been increased from 256 bytes to 1,024 bytes. By providing richer representations of database hierarchy and business semantics, AI agents can more accurately understand data assets, generate scripts, and execute analytical tasks, resulting in a more reliable enterprise-grade intelligent closed loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enterprise-Grade Agent Development and Governance Foundation&lt;/strong&gt;&lt;br&gt;
DolphinX provides a common capability layer for building and governing Agents, including session management, Memory, Skills, context assembly, LLM provider management, and permission auditing. This helps enterprises avoid rebuilding these capabilities from scratch for every Agent, while enabling unified governance and operations.It supports both short-term and long-term memory, and provides a built-in RAG knowledge system based on DolphinDB’s TextDB and VectorDB. It also supports unified integration with multiple LLM providers, including fallback switching, token budget management, and local or private cloud deployment, balancing flexible extensibility, data security, and autonomous control.&lt;/p&gt;

&lt;p&gt;Designed for scenarios such as natural language interaction, intelligent analytics, and automated processing, DolphinX deeply integrates AI capabilities with DolphinDB to provide an integrated product ecosystem that combines out-of-the-box usability with enterprise-level extensibility. It enables organizations to build intelligent data applications with lower barriers to entry and reduced system integration costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. FeatureDB: A Low-Latency Feature Storage Engine for AI and Machine Learning Applications&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In AI and machine learning training and online inference scenarios, feature data access follows different patterns than general analytical queries with high-frequency reads by column, window, and timestamp. Traditional online feature stores, which are primarily designed for row-based access, focus more on full-row read/write operations and key-value retrieval. As a result, they are not fully optimized for these access patterns and can become performance bottlenecks in scenarios involving wide feature tables, frequent reads, and low-latency services.&lt;/p&gt;

&lt;p&gt;FeatureDB is purpose-built to address exactly this gap. Running within the DolphinDB environment, it offers a lightweight, high-performance foundation for feature serving pipelines in AI and machine learning applications.&lt;/p&gt;

&lt;p&gt;Note: The FeatureDB introduced in this release currently supports in-memory mode only. Persistent storage will be available in future releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microsecond-Level Random Reads to Alleviate Feature Access Bottlenecks&lt;/strong&gt;&lt;br&gt;
FeatureDB delivers microsecond-level random reads, with response times typically ranging from a few micro-seconds to tens of microseconds. This significantly reduces the impact of feature access latency on both training throughput and inference speed, especially in large-scale, high-frequency access scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Native Support for Machine Learning Data Types and Flexible Wide Table Design&lt;/strong&gt;&lt;br&gt;
FeatureDB natively supports commonly used floating-point data types in machine learning, including FLOAT8, FLOAT16, FLOAT32, and FLOAT64, covering different tradeoffs between precision and storage efficiency. It also supports wide tables containing hundreds of feature columns and provides simple syntax for quickly defining continuous feature columns, reducing the management complexity of large-scale feature engineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seamless Python Ecosystem Integration, Simplifying Workflows from Import to Retrieval&lt;/strong&gt;&lt;br&gt;
On the upstream side, FeatureDB integrates with feature computation tools such as Spark and PyArrow through the standard Parquet format, enabling one-click batch import. On the downstream side, its Python SDK directly outputs data that can be consumed by mainstream ecosystem tools such as NumPy and PyTorch, covering the complete pipeline from storage to model training and inference. By leveraging industry-standard tools and formats throughout the workflow, FeatureDB effectively reduces system integration and maintenance costs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk6y6aqrm9wt6a8328m1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk6y6aqrm9wt6a8328m1.webp" alt=" " width="640" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The introduction of FeatureDB marks an extension of DolphinDB’s capabilities in AI and machine learning scenarios, providing users with a new infrastructure option for building high-performance feature data services.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. Building a Complete Pricing Loop: Comprehensive FICC Capability Enhancements&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The new release continues to enhance DolphinDB’s FICC pricing capability framework by introducing a wide range of pricing functions across fixed income, interest rates, credit, foreign exchange, and equity OTC derivatives. New support covers agreements, government bond futures, FRAs, Caps/Floors, Swaptions, CDS, NDFs, digital options, and range accrual options.&lt;/p&gt;

&lt;p&gt;In addition, the release introduces new foundational tools such as Copula functions, credit curves, volatility surfaces/cubes, date generation, and schedule construction. These enhancements further strengthen DolphinDB’s cross-asset pricing and risk analysis capabilities, enabling a more complete and integrated pricing workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvt1z2dl5zcgjnkggcc4k.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvt1z2dl5zcgjnkggcc4k.webp" alt=" " width="640" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fog7i8o5341lbogsbcp6x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fog7i8o5341lbogsbcp6x.webp" alt=" " width="640" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Enhanced JIT Capabilities with Broader Language Support and Greater Stability&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As enterprises increasingly demand real-time execution of complex business logic, high-performance script acceleration, and event-driven processing, Just-In-Time (JIT) compilation has become a critical foundation for executing core computational workloads. In production environments, users expect more than successful compilation — they also require comprehensive support for complex language features, rich object and container operations, and consistent performance under long-running workloads, complex event streams, and high-concurrency scenarios.&lt;/p&gt;

&lt;p&gt;DolphinDB has comprehensively upgraded its JIT engine in the new release, expanding language support while significantly improving runtime stability. These enhancements allow users to migrate more business logic into the high-performance execution path with minimal code refactoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broader Language Support for Complex Scripts&lt;/strong&gt;&lt;br&gt;
The new release adds JIT support for a wider range of language features, including partial application, anonymous functions, lambda expressions, closures, default parameters, and keyword arguments. As a result, many scripting patterns that previously relied on the interpreter can now be compiled directly without rewriting code specifically for JIT compatibility. This significantly improves scripting flexibility for scenarios involving reusable abstractions, complex computational workflows, and sophisticated data structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enhanced Type System and Container Support&lt;/strong&gt;&lt;br&gt;
This release introduces explicit type annotations as a major enhancement to the JIT type system. Developers can declare member types directly within class definitions using the following syntax:&lt;br&gt;
&lt;code&gt;class MyClass {&lt;br&gt;
    field1 :: STRING&lt;br&gt;
    field2 :: INT&lt;br&gt;
    field3 :: DOUBLE VECTOR&lt;br&gt;
    field4 :: DICTIONARY[STRING, STRING]&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
Type annotations support scalar types, user-defined classes, vectors, ANY, and recursively nested dictionaries. The JIT compiler generates type-specialized machine code directly from these declarations, eliminating runtime type inference and significantly improving performance when accessing complex objects and nested data structures.&lt;/p&gt;

&lt;p&gt;Container operations have also been substantially expanded. The JIT runtime now supports reading, assignment, deletion, clearing, indexing, and concurrent access across these data types. In addition, Matrix support and non-scalar indexing have been introduced, while numerous container-related crashes, memory issues, and incorrect computation results have been resolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved Compatibility for CEP&lt;/strong&gt;&lt;br&gt;
JIT support for classes has been further enhanced to better serve Complex Event Processing (CEP) applications. The new release supports event classes, monitor classes, and helper classes used in CEP, while improving advanced class features such as dictionary/vector member access, nested classes, inter-method calls, and callback function passing. These enhancements improve both execution efficiency and runtime stability in CEP-related workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Greater Stability and Production Readiness&lt;/strong&gt;&lt;br&gt;
Beyond language enhancements, this release significantly improves the engineering robustness of the JIT engine. Numerous stability issues involving complex data processing, nested assignments, loop execution, classes, and tuples have been resolved, along with multiple memory leaks, persistent memory growth, and abnormal resource utilization issues. Together, these improvements reduce runtime failures and make JIT considerably more reliable for complex business logic running under production-scale data workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Upgraded Dynamic Script Optimization for Faster Nested Functions and Loop Execution&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In quantitative analytics, real-time risk management, and low-latency trading, users frequently write complex scripts containing loops, conditional branches, and function calls. Because these workloads are often difficult to fully vectorize, interpreter overhead can become a significant performance bottleneck.&lt;br&gt;
DolphinDB provides Dynamic Script Optimization, which automatically optimizes interpreted scripts at runtime without requiring any code modifications. Once enabled, it significantly accelerates scripts containing loops, conditional branches, and function calls. In function-intensive workloads, performance improvements of several times have been observed.&lt;/p&gt;

&lt;p&gt;In the new release, this capability has been further refined by changing the runtime optimization scope from process-level to session-level. Configuration parameters continue to define the default behavior, while runtime APIs now affect only the current session without impacting other sessions within the same process. This improvement provides much greater flexibility in multi-user and multi-task environments. For example, users can enable optimization in one session for performance testing while keeping another session unchanged for functional validation, making the feature more practical for production deployment.&lt;/p&gt;

&lt;p&gt;Dynamic Script Optimization is particularly effective for the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deeply nested function calls, such as strategy logic decomposed into multiple reusable functions with frequent invocation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complex iterative computations, including tick-by-tick or bar-by-bar processing, element-wise window calculations, and recursive state updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High-frequency conditional branching, such as signal generation, chained risk control rules, and sequential trading condition filtering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Business logic that cannot be easily vectorized, including state-dependent, path-dependent, or multi-stage decision workflows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these workloads, Dynamic Script Optimization significantly reduces interpreter overhead and minimizes the performance cost of loops and function calls. Benchmark results show performance improvements of 63.9%–88.8% for function-intensive scripts, up to 8.93× acceleration for deeply nested function calls, and more than 30% performance gains for representative financial factor calculations such as VWAP.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Enhanced High Availability for Streaming Data&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The new release further strengthens the high availability (HA) capabilities for streaming data, covering both HA stream tables and HA MVCC tables to improve production reliability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For stream tables, data subscription and delivery are now more stable, subscription management is more reliable, and services recover faster after node status changes. These enhancements also extend to the ORCA streaming platform, improving stream graph continuity and overall data reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For MVCC tables, HaMvccTable further enhances the stability of data reading, writing, and synchronization, improves resource management, and ensures data consistency across multiple nodes. With continued optimization, HaMvccTable is now more production-ready and provides stronger protection for mission-critical data.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;7. Improvements to Core Capabilities&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This release also delivers enhancements across the database kernel and data analytics engine.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;br&gt;
Core database capabilities have been improved across distributed scheduling, storage integration, data lifecycle management, and write control. The new release delivers better task scheduling balance in multi-replica clusters, simplified Amazon S3 integration through the AWS SDK default credential chain, enhanced monthly partition retention and cold storage migration, and more precise write controls to prevent unintended NULL values during complex update operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analytical Functions&lt;/strong&gt;&lt;br&gt;
The function library has been expanded for quantitative research and real-time analytics. New capabilities include time-based weighted calculations, linear filtering, variable-length sliding windows, and rolling drawdown calculations. Custom aggregation and regression options have also been extended, while text import and JSON serialization have been improved for better interoperability with external systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core Language&lt;/strong&gt;&lt;br&gt;
The core language continues to evolve with improvements to type definitions, class support, control flow, metaprogramming, SQL semantics, and function metadata. New SQL features include union join (uj), ROLLUP, group-level pagination with context by, enhanced type conversion, and improved identifier compatibility. Function annotations have also been enriched with @desc, parameter types, and return type declarations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stream Processing&lt;/strong&gt;&lt;br&gt;
The streaming framework has been further strengthened with more flexible trigger mechanisms in the time-series engine, refined snapshot generation across multiple time ranges, access control identifiers for stream table queries, and improved management of dynamic metrics in ORCA stream graphs, delivering greater stability for complex streaming workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plugins &amp;amp; API&lt;/strong&gt;&lt;br&gt;
Plugin installation and Python data upload have been streamlined, improving plugin deployment in restricted network environments. Python integration with the Apache Arrow ecosystem has also been enhanced, enabling smoother interoperability with tools such as Polars.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;8. Enhanced System Operations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The new release strengthens observability, resource management, and operational efficiency for production environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Observability&lt;/strong&gt;&lt;br&gt;
System monitoring has been improved with richer metadata and status visibility. Administrators can now query historical background tasks based on permissions, while stream tables, functions, shared variables, and in-memory tables expose additional status information. Automatic DDL generation through getDatabaseDDL and getDBTableDDL also simplifies database migration and schema replication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stronger Security and Resource Control&lt;/strong&gt;&lt;br&gt;
AES-256-CBC encryption has been added for secure string encryption and decryption. License management now supports hardware dongle verification and real-time license status monitoring. User-level task concurrency limits help prevent resource contention, while Catalog and Schema objects now support descriptions for easier management in multi-tenant environments. (Some features are available only in V3.00 and later.)&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9. Performance and User Experience Improvements&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This release delivers comprehensive improvements in data processing performance, system stability, and usability. Enhancements to query parsing, error reporting, storage scheduling, cache warm-up, transaction management, and Leader stability improve performance, observability, and reliability while reducing resource consumption in storage-compute separation, hot/cold tiering, and large-scale data access scenarios.&lt;/p&gt;

&lt;p&gt;At the storage layer, TSDB introduces the new volumeType configuration for storage volumes. With SSD optimization enabled, level file access paths have been optimized to reduce storage latency and improve throughput for high-frequency read workloads. Benchmark results show an average throughput improvement of approximately 13% for multi-level group aggregation, with gains of up to 35% in specific scenarios. CPU-intensive read workloads achieve throughput improvements of up to 109%.&lt;/p&gt;

&lt;p&gt;Usability has also been enhanced with improvements to object display, timestamp handling, dynamic metric management, field parsing, license queries, matrix indexing, and other commonly used data processing capabilities, providing greater flexibility, ease of use, and compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;10. Roadmap&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Storage &amp;amp; Data Ingestion&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Introduce persistent mode for FeatureDB to improve the reliability and reusability of feature data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enhance the read performance of external tables (e.g., Amazon S3) to improve data ingestion efficiency in lakehouse architectures.&lt;br&gt;
&lt;strong&gt;Financial Derivatives Pricing&lt;/strong&gt;&lt;br&gt;
Expand support for pricing complex financial derivatives across three major asset classes:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fixed Income: Embedded-option bonds&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Credit: CLN, CRMW, and CRMA&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FX: Asian, Barrier, Touch, and Quanto options&lt;br&gt;
Further enhance core pricing models — including LV, SV, SLV, PDE, and Monte Carlo methods, and optimize pricing engine compatibility to provide a unified infrastructure for multi-asset derivatives pricing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Agent Ecosystem&lt;/strong&gt;&lt;br&gt;
Launch a suite of domain-specific AI agents for different use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database Operations Agent&lt;/li&gt;
&lt;li&gt;Plugin Development Agent&lt;/li&gt;
&lt;li&gt;Machine Learning &amp;amp; Data Analytics Agent&lt;/li&gt;
&lt;li&gt;Quantitative Research Agent&lt;/li&gt;
&lt;li&gt;Dlang Programming Assistant with long-context conversation support.&lt;/li&gt;
&lt;li&gt;Release an AI agent evaluation framework X-Lab for measuring and validating agent capabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Query Engine Optimization&lt;/strong&gt;&lt;br&gt;
Continue enhancing the SQL engine by improving standards compliance while significantly boosting execution performance for complex multi-table join workloads.&lt;/p&gt;

&lt;p&gt;Download &lt;a href="https://dolphindb.com/product#downloads-top" rel="noopener noreferrer"&gt;DolphinDB V3.00.6 &amp;amp; V2.00.19&lt;/a&gt; now.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Stay connected with DolphinDB by following us on X &lt;a href="https://dolphindb.com/product#downloads-top" rel="noopener noreferrer"&gt;(@DolphinDB_Inc)&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/dolphindb/posts/?feedView=all&amp;amp;viewAsMember=true" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for the latest updates, technical articles, and product news. For more information, visit our &lt;a href="https://dolphindb.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How Crypto Pin Bars Form and How to Detect Them: Lessons from a $19B Liquidation</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 08 Jul 2026 09:02:42 +0000</pubDate>
      <link>https://dev.to/dolphindb/how-crypto-pin-bars-form-and-how-to-detect-them-lessons-from-a-19b-liquidation-7ec</link>
      <guid>https://dev.to/dolphindb/how-crypto-pin-bars-form-and-how-to-detect-them-lessons-from-a-19b-liquidation-7ec</guid>
      <description>&lt;p&gt;One Candlestick, $19 Billion Wiped Out Overnight&lt;/p&gt;

&lt;p&gt;On October 10, 2025, the U.S. abruptly announced a 100% tariff on all Chinese imports and introduced new export restrictions on critical software. The news shattered an otherwise quiet Friday evening. Bitcoin plunged more than 14% from near its all-time high, while several Altcoins fell by as much as 70% before rebounding [1]. Within just a few hours between October 10 and 11, more than $19 billion in leveraged positions were liquidated, making it one of the largest single-day liquidation events in crypto market history.&lt;/p&gt;

&lt;p&gt;Left behind on the charts: a series of long lower wicks that traders know all too well as pin bars.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9oore6xjfd9rk2x8el3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9oore6xjfd9rk2x8el3d.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Pin Bar?
&lt;/h2&gt;

&lt;p&gt;A pin bar is an extreme candlestick pattern characterized by &lt;strong&gt;a very long wick and a relatively small body.&lt;/strong&gt; The price briefly breaks through a key support or resistance level before rapidly reversing, leaving behind a distinctive "needle" on the chart. It serves as the market's fingerprint of a brief loss of equilibrium.&lt;/p&gt;

&lt;p&gt;The concept originates from classical candlestick analysis and can be traced back to Steve Nison's pioneering work on candlestick techniques [2]. Traditional definitions, however, are largely qualitative. To provide a more objective definition, we quantify a pin bar using the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The wick is significantly longer than the candlestick body.&lt;/li&gt;
&lt;li&gt;The wick accounts for a large proportion of the candlestick's total price range.&lt;/li&gt;
&lt;li&gt;The overall price range exceeds a minimum absolute or relative threshold to filter out normal bid-ask fluctuations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Do Pin Bars Occur?
&lt;/h2&gt;

&lt;p&gt;Market microstructure offers several classic explanations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Informed Trading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Large institutions or traders with superior information execute sizable orders during periods of limited liquidity, causing significant price impact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Insufficient Liquidity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smaller cryptocurrencies often have shallow order books, allowing a single large order to move the market by several percentage points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Market Manipulation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some traders intentionally trigger clusters of stop-loss or liquidation orders by forcing prices through key levels before buying back at lower prices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leverage and Cascading Liquidations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the most common and most destructive mechanism. Highly leveraged long positions tend to concentrate their liquidation levels just below important support prices. Once the market reaches those levels, systems automatically execute market sell orders. The additional selling pressure pushes prices even lower, triggering more liquidations and creating a vicious cycle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pin Bar → Liquidations → Deeper Pin Bar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The October 10 crash provides a textbook example of this mechanism. Bitcoin was trading near its historical highs, with the market heavily positioned on the long side using excessive leverage. Meanwhile, liquidity had thinned during Friday evening as institutional market makers reduced activity. Under such conditions, a single geopolitical shock was enough to ignite the cascade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconstructing October 10: What the Data Saw
&lt;/h2&gt;

&lt;p&gt;This analysis is based on real market data collected from Binance and OKX throughout October 2025. Three types of datasets are used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minute-level candlestick (K-line) data for identifying pin bar patterns.&lt;/li&gt;
&lt;li&gt;Aggregated trade data for calculating VPIN (Volume-Synchronized Probability of Informed Trading).&lt;/li&gt;
&lt;li&gt;Order book depth snapshots for measuring order book imbalance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All timestamps are converted to UTC and stored in the DolphinDB distributed database for analysis.&lt;/p&gt;

&lt;p&gt;We begin by reconstructing the event using the three most direct indicators—pin bar patterns, liquidation records, and trading volume—to understand exactly what unfolded on that day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying Pin Bars
&lt;/h3&gt;

&lt;p&gt;We scanned the 5-minute BTCUSDT candlestick data from Binance to identify pin bars using the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wick length &amp;gt; 5 × body length&lt;/li&gt;
&lt;li&gt;Wick length &amp;gt; 50% of the total candlestick range&lt;/li&gt;
&lt;li&gt;Candlestick range &amp;gt; 0.5% of the average price&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core implementation is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;close&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;close&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;avgPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Detection&lt;/span&gt; &lt;span class="n"&gt;criteria&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adjust&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;needed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;pinbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;pinbar_upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; 
&lt;span class="n"&gt;pinbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pinbar_upper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pinbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pinbar&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;avgPrice&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the 5-minute BTCUSDT candlestick data from Binance as an example, the detection results are shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh825wq00nqscyobvbjiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh825wq00nqscyobvbjiz.png" alt=" " width="798" height="119"&gt;&lt;/a&gt;&lt;br&gt;
Example of detected pin bars&lt;/p&gt;

&lt;p&gt;Candlesticks identified as pin bars are typically accompanied by unusually high trading volume compared with the day's average. As shown in the figure, at 2025-10-10 21:20:00 UTC, the price briefly fell to 101,516 USDT, dropping by more than 7,000 USDT from the opening price of 108,888 USDT. It then rebounded rapidly and closed at 107,747 USDT. Trading volume during this interval was approximately an order of magnitude higher than during other periods of the day.&lt;/p&gt;

&lt;p&gt;So, who was selling during this period, and how much was actually sold?&lt;/p&gt;
&lt;h3&gt;
  
  
  Liquidation Data: Reconstructing the Cascade of Forced Liquidations
&lt;/h3&gt;

&lt;p&gt;To answer this question, we aggregated minute-level liquidation data from Binance and OKX for the entire month of October. The data were then summarized by liquidation direction (long vs. short) to reveal the overall liquidation dynamics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;liqDataForSym&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loadTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"dfs://CryptocurrencyDay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"liquidation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;liqDataForSym&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temporalAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"H"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;Convert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;UTC&lt;/span&gt;
&lt;span class="n"&gt;liqDataForSymDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForSym&lt;/span&gt; 
    &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="n"&gt;res1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForSymDay&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="n"&gt;liqDataForOneDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForSym&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;
&lt;span class="n"&gt;res2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForOneDay&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;sum_quantity&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkhyho0tjce05id3pciba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkhyho0tjce05id3pciba.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results are striking. The total liquidation volume on October 10 far exceeded that of any other day in the month. In terms of liquidation direction, long liquidations (forced sell orders) overwhelmingly outnumbered short liquidations, providing strong evidence of a cascading long squeeze. Intraday, liquidation activity peaked between 21:10 and 22:00, closely matching the period in which pin bars were most frequently detected.&lt;/p&gt;

&lt;p&gt;Using XRPUSDT as an example, the figure below illustrates the relationship between minute-level liquidation volume and price movements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2mzdccspa2vn1j4pqcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2mzdccspa2vn1j4pqcl.png" alt=" " width="799" height="152"&gt;&lt;/a&gt;&lt;br&gt;
Liquidation Data vs. Price Movement&lt;/p&gt;
&lt;h3&gt;
  
  
  Trading Volume Reveals the Sentiment Turning Point
&lt;/h3&gt;

&lt;p&gt;We further analyzed the intraday trading volume of XRPUSDT and compared it with the corresponding price movement. It is important to note that the aggregated trade data used here includes only regular market orders and &lt;strong&gt;excludes liquidation-related forced orders&lt;/strong&gt;. As a result, it provides a clearer view of genuine market sentiment rather than passive trading triggered by forced liquidations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzqm2k5yvopbee5dflvwm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzqm2k5yvopbee5dflvwm.png" alt=" " width="799" height="147"&gt;&lt;/a&gt;&lt;br&gt;
XRPUSDT tradeQty vs Volatility&lt;/p&gt;

&lt;p&gt;The results show that trading volume surged sharply after &lt;strong&gt;20:50&lt;/strong&gt;, indicating that the shift in market sentiment occurred before the largest price decline. A wave of aggressive sell orders entered the market first, disrupting the supply-demand balance. These active sell orders were then amplified by forced liquidation orders, together driving larger price swings and ultimately producing the sharp wick observed around 21:20.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Signals That Were Already There
&lt;/h2&gt;

&lt;p&gt;The previous analysis reconstructed the full sequence of events during the price wick. For traders, however, stop-loss orders being triggered, positions being liquidated, or even accounts being wiped out often happen within minutes—or even seconds. Post-event analysis offers little practical value. The more important question is whether these events can be anticipated before the wick forms. &lt;/p&gt;

&lt;p&gt;To answer this, we evaluated two market microstructure indicators—VPIN and Order Book Imbalance—using historical data to determine whether the market had already begun signaling elevated risk before the extreme price movement occurred.&lt;/p&gt;
&lt;h3&gt;
  
  
  VPIN: Tracking the Evolution of Order Flow Toxicity
&lt;/h3&gt;

&lt;p&gt;VPIN (Volume-Synchronized Probability of Informed Trading) was introduced by Easley et al. in 2011 [3]. Its key idea is **to measure market activity based on trading volume rather than clock time. **Specifically, the order flow is divided into equal-volume buckets, and the imbalance between aggressive buy orders and aggressive sell orders is calculated for each bucket. A larger imbalance indicates that one side is dominating the market, implying higher order flow toxicity and a greater likelihood of extreme price movements. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmiihsbhh1hykezess36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmiihsbhh1hykezess36.png" alt=" " width="216" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We calculated the VPIN indicator using aggregated trade data to track the dynamics of order flow toxicity across multiple cryptocurrencies throughout October. &lt;/p&gt;

&lt;p&gt;Parameter settings: The fixed bucket size (V) was determined by dividing the average daily trading volume over the previous 20 trading days by 50 buckets per day. A rolling window of 10 buckets was used to calculate VPIN. The indicator was computed in parallel for BTC, ETH, XRP, and ADA.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;symList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;`BTCUSDT,`&lt;/span&gt;&lt;span class="n"&gt;ETHUSDT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;`XRPUSDT,`&lt;/span&gt;&lt;span class="n"&gt;ADAUSDT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="n"&gt;targetSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Binance-Futures"&lt;/span&gt;
&lt;span class="n"&gt;dataType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"trade"&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;"trade"&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;"minKLine"&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;first&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;divide&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="n"&gt;buckets&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;calculate&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;window&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Parallel&lt;/span&gt; &lt;span class="n"&gt;computation&lt;/span&gt; &lt;span class="n"&gt;across&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="n"&gt;trading&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;   &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;UTC&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;peach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calcVPINForAllSym&lt;/span&gt;&lt;span class="p"&gt;{,&lt;/span&gt;&lt;span class="n"&gt;symList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;targetSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unionAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;Calculate&lt;/span&gt; &lt;span class="n"&gt;volatility&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;correlation&lt;/span&gt; &lt;span class="n"&gt;simultaneously&lt;/span&gt;
&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calcVPINVolCor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The figure below compares VPIN with price volatility.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4l4xemdxc4fisy0c6s1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4l4xemdxc4fisy0c6s1m.png" alt=" " width="798" height="160"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffabi8q5jv6msxoj9xrzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffabi8q5jv6msxoj9xrzh.png" alt=" " width="798" height="149"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2x91nyprq83ggmhjb0hg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2x91nyprq83ggmhjb0hg.png" alt=" " width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the result we can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ADA and ETH: VPIN and price volatility were highly synchronized around October 10. In many cases, sharp increases in VPIN either preceded or coincided with significant price swings, suggesting that VPIN exhibits meaningful early warning characteristics.&lt;/li&gt;
&lt;li&gt;BTC: The relationship between VPIN and price volatility was noticeably weaker, which is consistent with Bitcoin's larger market size and stronger liquidity absorption capacity.&lt;/li&gt;
&lt;li&gt;Cross-asset pattern: The lower the liquidity of an asset, the more sensitive VPIN is as an early warning indicator for extreme market events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The imbalance in order flow is already evident—but what about order book liquidity? Was market depth deteriorating at the same time?&lt;/p&gt;
&lt;h3&gt;
  
  
  Order Book Imbalance: Detecting the Early Signs of Liquidity Exhaustion
&lt;/h3&gt;

&lt;p&gt;Another indicator that directly reflects market conditions is the &lt;strong&gt;Order Book Depth Ratio&lt;/strong&gt;. It is calculated as the ratio of the cumulative bid size from Level 1 to Level N to the cumulative ask size from Level 1 to Level N.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fydksn7hz9ewinjwlk3l9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fydksn7hz9ewinjwlk3l9.png" alt=" " width="181" height="42"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When this ratio drops sharply, it indicates that buy-side liquidity is rapidly disappearing. Under such conditions, even modest selling pressure can trigger a cascade of price declines [4].&lt;/p&gt;

&lt;p&gt;To validate this hypothesis, we analyzed order book depth snapshots from &lt;strong&gt;October 10, 2025&lt;/strong&gt;. To avoid signal dilution caused by averaging multiple snapshots, we retained only the last snapshot of each minute. We then calculated a normalized order book imbalance metric and compared it with subsequent price volatility.&lt;/p&gt;

&lt;p&gt;The analysis consisted of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve order book depth data and calculate the imbalance metric.&lt;/li&gt;
&lt;li&gt;Retain the last order book snapshot of each minute to preserve signal strength.&lt;/li&gt;
&lt;li&gt;Calculate future returns and price volatility over different forward-looking windows.&lt;/li&gt;
&lt;li&gt;Identify depth imbalance spikes using the 90th percentile of depthRatioQtyDiff for each asset as the threshold.&lt;/li&gt;
&lt;li&gt;Compute ampRatio = future volatility following a Spike / future volatility without a Spike to evaluate the predictive power of the indicator.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;CalcDepthRatioModule&lt;/span&gt;
&lt;span class="n"&gt;symList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;`BTCUSDT,`&lt;/span&gt;&lt;span class="n"&gt;XRPUSDT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;`ETHUSDT,`&lt;/span&gt;&lt;span class="n"&gt;ADAUSDT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;targetSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Binance-Futures"&lt;/span&gt;
&lt;span class="n"&gt;targetDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="k"&gt;input&lt;/span&gt;
&lt;span class="n"&gt;futureStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="n"&gt;bars&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="k"&gt;window&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="k"&gt;Call&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;depthRatioAnalysis&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt;
&lt;span class="k"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spikeComparisonForSym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corrForSym&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;depthRatioAnalysis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;targetSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;targetDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;futureStep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Using ADAUSDT as an example, the figure below compares the order book imbalance indicator with price movement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fios9ekyohvnt8ha9bk8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fios9ekyohvnt8ha9bk8i.png" alt=" " width="798" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the order book imbalance exceeded the 90th percentile, creating an extreme spike, the average price range over the following five minutes increased significantly across all assets. This suggests that severe order book imbalances are indeed associated with elevated short-term volatility and can serve as an effective early warning signal for flash wicks. &lt;/p&gt;

&lt;p&gt;The order book data used in this case includes the top 20 price levels from Binance and the top 5 price levels from OKX. Since this does not represent the exchanges' complete order books, we also used the difference between the best bid and best ask quantities (bidQty[0] - askQty[0]) as a real-time proxy for market liquidity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loadTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"dfs://CryptocurrencyTick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"depth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"BTCUSDT"&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;symbolSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sySource&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt; &lt;span class="k"&gt;between&lt;/span&gt; &lt;span class="n"&gt;startTargetTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;endTargetTime&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temporalAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"H"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;Convert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;UTC&lt;/span&gt;
&lt;span class="n"&gt;plotData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="n"&gt;bidQty&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;askQty&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;diffQty&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;Difference&lt;/span&gt; &lt;span class="k"&gt;between&lt;/span&gt; &lt;span class="n"&gt;cumulative&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt; &lt;span class="n"&gt;quantities&lt;/span&gt; &lt;span class="n"&gt;across&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;plotData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bidQty&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;askQty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;diffQty&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;
&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plotData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;`diffQty],plotData.eventTime.minute(),"bid-ask spread",extras={multiYAxes: true})
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flmu2lxvjxb52z19xeqin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flmu2lxvjxb52z19xeqin.png" alt=" " width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown in the figure above, the order book exhibited several episodes of significant imbalance throughout the day, with the most pronounced fluctuations occurring between 21:00 and 22:10. This period coincided with the sharpest price movements, indicating severe order flow imbalance and a substantially higher likelihood of flash wick events. &lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Real-Time Risk Monitoring System with VPIN
&lt;/h2&gt;

&lt;p&gt;Identifying the signal is only the first step. During extreme market conditions, traders cannot afford to wait until offline analysis is complete before taking action. To address this, we integrated these indicators into a real-time stream processing pipeline and built a VPIN-based monitoring system that automatically generates alerts whenever abnormal conditions are detected, helping traders reduce exposure or exit positions before market conditions deteriorate further. &lt;/p&gt;

&lt;p&gt;The workflow consists of four steps: &lt;/p&gt;

&lt;p&gt;1. Continuously ingest aggregated trade data and divide the order flow into equal-volume buckets (bucket size = average daily trading volume over the previous 20 trading days ÷ 50 buckets per day). &lt;/p&gt;

&lt;p&gt;2. Calculate the buy-sell imbalance (diffQty) for each bucket in real time. &lt;/p&gt;

&lt;p&gt;3. Compute VPIN using a rolling window of 5 buckets. Trigger an alert whenever VPIN exceeds a predefined threshold (e.g., 0.2). &lt;/p&gt;

&lt;p&gt;4. Push real-time notifications through channels such as WeCom or email to assist traders in making timely decisions. &lt;/p&gt;

&lt;p&gt;We replayed historical market data from October 10, 2025 to simulate the real-time monitoring process. Using  ADAUSDT as an example: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxzxefc88zo80b0qud509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxzxefc88zo80b0qud509.png" alt=" " width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results show that VPIN for ADAUSDT rose sharply after 21:00, closely matching the period of extreme price volatility. This demonstrates that the proposed monitoring framework is capable of providing meaningful early warning signals before flash wick events unfold.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Pin bars are not random market noise. From surging trading volume and rising VPIN to the depletion of order book liquidity, every stage leaves measurable traces in the data. The real danger is not the wick itself, but the liquidity vacuum and leveraged pressure that quietly accumulate beforehand—and those warning signs are already embedded in the market data long before the event occurs. &lt;/p&gt;

&lt;p&gt;All analyses in this article were implemented using DolphinDB. From batch historical replay across multiple exchanges to real-time stream processing of VPIN and order book imbalance with automated alerts, the entire workflow runs within a unified framework. The goal is simple: surface critical signals when they matter—not after the market has already moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;[1] Jung Hua Liu.(2025). Crypto “Black Swan” Crash: An Academic Analysis: &lt;a href="https://medium.com/@gwrx2005/the-october-11-2025-crypto-black-swan-crash-an-academic-analysis-db92a3d2ad66" rel="noopener noreferrer"&gt;++The October 11, 2025 Crypto “Black Swan” Crash: An Academic Analysis | by Jung-Hua Liu | Medium++&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[2] Nison, S. (1991). &lt;em&gt;Japanese Candlestick Charting Techniques&lt;/em&gt;. New York: New York Institute of Finance.&lt;/p&gt;

&lt;p&gt;[3] Torben G. Andersen,Oleg Bondarenko (2014). VPIN and the Flash Crash: Journal of Financial Markets&lt;/p&gt;

&lt;p&gt;[4] How to Read and Interpret Liquidity and Order Book Depth: &lt;a href="https://www.altrady.com/crypto-trading/fundamental-analysis/liquidity-order-book-depth" rel="noopener noreferrer"&gt;++How to Read and Interpret Liquidity and Order Book Depth++&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Thanks for reading! Stay connected with DolphinDB by following us on X &lt;a href="https://x.com/DolphinDB_Inc" rel="noopener noreferrer"&gt;(@DolphinDB_Inc)&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/dolphindb/posts/?feedView=all&amp;amp;viewAsMember=true" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for the latest updates, technical articles, and product news. For more information, visit our &lt;a href="https://dolphindb.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>fintech</category>
      <category>database</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Introduction to backtesting strategy: Historical data replay in DolphinDB</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Thu, 29 Aug 2024 03:08:33 +0000</pubDate>
      <link>https://dev.to/dolphindb/introduction-to-backtesting-strategy-historical-data-replay-in-dolphindb-1ole</link>
      <guid>https://dev.to/dolphindb/introduction-to-backtesting-strategy-historical-data-replay-in-dolphindb-1ole</guid>
      <description>&lt;p&gt;In DolphinDB, we can import historical data into a stream table in chronological order as “real-time data” so that the same script can be used both for backtesting and real-time trading. Regarding streaming in DolphinDB please refer to &lt;em&gt;&lt;a href="https://github.com/dolphindb/Tutorials_EN/blob/master/streaming_tutorial.md" rel="noopener noreferrer"&gt;DolphinDB Streaming Tutorial&lt;/a&gt;&lt;/em&gt; .&lt;/p&gt;

&lt;p&gt;This article introduces functions replay and replayDS and then demonstrates the process of data replaying.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8p90r0kgn2jvyyo8mbbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8p90r0kgn2jvyyo8mbbj.png" alt="Image description" width="720" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Functions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;replay&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replay(inputTables, outputTables, [dateColumn], [timeColumn], [replayRate], [absoluteRate=true], [parallelLevel=1])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function replay injects data from specified tables or data sources into stream tables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;‘inputTables’ is a table or a tuple. Each element of the tuple is an unpartitioned table or a data source generated by function replayDS.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘outputTables’ is a table or a tuple of tables, or a string or a string vector. The number of elements of outputTables must be the same as the number of elements of inputTables. If it is a vector, it is a list of the names of the shared stream tables where the replayed data of the corresponding tables of inputTables are saved. If it is a tuple, each element is a shared stream table where the replayed data of the corresponding table in inputTables are saved. The schema of each table in outputTables must be identical as the schema of the corresponding table in inputTables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘dateColumn’ and ‘timeColumn’ are strings indicating the date column and time column in inputTables. If neither is specified, the first column of the table is chosen as ‘dateColumn’. If there is a ‘dateColumn’, it must be one of the partitioning columns. If only ‘timeColumn’ is specified, it must be one of the partitioning columns. If information about date and time comes from the same column (e.g., DATETIME, TIMESTAMP), use the same column for both ‘dateColumn’ and ‘timeColumn’. Data are replayed in batches determined by the smallest unit of time in ‘timeColumn’ or ‘dateColumn’ if ‘timeColumn’ is not specified. For examples, if the smallest unit of time in ‘timeColumn’ is second then all data in the same second are replayed in the same batch; if ‘timeColumn’ is not specified, then all data in the same day are replayed in the same batch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘replayRate’ is a nonnegative integer indicating the number of rows to be replayed per second. If it is not specified, it means data are replayed at the maximum speed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘replayRate’ is an integer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘absoluteRate’ is a Boolean value. The default value is true.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regarding ‘replayRate’ and ‘absoluteRate’:&lt;/p&gt;

&lt;p&gt;(1) If ‘replayRate’ is a positive integer and absoluteRate=true, replay at the speed of ‘replayRate’ rows per second.&lt;/p&gt;

&lt;p&gt;(2) If ‘replayRate’ is a positive integer and absoluteRate=false, replay at ‘replayRate’ times the original speed of the data. For example, if the difference between the maximum and the minimum values of ‘dateColumn’ or ‘timeColumn’ is n seconds, then it takes n/replayRate seconds to finish the replay.&lt;/p&gt;

&lt;p&gt;(3) If ‘replayRate’ is unspecified or negative, replay at the maximum speed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;‘parallelLevel’ is a positive integer. When the size of individual partitions in the data sources is too large relative to memory size, we need to use function replayDS to further divide individual partitions into smaller data sources. 'parallelLevel' indicates the number of threads loading data into memory from these smaller data sources simultaneously. The default value is 1. If 'inputTables' is a table or a tuple of tables, the effective 'parallelLevel' is always 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;replayDS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replayDS(sqlObj, [dateColumn], [timeColumn], [timeRepartitionSchema])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function replayDS generates a group of data sources to be used as the inputs of function replay. It splits a SQL query into multiple subqueries based on 'timeRepartitionSchema' with 'timeColumn' within each 'dateColumn' partition.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;‘sqlObj’ is a table or metacode with SQL statements (such as ) indicating the data to be replayed. The table object of “select from” must use a DATE type column as one of the partitioning columns.&lt;/li&gt;
&lt;li&gt;‘dateColumn’ and ‘timeColumn’ are strings indicating the date column and time column. If neither is specified, the first column of the table is chosen as ‘dateColumn’. If there is a ‘dateColumn’, it must be one of the partitioning columns. If only ‘timeColumn’ is specified, it must be one of the partitioning columns. If information about date and time comes from the same column (e.g., DATETIME, TIMESTAMP), use the same column for both ‘dateColumn’ and ‘timeColumn’. Function replayDS and the corresponding function replay must use the same set of 'dateColumn' and 'timeColumn'.&lt;/li&gt;
&lt;li&gt;‘timeRepartitionSchema’ is a TIME or NANOTIME type vector. ‘timeRepartitionSchema’ deliminates multiple data sources on the dimension of ‘timeColumn’ within each ‘dateColumn’ partition. For example, if timeRepartitionSchema=[t1, t2, t3], then there are 4 data sources within a day: [00:00:00.000,t1), [t1,t2), [t2,t3) and [t3,23:59:59.999).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Replay a single in-memory table&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replay(inputTable, outputTable, `date, `time, 10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replay a single table using data sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To replay a single table with a large number of rows, we can use function replayDS together with function replay. Function replayDSdeliminates multiple data sources on the dimension of 'timeColumn' within each 'dateColumn' partition. Parameter 'parallelLevel' of functionreplay` specifies the number of threads loading data into memory from these smaller data sources simultaneously. In this example, 'parallelLevel' is set to 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqu7ln2m0ce0760kvx6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqu7ln2m0ce0760kvx6z.png" alt="Image description" width="632" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replay multiple tables simultaneously using data sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To replay multiple tables simultaneously, assign a tuple of these table names to parameter ‘inputTables’ of function replay and specify the output tables. Each of the output tables corresponds to an input table and should have the same schema as the corresponding input table. All input tables should have identical 'dateColumn' and 'timeColumn'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frizqoi2b3mple9zhnwbz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frizqoi2b3mple9zhnwbz.png" alt="Image description" width="676" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cancel replay&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If function replay was called with submitJob, we can use getRecentJobs to get jobId, then cancel the replay with command cancelJob.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt7x72fj2svzp22vpcup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt7x72fj2svzp22vpcup.png" alt="Image description" width="188" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If function replay was called directly, we can use getConsoleJobs in another GUI session to get jobId, then cancel the replay use command cancelConsoleJob.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyamotibcztc90oxcxne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyamotibcztc90oxcxne.png" alt="Image description" width="272" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. How to use replayed data
&lt;/h2&gt;

&lt;p&gt;Replayed data are streaming data. We can subscribe to and process the replayed data in the following 3 ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscribe in DolphinDB. Write user-defined functions in DolphinDB to process streaming data.&lt;/li&gt;
&lt;li&gt;Subscribe in DolphinDB. To conduct real-time calculations with streaming data, use DolphinDB’s built-in streaming aggregators such as time-series aggregator, cross-sectional aggregator and anomaly detection engine. They are very easy to use and have excellent performance. In section 3.2, we use a cross-sectional aggregator to calculate the intrinsic value of an ETF.&lt;/li&gt;
&lt;li&gt;With third-party client through DolphinDB’s streaming API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Examples
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Replay level 1 stock quotes to calculate ETF intrinsic value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this example, we replay the level 1 stock quotes in US stock markets on 2007/08/17, and calculate the intrinsic value of an ETF with the built-in cross-sectional aggregator in DolphinDB. The following are the schema of the input table ‘quotes’ and a preview of the data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpz4be23nmw34q0t50fg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpz4be23nmw34q0t50fg.png" alt="Image description" width="502" height="58"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F246lj3gxmrk3wv8nge7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F246lj3gxmrk3wv8nge7q.png" alt="Image description" width="266" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhogib5rgcwgvpqpj3nh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhogib5rgcwgvpqpj3nh.png" alt="Image description" width="492" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5h65cdv20uh8cwxnfam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5h65cdv20uh8cwxnfam.png" alt="Image description" width="720" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(1) To replay a large amount of data, if we load all data into memory first, we may have an out-of-memory problem. We can first use function replayDS and specify parameter 'timeRepartitionSchema' to divide the data into 60 parts based on the column 'time'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4otlnpqljomd2t94rlu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4otlnpqljomd2t94rlu.png" alt="Image description" width="653" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(2) Define the output stream table ‘outQuotes’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6zjm8nneex9vs6eqxdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6zjm8nneex9vs6eqxdy.png" alt="Image description" width="626" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(3) Define a dictionary for the ETF components weights and function etfVal to calculate ETF intrinsic value. For simplicity we use an ETF with only 6 component stocks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fero0khk6gt73fdhj5v7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fero0khk6gt73fdhj5v7z.png" alt="Image description" width="454" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(4) Define a streaming aggregator to subscribe to the output stream table ‘outQuotes’. We specify a filtering condition for the subscription that only data with stock symbols of AAPL, IBM, MSFT, NTES, AMZN or GOOG are published to the aggregator. This significantly reduces unnecessary network overhead and data transfer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp42m35audgh3yvax0u1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp42m35audgh3yvax0u1.png" alt="Image description" width="619" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(5) Start to replay data at the specified speed of 100,000 rows per second. The streaming aggregator conducts real-time calculation with the replayed data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt25a1pbxjwm65qfp5kt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt25a1pbxjwm65qfp5kt.png" alt="Image description" width="658" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(6) Check ETF intrinsic values&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07z8ujazkawggtqahlbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07z8ujazkawggtqahlbo.png" alt="Image description" width="343" height="39"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe60enjdifpb5m3qy6glk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe60enjdifpb5m3qy6glk.png" alt="Image description" width="284" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Performance testing
&lt;/h2&gt;

&lt;p&gt;We tested data replaying in DolphinDB on a server with the following configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server: DELL PowerEdge R730xd&lt;/li&gt;
&lt;li&gt;CPU: Intel Xeon(R) CPU E5–2650 v4(24cores, 48 threads, 2.20GHz)&lt;/li&gt;
&lt;li&gt;RAM: 512 GB (32GB × 16, 2666 MHz)&lt;/li&gt;
&lt;li&gt;Harddisk: 17T HDD (1.7T × 10, read speed 222 MB/s, write speed 210 MB/s)&lt;/li&gt;
&lt;li&gt;Network: 10 Gigabit Ethernet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DolphinDB script:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4htq53ysqwtf1reogvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4htq53ysqwtf1reogvm.png" alt="Image description" width="666" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When replaying at maximum speed (parameter ‘replayRate’ is not specified) and the output table is not subscribed, it only takes about 100 seconds to replay 336,305,414 rows of data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dolphindb/Tutorials_EN/blob/master/historical_data_replay.md?source=post_page-----497e24af596d--------------------------------" rel="noopener noreferrer"&gt;https://github.com/dolphindb/Tutorials_EN/blob/master/historical_data_replay.md?source=post_page-----497e24af596d--------------------------------&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dolphindb</category>
      <category>backtesting</category>
      <category>datareplay</category>
      <category>marketdata</category>
    </item>
    <item>
      <title>Helpful Tools for Quant丨Efficiently Calculate Transaction Costs from Tick Data</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 28 Aug 2024 01:48:54 +0000</pubDate>
      <link>https://dev.to/dolphindb/helpful-tools-for-quantgun-efficiently-calculate-transaction-costs-from-tick-data-3ggb</link>
      <guid>https://dev.to/dolphindb/helpful-tools-for-quantgun-efficiently-calculate-transaction-costs-from-tick-data-3ggb</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwexqcy83g5pckqqhzopd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwexqcy83g5pckqqhzopd.png" alt="Image description" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The calculation of transaction costs from tick data often involves two tables: trade and nbbo. As the timestamps of both tables are at nanosecond level, there are virtually no exact match between the timestamps of the two tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3bh3f6e3dfzhxohe9os.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3bh3f6e3dfzhxohe9os.png" alt="Image description" width="720" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, in order to calculate transaction costs, we need to locate the most recent quote before each trade (of the same stock). We may also need to calculate the average quotes within a specific window relative to each trade. &lt;strong&gt;These non-exact joins are frequently used in quant finance, but they are not supported in most databases&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ui0d7icjmkuyozyqw0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ui0d7icjmkuyozyqw0h.png" alt="Image description" width="720" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, DolphinDB provides you with &lt;strong&gt;asof join&lt;/strong&gt; and &lt;strong&gt;window join&lt;/strong&gt; for these scenarios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gnixdc2e20m44yospbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gnixdc2e20m44yospbn.png" alt="Image description" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltkt1tc3wuftgt651qgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltkt1tc3wuftgt651qgs.png" alt="Image description" width="720" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a look at the following case!&lt;/p&gt;

&lt;p&gt;The data used in this example is the &lt;strong&gt;high-frequency data&lt;/strong&gt; from the &lt;strong&gt;New York Stock Exchange&lt;/strong&gt;, consisting of two tables: trade and nbbo, respectively containing 27 million and 78 million records. The DolphinDB script is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trade = loadTable("dfs://EQY", "trade")
select count(*) from trade

nbbo = loadTable("dfs://EQY", "nbbo")
select count(*) from nbbo

// asof join
timer TC1 = select sum(Trade_Volume*abs(Trade_Price-(Bid_Price+Offer_Price)/2))/sum(Trade_Volume*Trade_Price)*10000 as TC_aj from aj(trade,nbbo,`Symbol`Time) where Time between 09:30:00.000000000 : 15:59:59.999999999 group by symbol

// window join
timer TC2 = select sum(Trade_Volume*abs(Trade_Price-(Bid_Price+Offer_Price)/2))/sum(Trade_Volume*Trade_Price)*10000 as TC_wj from pwj(trade,nbbo,-100000000:0,&amp;lt;[avg(Offer_Price) as Offer_Price, avg(Bid_Price) as Bid_Price]&amp;gt;,`Symbol`Time) where Time between 09:30:00.000000000 : 15:59:59.999999999 group by symbol

select * from ej(TC1,TC2,`symbol) where symbol in `AAPL`MS`EBAY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just &lt;strong&gt;one line of script&lt;/strong&gt; can implement complex calculation logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// asof join
timer TC1 = select sum(Trade_Volume*abs(Trade_Price-(Bid_Price+Offer_Price)/2))/sum(Trade_Volume*Trade_Price)*10000 as TC_aj from aj(trade,nbbo,`Symbol`Time) where Time between 09:30:00.000000000 : 15:59:59.999999999 group by symbol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes &lt;strong&gt;339 milliseconds&lt;/strong&gt; to complete the calculation of transaction costs with &lt;strong&gt;asof join&lt;/strong&gt;, which is more than &lt;strong&gt;100 times faster than the equivalent calculation in Python pandas&lt;/strong&gt;, and the script to calculate with &lt;strong&gt;window join&lt;/strong&gt; takes &lt;strong&gt;402 milliseconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figb3qeanqsnz0gy8v1jh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figb3qeanqsnz0gy8v1jh.png" alt="Image description" width="720" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While calculating transaction costs, DolphinDB shows an excellent performance with concise code. For a visual representation of the operation covered in this article, you can take one minute watching this demo!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/2HfTRLDYUrY" rel="noopener noreferrer"&gt;https://youtu.be/2HfTRLDYUrY&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dolphindb</category>
      <category>database</category>
      <category>stockmarket</category>
      <category>hft</category>
    </item>
    <item>
      <title>Mastering Pairwise Correlations Calculation of Securities Through Coding</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Tue, 27 Aug 2024 07:23:22 +0000</pubDate>
      <link>https://dev.to/dolphindb/mastering-pairwise-correlations-calculation-of-securities-through-coding-251l</link>
      <guid>https://dev.to/dolphindb/mastering-pairwise-correlations-calculation-of-securities-through-coding-251l</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futjhqcguw56snu0kmdsr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futjhqcguw56snu0kmdsr.png" alt="Image description" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Calculating pairwise correlations of securities sheds light on the relationship between different securities, assisting in investment decision-making and risk management. In this article, you’re going to learn &lt;strong&gt;how to calculate the pairwise correlations of multiple securities using high frequency data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The tool we use is &lt;a href="https://www.dolphindb.com/alone/alone.php?id=75" rel="noopener noreferrer"&gt;DolphinDB&lt;/a&gt; and the data we use is the &lt;strong&gt;high-frequency quote data&lt;/strong&gt; of US stocks on August 1, 2007. The raw data is 16.1 GB with 380 million records. We will calculate the pairwise correlations of the &lt;strong&gt;most actively traded 500 stocks&lt;/strong&gt; in that day.&lt;/p&gt;

&lt;p&gt;Let’s delve into the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select count(*) from loadTable("dfs://TAQ", "quotes") where date= 2007.08.01

def getStockCorrelation(dateValue, num){
    quotes = loadTable("dfs://TAQ", "quotes")

    syms = (exec count(*) from quotes where date = dateValue, time between 09:30:00 : 15:59:59, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1 group by Symbol order by count desc).Symbol[0:num]
    priceMatrix = exec avg(bid + ofr)/2.0 as price from quotes where date = dateValue, Symbol in syms, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1, time between 09:30:00 : 15:59:59 pivot by time.minute() as minute, Symbol
    retMatrix = each(def(x):ratios(x)-1, priceMatrix)
    correlationMatrix = corrMatrix(retMatrix[1:,].ffill())

    mostCorrelated = select * from table(correlationMatrix.columnNames() as sym, correlationMatrix).unpivot(`sym, syms).rename!(`sym`corrSym`corr) context by sym having rank(corr,false) between 1:10
    return mostCorrelated
}

dateValue = 2007.08.01
num = 500
timer mostCorrelated = defStockCorrelation(dateValue,num)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we generate a vector of 500 stock tickers with the largest number of quote records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;syms = (exec count(*) from quotes where date = dateValue, time between 09:30:00 : 15:59:59, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1 group by Symbol order by count desc).Symbol[0:num]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F236bl1skzfylgqirigq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F236bl1skzfylgqirigq0.png" alt="Image description" width="720" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why we use “exec” statement?&lt;/p&gt;

&lt;p&gt;The syntax of the “exec” statement is the same as the “select” statement except that “&lt;strong&gt;select&lt;/strong&gt;” always returns a &lt;strong&gt;table **whereas “&lt;/strong&gt;exec*&lt;em&gt;” can return a **scalar, vector, matrix or table&lt;/em&gt;*, which is more convenient for certain operations.&lt;/p&gt;

&lt;p&gt;Next, we select the records for these 500 stocks and filter out dirty data, then generate a &lt;strong&gt;minute-level mid-price matrix&lt;/strong&gt; with stock tickers as column labels and minutes as row labels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;priceMatrix = exec avg(bid + ofr)/2.0 as price from quotes where date = dateValue, Symbol in syms, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1, time between 09:30:00 : 15:59:59 pivot by time.minute() as minute, Symbol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwgjd0rukbgd0h8rikr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwgjd0rukbgd0h8rikr.png" alt="Image description" width="720" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then convert the minute-level mid-price matrix into a &lt;strong&gt;stock return matrix&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;retMatrix = each(def(x):ratios(x)-1, priceMatrix)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckqdapkfe7qtr7qlweil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckqdapkfe7qtr7qlweil.png" alt="Image description" width="720" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on the stock return matrix, we calculate the correlation between every two columns to get a &lt;strong&gt;500 by 500 pairwise correlation matrix&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;correlationMatrix = corrMatrix(retMatrix[1:,].ffill())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcqyprjb2j7umpxo3y24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcqyprjb2j7umpxo3y24.png" alt="Image description" width="720" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So actually, it only takes 4 lines of code to get the pairwise correlation matrix from the high frequency data in DolphinDB.&lt;/p&gt;

&lt;p&gt;In addition, running the script only takes &lt;strong&gt;2.5 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57kbsq5atl8e7iylzk4p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57kbsq5atl8e7iylzk4p.png" alt="Image description" width="720" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the accuracy, we get the 3 stocks with the highest correlation with Lehman Brothers from the pairwise correlation matrix. The outputs are Morgan Stanley, Goldman Sachs, and Merrill Lynch, which are all investment bank stocks that are supposed to be highly correlated with Lehman Brothers.&lt;/p&gt;

&lt;p&gt;In this example, DolphinDB demonstrates excellent performance in data analysis of large amounts of data with elegant code and minimal coding efforts. The calculation process can be encapsulated into a function to be used in script as well as through various APIs such as Python, C++, Java, C#, Go, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;addFunctionView(getStockCorrelation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the video below to try the efficient calculation 👇&lt;br&gt;
&lt;a href="https://youtu.be/kmz5OsoDBs0" rel="noopener noreferrer"&gt;https://youtu.be/kmz5OsoDBs0&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>stockmarket</category>
      <category>coding</category>
      <category>dolphindb</category>
    </item>
    <item>
      <title>Accelerating Data Analysis: Embracing the Efficient Query and Aggregation Calculations</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Mon, 26 Aug 2024 08:18:06 +0000</pubDate>
      <link>https://dev.to/dolphindb/accelerating-data-analysis-embracing-the-efficient-query-and-aggregation-calculations-5ed6</link>
      <guid>https://dev.to/dolphindb/accelerating-data-analysis-embracing-the-efficient-query-and-aggregation-calculations-5ed6</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfjblv0t0sca60aziezi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfjblv0t0sca60aziezi.png" alt="Image description" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Efficient query and calculation capabilities play a pivotal role in handling large-scale datasets, and ensuring timely and accurate data analysis. In this article, we will explore &lt;strong&gt;how DolphinDB’s query and advanced calculation features significantly enhance data processing and analysis workflows through practical code examples&lt;/strong&gt;👇&lt;/p&gt;

&lt;p&gt;In this case, we choose &lt;strong&gt;TSDB engine&lt;/strong&gt; as the DolphinDB storage engine. We use a server with 16 CPU cores, 512 GB of memory, and 4 SSDs. The read speed of each disk is about 400 MB/s.&lt;/p&gt;

&lt;p&gt;The data we use is the US Stock Exchange level 1 quotes of 4 years. The &lt;strong&gt;original data size&lt;/strong&gt; is around &lt;strong&gt;12.75 TB&lt;/strong&gt;. &lt;strong&gt;After compression&lt;/strong&gt;, the disk space occupied by DolphinDB is &lt;strong&gt;2.12 TB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;All these data are stored in one table in DolphinDB, and there are almost &lt;strong&gt;270 billion records&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;login(`admin, `123456)
pnodeRun(clearAllCache)
quotes = loadTable("dfs://TAQ", "quotes")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ud1i4zf1zo3uo6lwy3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ud1i4zf1zo3uo6lwy3p.png" alt="Image description" width="720" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, let’s do the query.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can get the data of Lehman Brothers at 3:59:59 PM, August 21, 2007 through the code below. It turns out that DolphinDB takes only 7.9 ms to extract one second’s data of a stock from 270 billion (12.75 TB) records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select count(*) from quotes 

timer x=select symbol, time, bid, ofr from quotes where symbol='LEH', date=2007.08.21 and time=15:59:59
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fypru6fmeys7roynyad6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fypru6fmeys7roynyad6y.png" alt="Image description" width="720" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The query result is assigned to the variable x, and the content of x can be shown in the data browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2qw0hx2ebivvtgaboui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2qw0hx2ebivvtgaboui.png" alt="Image description" width="720" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next, let’s calculate the average bid-ask spread of Lehman Brothers in each minute on Aug 31, 2007.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bid-ask spread is defined as the ask price minus the bid price, and then divided by the average ask price and the bid price, and we also add some filtering conditions to ensure the validity of the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.08.31, symbol=`LEH, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, ofr&amp;gt;0, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script, and we can see that &lt;strong&gt;execution takes 89 milliseconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87vffy2icu42jireuu8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87vffy2icu42jireuu8r.png" alt="Image description" width="494" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The calculation result is plotted in this graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folz32ed36aaznug0fuwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folz32ed36aaznug0fuwo.png" alt="Image description" width="720" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then we’ll increase the data volume, remove the stock code limitation, and calculate the bid-and-ask price difference per minute.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.09.24, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data of all stocks in the whole market for a whole day is about 10 GB. Run the script, and it takes &lt;strong&gt;2.1 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0jn8jgc0uwlec2cje6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0jn8jgc0uwlec2cje6z.png" alt="Image description" width="573" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a well-designed system, the time it takes to finish a query should be linearly related to the amount of days the query consumes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu5gv361ccd63iztp7i4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu5gv361ccd63iztp7i4.png" alt="Image description" width="720" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So this time, we performs the same calculation on 3 days.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date between 2007.09.25 : 2007.09.27, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes &lt;strong&gt;5.47 seconds&lt;/strong&gt;, about 3 times as long as the previous query, which means that the time consumption is directly proportional to the amount of data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihq25tavlut4sgoxntzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihq25tavlut4sgoxntzx.png" alt="Image description" width="587" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a word, performance of DolphinDB is pretty good for both querying a small amount of data and aggregating calculations with large amounts of data.&lt;/p&gt;

&lt;p&gt;The complete script is displayed as follows 👇👀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;login(`admin, `123456)
pnodeRun(clearAllCache)
quotes = loadTable("dfs://TAQ", "quotes")

select count(*) from quotes 

timer x=select symbol, time, bid, ofr from quotes where symbol='LEH', date=2007.08.21 and time=15:59:59

timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.08.31, symbol=`LEH, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, ofr&amp;gt;0, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")

timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.09.24, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")

timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date between 2007.09.25 : 2007.09.27, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can follow the video below, experiencing the efficient query and aggregation calculation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/A_XoHP9NboE" rel="noopener noreferrer"&gt;https://youtu.be/A_XoHP9NboE&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>coding</category>
      <category>stockmarket</category>
      <category>dolphindb</category>
    </item>
    <item>
      <title>High Frequency Data Analysis: Converting High-frequency Signals to Discrete Buy/Sell Signals</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Fri, 23 Aug 2024 03:01:17 +0000</pubDate>
      <link>https://dev.to/dolphindb/high-frequency-data-analysis-converting-high-frequency-signals-to-discrete-buysell-signals-46de</link>
      <guid>https://dev.to/dolphindb/high-frequency-data-analysis-converting-high-frequency-signals-to-discrete-buysell-signals-46de</guid>
      <description>&lt;p&gt;In high-frequency trading, we generate high-frequency signals from trade and quote tick data and analyze these signals to identify trading opportunities. This tutorial demonstrates how to convert high-frequency signals into discrete buy/sell/hold signals. Essentially, the problem is to convert an array of floating-point values into an array of only 3 integers: +1 (buy), 0 (hold) and -1 (sell).&lt;/p&gt;

&lt;p&gt;The conversion rules can be quite simple. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;+1 if signal &amp;gt; t1&lt;/li&gt;
&lt;li&gt;-1 if signal &amp;lt; t2&lt;/li&gt;
&lt;li&gt;0 otherwise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Such conversion rules can be easily implemented in DolphinDB with function &lt;code&gt;iif&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iif(signal &amp;gt; t1, 1, iif(signal &amp;lt;t2, -1, 0))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, to avoid too frequent reversals of trading direction, we usually adopt a more complex set of rules: if a signal is above the t1 threshold, it’s a buy signal (+1) and the subsequent signals remain buy signals until one falls below the t10 threshold. Similarly, if a signal is below the t2 threshold, it is a sell signal (-1) and the subsequent signals remain sell signals until a signal exceeds the t20 threshold. The relationship between the thresholds is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1 &amp;gt; t10 &amp;gt; t20 &amp;gt; t2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above rules, the value of a trade signal is determined not only by the value of the current signal but also by the state of the previous signal. This is a typical example of path dependence, which is commonly considered unsuitable or difficult to be handled by vector operations and thus very slow in scripting languages including DolphinDB.&lt;/p&gt;

&lt;p&gt;In some cases, however, a path dependence problem can be solved with vector operations. The problem above is one such example. The next section describes how to solve it with vector operations.&lt;/p&gt;

&lt;p&gt;First, find out the signals that fall in the ranges of a determined state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if signal &amp;gt; t1, state=1&lt;/li&gt;
&lt;li&gt;if signal &amp;lt; t2, state=-1&lt;/li&gt;
&lt;li&gt;if t20&amp;lt;= signal &amp;lt;= t10, state=0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The states of the signals in the ranges of [t2,t20] and [t10,t1] are determined by the states of the signals preceding these ranges.&lt;/p&gt;

&lt;p&gt;The DolphinDB script for implementing the above rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;direction = (iif(signal&amp;gt;t1, 1, iif(signal&amp;lt;t2, -1, iif(signal between t20:t10, 0, NULL)))).ffill().nullFill(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s run a simple test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1= 10
t10 = 6
t20 = -6
t2 = -10
signal = 20.12 8.78 4.39 -20.68 -8.49 -6.98 0.7 2.08 8.97 12.41
direction = (iif(signal&amp;gt;t1, 1, iif(signal&amp;lt;t2, -1, iif(signal between t20:t10, 0, NULL)))).ffill().nullFill(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script would be like this if we use pandas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1=60
t10=50
t20=30
t2=20
signal=pd.Series([20.12,8.78,4.39,-20.68,-8.49,-6.98,0.7,2.08,8.97,12.41])
start=time.time()
direction1=(signal.apply(lambda signal: 1 if signal &amp;gt; t1 else(-1 if signal&amp;lt;t2 else(0 if t20 &amp;lt; signal &amp;lt; t10 else np.nan)))).ffill().fillna(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test below generates a random array of 10 million signals between 0 and 100 to test the performance of DolphinDB and pandas. The test environment setup is as follows:&lt;/p&gt;

&lt;p&gt;CPU: Intel(R) Core(TM) i7–7700 CPU @3.60GHz 3.60 GHz&lt;/p&gt;

&lt;p&gt;Memory: 16 GB&lt;/p&gt;

&lt;p&gt;OS: Windows 10&lt;/p&gt;

&lt;p&gt;The executions take 171.73ms (DolphinDB) and 3.28 seconds (pandas), respectively.&lt;/p&gt;

&lt;p&gt;DolphinDB script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1= 10
t10 = 6
t20 = -6
t2 = -10
signal = rand(100.0, 10000000)
direction = (iif(signal&amp;gt;t1, 1, iif(signal&amp;lt;t2, -1, iif(signal between t20:t10, 0, NULL)))).ffill().nullFill(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pandas script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time
t1= 60
t10= 50
t20= 30
t2= 20
signal= pd.Series(np.random.random(10000000) * 100)
start= time.time()
direction1=(signal.apply(lambda signal: 1 if signal &amp;gt; t1 else(-1 if signal&amp;lt;t2 else(0 if t20 &amp;lt; signal &amp;lt; t10 else np.nan)))).ffill().fillna(0)
end= time.time()
print(end- start)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>hft</category>
      <category>dolphindb</category>
      <category>database</category>
      <category>pandas</category>
    </item>
    <item>
      <title>How to Downsample Your Data Efficiently</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Thu, 22 Aug 2024 03:33:09 +0000</pubDate>
      <link>https://dev.to/dolphindb/how-to-downsample-your-data-efficiently-3037</link>
      <guid>https://dev.to/dolphindb/how-to-downsample-your-data-efficiently-3037</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcymt81c4o24l7jtailva.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcymt81c4o24l7jtailva.jpeg" alt="Image description" width="720" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tired of spending countless mintues downsampling your data? Look no further!&lt;/p&gt;

&lt;p&gt;In this article, you’ll learn how to efficiently downsample &lt;strong&gt;6.48 billion high-frequency records&lt;/strong&gt; to &lt;strong&gt;61 million minute-level records&lt;/strong&gt; in only &lt;strong&gt;41 seconds&lt;/strong&gt; in DolphinDB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The basic configuration of the DolphinDB server is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;16 CPU cores&lt;/li&gt;
&lt;li&gt;256 GB memory&lt;/li&gt;
&lt;li&gt;4 SSDs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A DolphinDB cluster with 4 data nodes is deployed, and each node uses a SSD.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1mtvw5fvgh8935zv00t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1mtvw5fvgh8935zv00t.png" alt="Image description" width="720" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The data we use is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the level 1 quotes on August, 2007 from New York Stock Exchange&lt;/li&gt;
&lt;li&gt;around 272 GB, with 6.48 billion records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Downsampling can be performed with a SQL statement in DolphinDB.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc0e4xzjvsfriuxpxc1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc0e4xzjvsfriuxpxc1e.png" alt="Image description" width="720" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the SQL query may involve multiple partitions, DolphinDB breaks down the job into several tasks and assigns the tasks to the corresponding data nodes for parallel execution. When all the tasks are completed, the system merges the intermediate results from the nodes to return the final result.&lt;/p&gt;

&lt;p&gt;The script is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db = database("dfs://TAQ")
quotes = db.loadTable("quotes")
select count(*)  from quotes where date between 2007.08.01 : 2007.08.31

model=select  top 1 symbol,date, minute(time) as minute, bid, ofr from quotes where date=2007.08.01,symbol=`EBAY
if(existsTable("dfs://TAQ", "quotes_minute_sql"))
 db.dropTable("quotes_minute_sql")
db.createPartitionedTable(model, "quotes_minute_sql", `date`symbol)

timer{
 minuteQuotes=select avg(bid) as bid, avg(ofr) as ofr from quotes where data between 2007.08.01 : 2007.08.31 group by symbol,date,bar(time, 60) as minute
 loadTable("dfs://TAQ", "quotes_minute_sql").append!(minuteQuotes)
}

select count(*)  from loadTable("dfs://TAQ", "quotes_minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frequency can be adjusted as needed just by modifying &lt;code&gt;bar(time, 60)&lt;/code&gt;. Here 60 means the data is downsampled to 1-minute interval as the timestamp values have seconds precision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh7wtgz4b21dmbs760lx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh7wtgz4b21dmbs760lx.png" alt="Image description" width="720" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The table “quotes_minute_sql“ is created with &lt;code&gt;createPartitionedTable&lt;/code&gt; and the downsampled result can be appended to this table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvu1wty8pw3mazy372xb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvu1wty8pw3mazy372xb.png" alt="Image description" width="659" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can execute the script and visit the web-based user interface to check the resource usage. It’s shown that all CPU cores have participated in the downsampling. On each data node, 15 tasks are running concurrently as data is being read from disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fgp016m4h6226hechgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fgp016m4h6226hechgy.png" alt="Image description" width="720" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we come back to VScode and check the execution status, we find that it only takes &lt;strong&gt;41 seconds&lt;/strong&gt; to complete the data downsampling, which generates 61 million minute-level records.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks6hvvfshbniuthe54fx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks6hvvfshbniuthe54fx.png" alt="Image description" width="720" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DolphinDB exhibits outstanding performance in data downsampling due to the following reasons:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Jobs are executed distributedly and resources of different nodes can be utilized at the same time;&lt;/li&gt;
&lt;li&gt;Compression reduces the disk I/O;&lt;/li&gt;
&lt;li&gt;Columnar storage and vectorized computation improve the efficiency of aggregation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To learn detailed operations of data downsampling, take a look at this demo!&lt;br&gt;
&lt;a href="https://youtu.be/0vRuiz1Lf6Y" rel="noopener noreferrer"&gt;https://youtu.be/0vRuiz1Lf6Y&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>dolphindb</category>
      <category>quant</category>
      <category>sql</category>
    </item>
    <item>
      <title>A Simpler Way to Calculate WorldQuant 101 Alphas</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 21 Aug 2024 02:31:10 +0000</pubDate>
      <link>https://dev.to/dolphindb/a-simpler-way-to-calculate-worldquant-101-alphas-m32</link>
      <guid>https://dev.to/dolphindb/a-simpler-way-to-calculate-worldquant-101-alphas-m32</guid>
      <description>&lt;p&gt;The formulas of &lt;strong&gt;101 quantitative trading alphas&lt;/strong&gt; used by WorldQuant were presented in the paper &lt;em&gt;&lt;a href="https://arxiv.org/ftp/arxiv/papers/1601/1601.00991.pdf" rel="noopener noreferrer"&gt;101 Formulaic Alphas&lt;/a&gt;&lt;/em&gt;. However, some formulas are complex, leading to challenges in calculation.&lt;/p&gt;

&lt;p&gt;Take the calculation formula of &lt;strong&gt;Alpha#98&lt;/strong&gt; for example, and the data we use is the daily data from the New York Stock Exchange.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kakushadze et al 2015 Alpha #98: 
(rank(decay_linear(correlation(vwap, sum(adv5, 26.4719), 4.58418), 7.18088)) -
rank(decay_linear(Ts_Rank(Ts_ArgMin(correlation(rank(open), rank(adv15), 20.8187), 8.62571), 6.95668), 8.07206)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This formula involves both &lt;strong&gt;cross-sectional data&lt;/strong&gt; and &lt;strong&gt;time series data&lt;/strong&gt;, and the calculation uses nested functions with up to 6 levels, which is extremely difficult to implement in most systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5mwaekfbfi5naxfbmko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5mwaekfbfi5naxfbmko.png" alt="Image description" width="720" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can significantly reduce the development cost of complex calculations such as Alpha#98 by using DolphinDB’s built-in functions with panel data (in matrix form).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ovalgklgbxburgg9e2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ovalgklgbxburgg9e2p.png" alt="Image description" width="720" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11od5mfbwbowrcd91j9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11od5mfbwbowrcd91j9n.png" alt="Image description" width="720" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why is DolphinDB’s code so concise and elegant? 😲&lt;/p&gt;

&lt;p&gt;On the one hand, using &lt;strong&gt;panel data&lt;/strong&gt; provided by DolphinDB to implement the Alpha#98 factor simplifies the calculation logic and makes the code very precise. Panel data is a matrix that combines cross-sectional data and time-series data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxzvii7ptp7m0v8pb67p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxzvii7ptp7m0v8pb67p.png" alt="Image description" width="674" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, DolphinDB has &lt;strong&gt;more than 1,500 built-in functions&lt;/strong&gt; and many of them are optimized. You can implement all 101 alphas with DolphinDB built-in functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful8tbp0tyjpmb7bk25wq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful8tbp0tyjpmb7bk25wq.png" alt="Image description" width="720" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a look at DolphinDB script, which is very similar to the original formulas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def alpha98Panel(vwap, open, vol){
    return tsRank(mavg(mcorr(vwap, msum(mavg(vol, 5), 26), 5), 1..7)) - tsRank(mavg(mrank(9 - mimin(mcorr(tsRank(open), 
    tsRank(mavg(vol, 15)), 21), 9), true, 7), 1..8))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script, and it only takes &lt;strong&gt;986ms&lt;/strong&gt; to generate a matrix with 7,162 columns and 252 rows and calculate Alpha#98 with the matrix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4vgpkd36u1o6yi7o33a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4vgpkd36u1o6yi7o33a.png" alt="Image description" width="720" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, DolphinDB supports &lt;strong&gt;unified stream and batch processing&lt;/strong&gt;. It provides the &lt;code&gt;streamEngineParser&lt;/code&gt; function to automatically form a pipeline of stream engines to carry out the specified metrics calculation. You can directly use the Alpha#98 function as the &lt;em&gt;metrics&lt;/em&gt;, with no need to modify the script for metrics calculation.&lt;/p&gt;

&lt;p&gt;Click the demo below to get more info! 👇&lt;br&gt;
&lt;a href="https://youtu.be/B0lYBAI_FEc" rel="noopener noreferrer"&gt;https://youtu.be/B0lYBAI_FEc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pandas</category>
      <category>hft</category>
      <category>database</category>
      <category>quant</category>
    </item>
  </channel>
</rss>
