Best Practices for Level 2 Quantitative Factor Development and Simulated Trading

In high-frequency financial trading, tick-by-tick trade data and Level 2 snapshot data contain rich temporal features and market microstructure information. They form the foundation for building quantitative factors and designing trading strategies. As market structures grow more complex and algorithmic trading continues to expand, the ability to process massive volumes of high-frequency data in real time has become a core competitive advantage for quantitative teams. High-frequency trading systems process hundreds of millions of records each day. Their workflows include preprocessing, time alignment, minute-level aggregation, factor calculation, and model inference, all of which demand extremely low latency, high reliability, and scalability.

To meet these needs, DolphinDB introduced SnapTradeStream, a real-time factor calculation module for minute-level quantitative trading. Built on high-performance streaming engines, the module handles the entire workflow, from real-time market data collection and minute-level aggregation to factor calculation, model inference, signal generation, and order matching. Its low latency and high throughput improve real-time factor calculation efficiency and system stability.

1. Generate Minute-Level Factors from Snapshot Data

Level 2 snapshot data includes ten levels of bid and ask quotes, order volumes, and real-time trade prices. It reveals market depth, order book pressure, and bid-ask imbalances. However, raw snapshot data can arrive at thousands of records per second and contain complex fields. Using it directly in strategy calculations is computationally demanding and makes it difficult to extract useful minute-level features. This chapter explains how to aggregate high-frequency snapshot data into minute-level factors in real time, covering the data characteristics, calculation rules, and implementation steps.

1.1 Characteristics of Level 2 Stock Snapshot Data

Exchanges push snapshot data at fixed intervals, providing static market information for the entire market. This data is widely used for minute-level market data archiving, factor calculation, and quantitative strategy backtesting. Snapshot data has a rich field structure. In addition to basic market data, such as the last, high, and low prices and trading volume, it includes order queues (multiple levels of bid and ask prices and their corresponding order volumes), ETF subscription and redemption data, bid and ask statistics, and the number of trades. See Appendix for the snapshot data fields. These fields can be used to calculate the following common minute-level factors:

Table 1. Table 1-1 Snapshot Fields for Minute-Level Factors
Field Name Data Type Description
securityId SYMBOL Security symbol
rseComputeUs LONG Calculation time for the reactive stateful engine (microseconds)
batchSize INT Number of records processed per batch by the reactive stateful engine
snapTseComputeUs LONG Calculation time for the time-series engine (microseconds)
tradeTime TIMESTAMP Timestamp of the current aggregation window (start of the next minute)
market SYMBOL Trading market
high DOUBLE Highest trade price
open DOUBLE Opening price
low DOUBLE Lowest trade price
close DOUBLE Closing price
volume LONG Trading volume added in the current minute
amount DOUBLE Trading value added in the current minute
numTrades LONG Number of trades added in the current minute
totalSellVolume LONG Total ask order volume across the current ten price levels
totalBuyVolume LONG Total bid order volume across the current ten price levels
sellVolume LONG Total ask order volume across ten price levels
buyVolume LONG Total bid order volume across ten price levels
entrustSellAmount DOUBLE Current total ask order value
entrustBuyAmount DOUBLE Current total bid order value
sellAvgPrice DOUBLE Current weighted average ask price
buyAvgPrice DOUBLE Current weighted average bid price

1.2 Calculation Rules for Minute-Level Factors

Converting high-frequency snapshot data into minute-level indicators reduces data dimensionality while preserving key market structure information. These indicators are important inputs for subsequent model inference and signal triggering. DolphinDB uses its time-series engine and reactive stateful engine to calculate minute-level indicators efficiently and with low latency. The following sections describe the calculation rules for prices, trades, and order book data.

1.2.1 Price Calculation Rules

Minute-level price movements provide a foundation for quantitative strategies to identify trends, assess strength, and detect turning points. For example, the closing price determines the model's prediction target, while the opening, high, and low prices affect decisions in drawdown and breakout strategies. You must also handle anomalies, such as an opening price of 0, and missing data appropriately. To ensure that strategies use consistent and reliable data, calculate minute-level price indicators as follows:

  • Opening price (open):

    • For minutes other than the opening and closing minutes, use the latest price from the first snapshot in the window as the opening price.

    • For the opening and closing minutes:

      • If the most recent opening price in the window is 0, use the last traded price from the first snapshot in the window.

      • Otherwise, use the most recent opening price in the window.

  • High price (high) and low price (low):

    • If the high price (or low price) in the last record of the window is 0, use the maximum (or minimum) last traded price in the window.

    • Otherwise, use the most recent high price (or low price) in the window.

  • Closing price (close):

    • Use the latest price from the last record of the current window as the closing price.

1.2.2 Handle Trading Volume, Trading Value, and Number of Trades

Trading indicators reflect market activity and are key indicators for distinguishing a valid breakout from a false rally. For example, rising trading volume often accompanies a continuing trend, while declining volume may signal a pullback. For intraday timing strategies in particular, real-time changes in trading volume often determine whether to execute a signal. Because snapshot trading volume is cumulative, calculate the actual change for each minute using differences between successive values:

  • Initially, use the values in the most recent record in the window for snapshot trading volume, trading value, and number of trades.

  • Use the deltas() function to calculate the differences between adjacent minutes for each field. If a difference is null, retain the value from the most recent record in the window; otherwise, use the calculated difference.

  • The fields include:

    • volume: the increase in trading volume.

    • amount: the increase in trading value.

    • numTrades: the increase in the number of trades.

1.2.3 Order Volume and Average Price Calculation Rules

One key benefit of snapshot data is that it provides order book information for the top 10 price levels, revealing the market's current supply and demand. Quantitative strategies focus on changes in order volume. For example, they may use order book volume-price imbalances to identify potential accumulation through sell pressure or distribution after driving prices up. Minute-level order statistics therefore provide an important window into the behavior of major market participants. To ensure that strategies capture the latest order book state at the end of each minute, process order book data using the following indicators:

  • When calculating indicators related to buy and sell order volume and value, use the most recent snapshot record in the current calculation window for every indicator:

    • totalSellVolume: total sell order volume, taken from the latest value of the totalOfferQuant field in the window.

    • totalBuyVolume: total buy order volume, taken from the latest value of the totalBuyQuant field in the window.

    • entrustSellAmount: total value of sell orders, calculated by multiplying the sell order volume by the weighted average sell price. Use the latest record in the window.

    • entrustBuyAmount: total value of buy orders, calculated in the same way as entrustSellAmount by multiplying the buy order volume by the weighted average buy price.

    • sellAvgPrice and buyAvgPrice: the weighted average sell and buy prices.

1.3 Calculate Minute-Level Factors

Building minute-level factors depends not only on precise calculation rules but also on high-quality streaming data processing and windowing. In live trading, raw snapshot data presents challenges such as complex trading sessions, inconsistent timestamps, and uneven data distribution. These issues can affect the accuracy and timeliness of minute-level calculations. To ensure stable, timely output of minute-level factors, DolphinDB uses snapshot preprocessing, time-series aggregation, and a reactive stateful engine to build a high-performance minute-level data pipeline. The following sections describe how to efficiently generate minute-level factor data from raw snapshots in three stages.

1.3.1 Clean Snapshot Data and Standardize Time

Snapshot data includes data from opening auction sessions, continuous trading sessions, irregular timestamps, and multiple markets. Without cleaning, these can significantly compromise the accuracy of minute-level factors. In particular, nonstandard timestamps near minute-bar trigger points can cause aggregation errors and missing minute-level data. Therefore, you need to clean and format the timestamps in raw snapshots to ensure that each record falls into the correct aggregation window. We use the following preprocessing logic to filter snapshot data and standardize its timestamps:

  • Filter data to trading sessions: Retain snapshots from the opening auction session (09:25–09:30) and the continuous trading sessions (09:30–11:30 and 13:00–14:57). Discard invalid data and data from outside trading hours.

  • Optimize minute-bar trigger timing: The latest snapshot for each minute arrives at second 59. To account for this, the calculation is triggered as soon as the snapshot for second 59 arrives.

The following code was developed for DolphinDB 3.00.4. We recommend using version 3.00.4 or later.

// tse: time-series engine
// msg: raw snapshot data
def handleRawSnapshot(mutable tse, mutable msg){
    msg = select * from msg
        where ((second(tradeTime) between 09:25:00:11:30:59) || (second(tradeTime) between 12:59:00:14:56:59))
    if (size(msg) == 0) return
    update msg set tradeTime =
        case
            when minute(tradeTime) in 09:25m..09:29m then concatDateTime(date(tradeTime), 09:30:00.000)
            else tradeTime
        end

    msg1 = select * from msg where second(tradeTime) % 60 >= 59
    update msg set tradeTime = timestamp(concatDateTime(date(tradeTime), second(minute(tradeTime)))) + 58 * 1000 + 999 where second(tradeTime) % 60 >= 59
    update msg1 set tradeTime = timestamp(concatDateTime(date(tradeTime), second(minute(tradeTime)))) + 59 * 1000 + 1
    append!(msg, msg1)
    msg.sortBy!(`tradeTime)
    append!(tse, msg)
}

1.3.2 Generate Minute-Level Data with the Time-Series Engine

After cleaning, snapshot data must be efficiently aggregated in one-minute windows to produce standardized minute-level market data. In quantitative trading, snapshot counts vary widely across stocks, so traditional aggregation methods can easily omit, miscount, or double-count data. DolphinDB's time-series engine instead ensures that each one-minute window is processed as soon as all its data arrives. The processing steps are as follows:

  • Set the window and step: Set both the window size and step to 60 seconds (1 minute) so that each minute bar is independent and non-overlapping.

  • Force-trigger mechanism: For one-minute windows containing only a small number of snapshots, set the forceTriggerTime parameter to force output of the minute bar.

tse = createTimeSeriesEngine(
    name= tseName,
    windowSize=60000,
    step=60000,
    metrics=tseMetrics,
    dummyTable=rawStream,
    outputTable=rse,
    timeColumn=`tradeTime,
    useSystemTime=false,
    keyColumn=`securityID,
    useWindowStartTime=false,
    parallelism=parallelTSE,
    lowLatency = ctx["lowLatency"],
    forceTriggerTime=100,
    outputElapsedMicroseconds = true
)

1.3.3 Generate Minute-Level Factors with the Reactive Stateful Engine

Although minute-level data provides a standardized market summary, it cannot be used directly to make strategy decisions. It must first be transformed into features that strategies can use, such as behavior of major market participants, order book strength, and trading deviations. Generating these factors requires stateful variables, sliding windows, and historical state, and involves complex calculations that must be completed with millisecond-level latency. To support this, DolphinDB provides a reactive stateful engine for processing minute-level factors in real time. The minute-level aggregation results from the previous step are fed into the reactive stateful engine, which generates the minute-level factors required by the strategy in real time:

rse = createReactiveStateEngine(
    name = rseName,
    metrics = rseMetrics,
    dummyTable = tseOutputDummy,
    outputTable = objByName(minStreamTbname, true),
    keyColumn = `securityId,
    parallelism = parallelRSE,
    lowLatency = ctx["lowLatency"],
    outputElapsedMicroseconds = true
)

2. Generate Minute-Level Factors from Tick-by-Tick Trades

Tick-by-tick trade data records the details of every trade in the market. It is essential for analyzing capital flows, identifying large-order activity, and understanding market microstructure. Quantitative strategies rely particularly on real-time assessments of institutional trading behavior, capital flow direction, and unusual order book activity. However, tick-by-tick data volumes are enormous, reaching hundreds of millions of records per day, and the records are irregularly spaced in time. This chapter focuses on using DolphinDB's powerful stream processing capabilities to clean, sort, and aggregate raw tick-by-tick trade data in real time. This produces accurate minute-level factors that reflect capital flows and provide data to support analysis of market sentiment and behavior of major market participants.

2.1 Characteristics of Tick-by-Tick Trade Data

Unlike snapshot data, tick-by-tick data has finer temporal granularity and records the price, quantity, direction, timestamp, and execution method of every trade. For details on the tick-by-tick trade data fields, see the Appendix. Using these fields, this article calculates the following commonly used minute-level factors:

Table 2. Table 2-1 Minute-Level Factor Fields for Tick-by-Tick Trades
Field Name Data Type Description
securityId SYMBOL Security symbol
rseComputeUs LONG Calculation time for the reactive stateful engine (microseconds)
batchSize INT Number of records processed per batch by the reactive stateful engine
tradeTseComputeUs LONG Calculation time for the time-series engine (microseconds)
tradeTime TIMESTAMP Timestamp of the current aggregation window
buyAmount DOUBLE Total trading value of all buy trades
sellAmount DOUBLE Total trading value of all sell trades
buySmallAmount DOUBLE Trading value of small buy orders over the past minute, where the trading volume is no more than 50,000 shares.
buyBigAmount DOUBLE Trading value of large buy orders over the past minute, where the trading volume is greater than 50,000 shares.
sellSmallAmount DOUBLE Trading value of small sell orders over the past minute, where the trading volume is no more than 50,000 shares.
sellBigAmount DOUBLE Trading value of large sell orders over the past minute, where the trading volume is greater than 50,000 shares.
tradeBias DOUBLE Last trade trend

2.2 Rules for Calculating Minute-Level Factors

DolphinDB uses a time-series engine and a reactive stateful engine to construct the following two types of minute-level factors from tick-by-tick trade data.

2.2.1 Trading Value-Based Factors

Quantitative strategies typically use capital flows to assess market strength and the trading direction of major market participants. Tick-by-tick data provides the most granular view of capital flows. It can be precisely decomposed into active buy and sell trading values, making it possible to determine whether major market participants are buying to support prices or selling to suppress them. In addition, analyzing how the last trade deviates from large- and small- order trades makes it possible to construct a microstructure-level behavioral bias factor. To address this requirement, the following factors measure the direction and scale of capital flows from trades executed during the current minute. The calculation logic is as follows:

  • sellAmount: trading value of active sell trades.

  • buyAmount: trading value of active buy trades.

  • TradeBias: a factor that measures whether the last trade is biased toward a large buy or sell order. It is calculated as: (lastAmount-sellMinAmount)/abs(sellMinAmount)-(lastAmount-buyMinAmount)/abs(buyMinAmount)

    • lastAmount: trading value of the last trade in the current minute.

    • buyMinAmount: minimum single-trade value among all active buy trades.

    • sellMinAmount: minimum single-trade value among all active sell trades.

2.2.2 Large- and Small-Order Decomposition Factors for Active Capital Flows

In market trading, major market participants typically execute trades through large orders, while small orders are more commonly associated with retail investors. Classifying active trades as large or small orders helps identify whether major market participants are quietly accumulating or distributing positions. In particular, when certain stocks are active, the continued appearance of large orders often indicates institutional activity and warrants close attention from the strategy. Accordingly, the following large- and small-order capital-flow factors are defined to quantify the structure of active capital flows at the minute frequency:

A single trade volume greater than 50,000 is used as the classification threshold:

  • buySmallAmount: total capital involved in active buy trades with a trading volume ≤50,000.

  • buyBigAmount: total capital involved in active buy trades with a trading volume >50,000.

  • sellSmallAmount: total capital involved in active sell trades with a trading volume ≤50,000.

  • sellBigAmount: total capital involved in active sell trades with a trading volume >50,000.

2.3 Calculate Minute-Frequency Factors

Tick-by-tick trade data is the smallest unit of order matching and records every actual trade executed in the market. Compared with snapshot data, tick-by-tick data provides a more accurate representation of actual capital flows and trading behavior. However, it also involves larger data volumes, finer time granularity, and more frequent out-of-order records. Therefore, dedicated data-processing and window-aggregation strategies must be designed before the data enters the minute-frequency aggregation workflow. The following sections describe how DolphinDB builds a real-time, tick-driven workflow for calculating minute-frequency factors through data standardization, a time-series engine, and a reactive stateful engine.

2.3.1 Trade Data Preprocessing and Standardization

Tick-by-tick trade data may contain out-of-order records, inconsistent time formats, and data from multiple markets. Aggregating the data directly can result in missing records or incorrect window assignments, affecting the accuracy and completeness of minute-frequency factors. Therefore, to assign tick-by-tick trade data to the correct minute-frequency windows and ensure that all trades are ordered correctly, the raw trade data must undergo cleansing and time standardization. The specific steps are as follows:

  1. Sort and align: First, sort the tick-by-tick trade data by tradeTime in ascending order to ensure a consistent chronological sequence. This prevents the time-series engine from discarding out-of-order records, which could compromise the correctness of minute-frequency factors.

  2. Write data: Convert the data to second-level frequency and write the data to the time-series engine for subsequent minute-frequency aggregation.

// tse: time-series engine
// msg: raw snapshot data
def handleRawTrade(mutable tse, mutable msg){
    msg.sortBy!(`tradeTime)
    // Convert the data to second-level frequency to prevent out-of-order records
    replaceColumn!(msg, `tradeTime, timestamp(datetime(msg.tradeTime)))
    tse.append!(msg)
}

2.3.2 Generate Minute-Frequency Data with the Time-Series Engine

The standardized trade data is ingested into the time-series engine, which divides it into minute-frequency windows and performs initial aggregation. Tick-by-tick trades are dense but unevenly distributed. Appropriate window settings and a forced-trigger mechanism are therefore required to ensure that every calendar minute produces an output. The time-series engine supports parallel sliding-window aggregation for multiple stocks through the parallelism parameter. It also provides trading-session controls and performance output to ensure real-time processing in live trading environments. The processing logic is as follows:

  • Window configuration: Set windowSize=60000 and step=60000. Each aggregation window is therefore 1 minute long, with a sliding step of 1 minute, producing non-overlapping calendar-minute windows.

  • Forced-trigger control: Set forceTriggerTime=100 to trigger minute-frequency calculations promptly for inactive stocks.

tse = createTimeSeriesEngine(
        name= tseName,
        windowSize=60000,
        step=60000,
        metrics=tseMetrics,
        dummyTable=rawStream,
        outputTable=rse,
        timeColumn=`tradeTime,
        useSystemTime=false,
        keyColumn=`securityID,
        useWindowStartTime=false,
        parallelism=parallelTSE,
        lowLatency = ctx["lowLatency"],
        forceTriggerTime=100,
        outputElapsedMicroseconds = true
)

2.3.3 Generate Minute-Frequency Factors with the Reactive Stateful Engine

Basic aggregated data provides only coarse-grained information such as total trading value and trade count, which cannot meet strategy requirements for features such as capital-flow structure, behavioral bias, and large-order trading. DolphinDB uses a reactive stateful engine to process minute-frequency trade data streams at a fine-grained level, construct interpretable factor features for strategies, and maintain real-time output. The minute-frequency aggregation results are fed into the stateful engine to calculate and output minute-frequency factors in real time:

rse = createReactiveStateEngine(
    name = rseName,
    metrics = rseMetrics,
    dummyTable = tseOutputDummy,
    outputTable = objByName(minStreamTbname, true),
    keyColumn = `securityId,
    parallelism = parallelRSE,
    lowLatency = ctx["lowLatency"],
    outputElapsedMicroseconds = true
)

3. Generate Derived Factors from Minute-Frequency Factors

Basic minute-frequency factors reflect the market's instantaneous state, but a single indicator cannot adequately capture complex dynamics and underlying trends. Quantitative strategies typically require derived features such as technical indicators, statistical characteristics, or multifactor combinations. Calculating these directly from raw data is inefficient and results in complex logic. In addition, calculating derived factors requires multiple heterogeneous data streams to be aligned and merged in real time, placing high demands on engine design and fault-tolerance mechanism. This chapter describes how to use DolphinDB's streaming engine to combine and calculate multiple factors based on the generated minute-frequency factor tables, producing the derived factors required by strategies in real time.

3.1 Definitions of Derived Factors

Based on minute-frequency factors derived from snapshots and tick-by-tick trades, this article calculates the following derived factors in real time:

Table 3. Table 3-1 Derived Factor Fields
Field Name Data Type Description
securityId SYMBOL Security symbol
tradeTime TIMESTAMP Trading timestamp
dealToEntrustRatio DOUBLE Trading value-to-order ratio
bigOrderVsDepth DOUBLE Large-order trade and order book depth
priceDeviation DOUBLE Trade price deviation
priceImpactEfficiency DOUBLE Price impact efficiency
bigOrderSupport DOUBLE Large-order price support
tradeMatchEfficiency DOUBLE Order-book trade matching
priceDiscoveryEfficiency DOUBLE Price discovery efficiency
liquidityConsumption DOUBLE Liquidity consumption
capitalEfficiency DOUBLE Capital efficiency
marketPressure DOUBLE Composite market pressure

3.2 Calculate Derived Factors

Snapshot-minute factors and trade-minute factors each capture a different aspect of the market. However, when designing actual strategies, information from a single source is often insufficient to support fine-grained trading decisions. For example, snapshots alone cannot fully capture the buying behavior of major market participants, while trades alone cannot adequately reflect order book pressure. Therefore, minute-level features from different sources must be combined to build higher-level derived factors. To support this process, DolphinDB provides the equi join engine to match data from multiple sources at the minute level and the reactive stateful engine to calculate complex composite features in real time. The complete derived-factor calculation pipeline is as follows:

3.2.1 Engine Architecture and Data Flow

Calculating derived factors requires both snapshot factors and trade factors, so the system must support cross-table merging, concurrent processing, and state management. The resulting derived factors must also be available to downstream inference modules in real time, which requires a consistent data structure and a clear processing path. DolphinDB therefore provides the following core components to build the stream processing pipeline:

  • Equi join engine: Matches two sources, snapshot-minute data and trade-minute data, by tradeTime and securityId to create a merged minute-level input stream.

  • Reactive stateful engine: Receives the merged streaming data output by the equi join engine and calculates derived factors from it. The engine maintains state and updates indicators by security symbol, while supporting concurrent processing.

  • Derived-factor stream table: Stores the minute-level factor stream output by the stateful engine and supports persistence, subscription, and subsequent inference for generating trading signals.

3.2.2 Merge Minute-Level Data Streams with Equi Join Engine

To enhance the expressive power of minute-level factors, minute-level data from different sources must be aligned to a common time frame. The equi join engine provides millisecond-level time alignment, ensuring that the merged data can be used directly to calculate derived factors in the downstream stateful engine. The engine connects to the minute-level factor tables generated from snapshots and trades, then merges the minute-level data tables from the two sources.

  • Data sources to be joined:

    • Left table: the minute-level factor table generated from snapshots.

    • Right table: the minute-level factor table generated from tick-by-tick trades.

  • Join conditions:

    • The matching field is securityId.

    • The time field used for alignment is tradeTime.

ejEngine = createEquiJoinEngine(
    name = "EquiJoinEngine",
    leftTable = snapMinDummy,
    rightTable = tradeMinDummy,
    outputTable = rse,
    metrics = sqlCol(colNames[2:]),
    matchingColumn = `securityId,
    timeColumn = `tradeTime
)

3.2.3 Calculate Minute-Level Derived Factors with the Reactive Stateful Engine

The merged minute-level data contains richer information and can be used to construct composite factors with greater strategic value, such as "trading value deviation from the average bid-ask price," "major-participant behavior versus order placement response," and "large-order-driven trends." These factors typically involve complex calculation logic, so the reactive stateful engine is used for efficient processing.

rse = createReactiveStateEngine(
    name = rseName,
    metrics = facMetrics,
    dummyTable = dummy,
    outputTable = objByName(factorTbname, true),
    keyColumn = `securityId,
    parallelism = parallelRSE,
    lowLatency = ctx["lowLatency"],
    outputElapsedMicroseconds = true
)

The results are ultimately written to the derived-factor stream table for real-time model inference and trading signal generation.

4. Real-Time Inference with a Model Trained on Derived Factors

Prediction is at the core of quantitative trading. After derived factors are generated, the ability to make real-time predictions and output trading signals within milliseconds is critical to the success of a live trading strategy. Traditional batch processing or high-latency inference pipelines can miss trading opportunities. This chapter explains how to perform inference with multiple models based on real-time derived factors within DolphinDB's stream processing framework and generate trading signals for subsequent execution.

4.1 Prepare the Models

In a quantitative trading system, model training and preparation are fundamental to accurate prediction. Model training extracts useful features from historical data and combines them with market microstructure information to build machine learning models. The training process includes data cleaning, feature engineering, and factor selection. It also uses model ensembles to combine information from different dimensions and generate predictions from multiple perspectives. This article prepares two demo models, one implemented in PyTorch and the other in LightGBM, for subsequent real-time inference.

4.2 Real-Time Inference Steps

Real-time inference must generate signals within milliseconds after data is produced to capture fleeting trading opportunities. This article uses DolphinDB's stream processing framework to integrate factor calculation with multi-model inference, allowing PyTorch- and LightGBM-based models to be loaded into memory for real-time inference. The main steps are as follows:

  1. Data reception and preprocessing: Receive the derived-factor data generated by stream processing, then clean and transform it to ensure compliance with each model's input requirements.

  2. Multi-model invocation and inference: Feed the preprocessed data into multiple models. Each model outputs its inference results independently; the results are then combined and pushed to the signal stream table for downstream subscribers.

5. Real-Time Simulated Trading

Figure 1. Figure 5-1 Simulated Trading Workflow

After real-time inference generates signals, simulated trading is used to validate the strategy's effectiveness and execution robustness and to identify potential risks before deploying actual capital. This chapter explains how to build a real-time simulated trading system with DolphinDB, reproduce market data and order matching, and perform closed-loop validation from signal generation through order execution.

Note: Real-time simulated trading depends on the MatchingEngineSimulator and OrderManagementEngine plugins, which must be installed and loaded in advance.

5.1 Steps for Building a Simulated Trading System

A simulated trading system reproduces the order matching and execution process in a real market environment, allowing you to evaluate strategy performance and execution efficiency under controlled risk. The specific steps are as follows:

  1. Configure trading parameters and create the order matching simulator: Configure the instruments, time intervals, matching rules, and risk control parameters, then initialize the order matching simulator. The simulator supports order book management and price-time priority matching.

  2. Subscribe to real-time snapshot market data: Subscribe to the raw snapshot stream table to obtain the latest prices and bid and ask order book depth, normalize the data, and then feed it into the order matching simulator.

  3. Generate signals from derived factors and submit orders: Subscribe to the trading signals produced by derived-factor inference, generate orders based on the strategy logic, and push them to the order matching simulator in real time for dynamic matching.

  4. Perform real-time order matching and generate execution reports: The order matching simulator continuously matches buy and sell orders in real time according to price-time priority, generates execution details, and outputs the results to a stream table, forming a closed loop from signals to executions.

5.2 Order Matching Simulator and Order Management

In the real-time simulated trading system based on DolphinDB, order management has been introduced alongside the order matching simulator, which reproduces the market order matching process. The following sections describe the role and benefits of order management in the order matching simulator.

5.2.1 Time-Window-Controlled Trade Execution

The order management feature allows you to specify a start time (effectiveTime) and end time (expireTime) for each order. The order can be matched and executed only during this interval. When the end time is reached, any unfilled or partially filled order triggers a cancellation.

  • Timely trade execution: Prevents orders from remaining open indefinitely and avoids exposing orders to risk after market conditions and the strategy's intent have changed.

  • Realistic simulation of the trading environment: In live trading, many orders have a specified validity period. Order management enables simulated trading to reflect these real-world constraints more accurately.

  • Improved simulation accuracy and risk control: Removes expired orders promptly, effectively controls trading risks, and prevents expired orders from causing erroneous judgments.

5.2.2 Partial Fills and Expiration Handling

In a simulated environment, a partially filled order may have an unfilled remainder. The order management feature is responsible for the following:

  • Automatically updates the status of the remaining order at the specified end time, preventing residual orders from interfering with subsequent strategy evolution.

  • Outputs complete execution and order status information to ensure the completeness and accuracy of the simulated data.

5.2.3 Applicable Scenarios

  • Orders in quantitative trading strategies often have extremely short lifecycles, making precise simulation of order validity periods essential.

  • Institutional strategies with strict capital management and risk control requirements need to closely monitor order execution time limits and fill status.

  • In market-making strategies, proactive order cancellation and validity-period control help ensure smooth order matching and real-time feedback.

  • Strategy testing and backtesting require detailed tracking and analysis of order-matching timeliness and partial-fill behavior.

6. Real-Time Factor Generation, Subsequent Inference, and Order Matching Simulation

Building a real-time factor calculation and prediction pipeline involves multiple engines, stream table subscriptions, data merging, and model invocation. Manual configuration is cumbersome and error-prone, making it difficult to meet the robustness and scalability requirements of production environments. This chapter introduces the SnapTradeStream module, which is developed based on the DolphinDB module mechanism. The module implements the complete workflow from minute-level factor calculation and derived-factor generation to model inference and order matching simulation. It supports rapid deployment, flexible configuration, and historical data replay, reducing the O&M costs of building a real-time quantitative analytics platform.

The following figure shows the overall architecture of the real-time stream processing workflow:

Figure 2. Figure 6-1 Real-Time Stream Processing Workflow

6.1 Stream Processing Framework and Features

The file structure of the SnapTradeStream module is as follows:

SnapTradeStream/
├── CreateTable.dos
├── OHLCFactors.dos
├── OrcaFrame.dos
├── StreamFrame.dos
├── StreamUtils.dos
└── utils.dos

The feature of each file is as follows:

Table 4. Table 6-1 SnapTradeStream Module File Descriptions
File Name Description
utils.dos Defines general utility functions used by the module.
StreamUtils.dos Defines utility functions shared by traditional stream processing and Orca.
CreateTable.dos Defines functions for creating the required database tables.
OHLCFactors.dos Defines the field construction logic and factor expression functions for minute-level market data factors.
StreamFrame.dos Defines the workflow functions for processing raw data into derived metrics, inference results, and simulated trades.
OrcaFrame.dos Defines workflow functions in the Orca platform for processing raw data into derived metrics, inference results, and simulated trades.
Note:
  • To use Orca, upgrade the DolphinDB server to version 3.00.3 or later.

  • In cluster mode, deploy a compute group. The Orca script runs on one of the group's compute nodes.

6.2 Steps for Building the Stream Processing Framework

A real-time processing system depends on a stable, low-latency end-to-end framework, especially during periods of sharp market volatility. This section describes the core workflow SnapTradeStream uses to build a real-time factor calculation framework. It covers the entire pipeline, from raw data through minute-frequency aggregation, multi-source joins, and derived factor calculation to inference and order matching simulation. The workflow is summarized as follows:

  1. Minute-frequency factor processing: Sends tick-by-tick trades and snapshot data to a time-series engine (60-second rolling windows based on tradeTime) and a reactive stateful engine, respectively. The engines output standardized minute-frequency factor stream tables, with the data structurally aligned and supplemented with additional fields.

  2. Derived factor calculation: Uses the equi join engine to join the minute-frequency snapshot and trade data on securityId and tradeTime. Sends the result to a reactive stateful engine to generate derived factors and write them to a stream table.

  3. Inference and simulated trading: Subscribes to the derived factor stream table and passes its data to an inference model to generate trading signals. Uses these signals to create orders and submit them to the order matching simulator, which outputs execution details in real time.

Figure 3. Figure 6-2 Module Workflow

6.3 Apply the Stream Processing Framework

This section shows how to build the stream processing framework described in the preceding sections from scratch using the SnapTradeStream module. See the attached files for the complete code.

6.3.1 Import Tick-by-Tick Trade and Snapshot Data

The attached files provide raw stock snapshot and trade data for a single day to demonstrate real-time factor generation and simulated trading.

//Import the module
clearCachedModules()
use SnapTradeStream::CreateTable
use SnapTradeStream::utils
use SnapTradeStream::OHLCFactors
use SnapTradeStream::StreamFrame

// Import the data
// Update the path
colNames, colTypes = getTradeTickColDefs()
tradeData = select * from loadText("/data/trade.csv", ,table(colNames, colTypes)) order by tradeTime

colNames, colTypes = getSnapshotColDefs()
snapshotData = select * from loadText("/data/snapshot.csv", ,table(colNames, colTypes)) order by tradeTime

6.3.2 Create Stream Tables

After importing the day's tick-by-tick trade and snapshot data, create stream tables to receive data in real time and enable downstream stream processing.

tradeStreamName = "tradeStream"
unsubAndDropAll(tradeStreamName)
sche = schema(tradeData).colDefs
colNames, colTypes = sche.name, sche.typeString
try{
    share(streamTable(100:0, colNames, colTypes), tradeStreamName)
}catch(ex){print(ex)}

snapStreamName = "snapshotStream"
unsubAndDropAll(snapStreamName)
sche = schema(snapshotData).colDefs
colNames, colTypes = sche.name, sche.typeString
try{
    share(streamTable(100:0, colNames, colTypes), snapStreamName)
}catch(ex){print(ex)}

6.3.3 Configure Factor Processing Functions and Other Parameters

To support minute-frequency feature extraction across different data sources, the system uses a unified function mapping dictionary, factorFuncDict, and a parallelism configuration dictionary, parallelDict. This design enables modularity and flexible scheduling.

factorFuncDict = {
    "tradeTSE": getTrade1MTSFactor, // Function for retrieving time-series engine metrics from trade data
    "tradeRSE": getTrade1MReactiveFactor, // Function for retrieving reactive stateful engine metrics from trade data
    "snapTSE": getSnap1MTSFactor, // Function for retrieving time-series engine metrics from snapshot data
    "snapRSE": getSnap1MReactiveFactor, // Function for retrieving reactive stateful engine metrics from snapshot data
    "mergedRSE": minDerivedKlineFactors // Function for retrieving reactive stateful engine metrics from derived data
}

// Define the parallelism parameter dictionary
parallelDict = {
    "tradeTSE": 5,
    "tradeRSE": 5,
    "snapTSE": 5,
    "snapRSE": 5,
    "mergedRSE": 5
}

// Define the model path
extraParams = {
    "torchModelPath": "/data/pytorch_model.pt",
    "lgbmModelPath": "/data/lightgbm_model.txt",
    "lowLatency": false
}
  • parallelDict specifies the number of parallel threads for each type of factor calculation task. Each task type is currently configured with 5 parallel tasks. You can adjust this setting based on the available node resources and data volume.

  • factorFuncDict is a dictionary whose keys are strings and whose values are functions. It specifies the metrics and field definitions for each streaming engine. These functions are defined in the OHLCFactors module by default. For example, the getTrade1MTSFactor function is defined as follows:

// Function for retrieving time-series engine metrics and field definitions from trade data
def getTrade1MTSFactor(){
    d = dict(STRING, ANY, true)
    d["getAmounts(tradeBSFlag, tradePrice, tradeQty, `B)"] = ["buyAmounts", "DOUBLE"]
    d["getAmounts(tradeBSFlag, tradePrice, tradeQty, `S)"] = ["sellAmounts", "DOUBLE"]
    colNames2 = ["buySmallAmount", "buyBigAmount", "sellSmallAmount", "sellBigAmount"]
    colTypes2 = take("DOUBLE", size(colNames2))
    d["calCapitalFlow(buyApplSeqNum,offerApplSeqNum, tradeBSFlag, tradeQty, tradePrice)"] = [colNames2, colTypes2]
    d["lastTradeBias(tradeTime, tradeBSFlag, tradePrice, tradeQty)"] = ["tradeBias", "DOUBLE"]
    return d
}

The function returns a dictionary whose keys are function-call strings and whose values are two-element lists. The first element is the metric name, and the second is the field type. This design lets you adjust the configuration to meet your business needs. When factor calculation logic changes, you only need to update it here. You do not need to update underlying components such as stream tables and streaming engines, which significantly reduces factor maintenance and update costs while improving system extensibility and development efficiency.

6.3.4 Build the End-to-End Calculation Framework

After defining the required parameters, call the main function buildStreamFrame to subscribe to the data and calculate minute-frequency factors in real time.

factorDbname = "dfs://factor"
factorTbname = "minDerivedFactorsTb"
buildStreamFrame(factorDbname, factorTbname, tradeStreamName, snapStreamName, factorFuncDict, parallelDict, extraParams)

buildStreamFrame takes seven parameters:

Table 5. Table 6-2 buildStreamFrame parameters
Parameter Description
factorDbname Name of the database for storing factors
factorTbname Name of table for storing factors
tradeStreamName Name of tick-by-tick trade stream table
snapStreamName Name of snapshot data stream table
factorFuncDict Dictionary of factor-processing functions
parallelDict Dictionary of parallel task counts
extraParams Additional parameters, including the model path and low-latency settings

6.3.5 Insert Data

Replay historical data into the stream tables to simulate the arrival of real-time streaming data and trigger the end-to-end streaming calculation workflow.

replay(
    [tradeData, snapshotData],
    [objByName(tradeStreamName, true), objByName(snapStreamName, true)],
    `tradeTime,
    `tradeTime,
    100,
    false
)

6.3.6 Run Inference and Simulated Order Matching

buildStreamFrame creates the pipeline for calculating minute-level factors. After the replay finishes, wait for data to appear in the derived factor table, then start model inference and simulated order matching:

maxWaitMs = 60000
waitedMs = 0
do {
    sleep(500)
    waitedMs += 500
} while (size(objByName(factorTbname, true)) == 0 && waitedMs < maxWaitMs)

use SnapTradeStream::StreamUtils
derived2Simulator(
    factorTbname,
    extraParams["lgbmModelPath"],
    extraParams["torchModelPath"],
    snapStreamName,
    false
)

6.3.7 Results

Real-time simulated trade table

Figure 4. Figure 6-3 Simulated Trade Details

The following table lists key fields in the real-time simulated trade details:

Table 6. Table 6-3 Simulated Trade Detail Fields
Field Name Description
OrderID ID of the order that was filled
Symbol Stock symbol
Direction

Buy/sell direction:

  • 1: buy

  • 2: sell

LimitPrice Order price
VolumeTotalOriginal Order quantity
TradeTime Order matching time
TradePrice Trade price
VolumeTraded Trading volume
OrderStatus

Order status:

  • 0: partially filled

  • 1: fully filled

  • 2: canceled

effectiveTime Order start time
expireTime Order expiration time
cumTradeQty Cumulative trading volume
tradeAvgPrice Average trade price

6.3.8 Clean Up the Environment

Run the following code to clean up the streaming environment:

clearStreamEnv()
each(undef{, SHARED}, [tradeStreamName, snapStreamName])

6.4 Real-Time Low-Latency Calculation

Starting with DolphinDB version 3.00.4, you can specify the lowLatency parameter for the time-series engine and reactive stateful engine. When set to true, the engines use a row-based low-latency algorithm, suited to workloads that require extremely low latency and write small amounts of data at a time. This article covers only the DolphinDB implementation. For even lower latency, use the low-latency calculation features in Swordfish. The following example shows how to create a time-series engine:

tse = createTimeSeriesEngine(
        name= tseName,
        windowSize=60000,
        step=60000,
        metrics=tseMetrics,
        dummyTable=rawStream,
        outputTable=rse,
        timeColumn=`tradeTime,
        useSystemTime=false,
        keyColumn=`securityID,
        useWindowStartTime=false,
        parallelism=parallelTSE,
        lowLatency = true, // Add this parameter
        outputElapsedMicroseconds = true
)

For the modules described in this article, use the following configuration:

extraParams = {
    "torchModelPath": "/data/pytorch_model.pt",
    "lgbmModelPath": "/data/lightgbm_model.txt",
    "lowLatency": true
}

Enable low-latency mode for all reactive stateful engines and time-series engines in the stream processing framework. This section simulates a real-time calculation workload and measures the calculation time of streaming engines processing different data volumes, with the lowLatency parameter disabled and enabled.

Test Results

Using a full day of real snapshot and trade data for 100, 200, 500, and 1,000 stocks, we measured the average calculation time, in microseconds, for the daily time-series engine (TSE) and the reactive stateful engine (RSE), with the lowLatency parameter disabled and enabled. The value in parentheses is the ratio of the calculation time with the lowLatency parameter enabled to the time with it disabled.

Table 7. Table 6-4 Low-Latency Performance Test Results
SampleCounts Snapshot Trade DerivedFactors
TSE RSE TSE RSE RSE
FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE
100 59.13 39.28 (0.66) 11.72 9.97 (0.85) 60.67 53.01 (0.87) 2.11 0.79 (0.37) 151.51 57.48 (0.38)
200 61.46 40.11 (0.65) 12.09 9.66 (0.80) 62.46 59.80 (0.96) 2.07 0.93 (0.45) 185.12 59.24 (0.32)
500 64.47 42.27 (0.66) 12.46 9.87 (0.79) 63.83 57.75 (0.90) 2.16 1.05 (0.49) 317.54 72.69 (0.23)
1,000 66.13 44.06 (0.67) 12.29 9.84 (0.80) 67.25 57.76 (0.86) 2.19 1.13 (0.52) 1,587.00 94.72 (0.06)

When the lowLatency parameter is enabled, all streaming engines have significantly lower computation times than when it is disabled.

7. Orca

As requirements for large-scale cluster deployment, complex task dependencies, and high availability continue to grow, manually managing multiple engines and subscription relationships has become increasingly complex. DolphinDB's Orca, a real-time computing platform, uses declarative APIs and centralized intelligent scheduling to simplify the entire design, deployment, and O&M lifecycle of stream processing workflows, improving system stability and resource utilization. The SnapTradeStream module uses Orca to implement the real-time factor calculation and simulated trading workflows described in Chapter 6, with a clearer and more systematic code structure.

The following figure shows the overall architecture of the Orca workflow:

Figure 5. Figure 7-1 Orca Workflow

As shown in the figure, Orca uses cluster computing resources to generate minute-level derived factors in real time from raw trade and snapshot data. Execution nodes subscribe to factor data, perform real-time inference, and output trading signals. These signals are used to construct orders, which are sent to the order matching engine, which ultimately outputs execution details.

7.1 Best Practice on Orca

Traditional stream processing requires you to manually complete tasks such as data ingestion, engine configuration, factor function registration, and model loading, making the process cumbersome. Orca uses a unified stream graph to encapsulate factor calculation as a declarative workflow. Model inference and simulated trading are executed on one node, improving development efficiency. This chapter uses Orca to build the system described above.

7.1.1 Define Parameters

The following code initializes the Orca minute-level factor framework. It imports sample trade and snapshot data, creates stream tables with the same schemas, and configures the factor functions, parallelism, model path, and result storage location.

use SnapTradeStream::CreateTable
use SnapTradeStream::utils
use SnapTradeStream::OHLCFactors
use SnapTradeStream::OrcaFrame

// Import the data
colNames, colTypes = getTradeTickColDefs()
tradeData = select * from loadText("/data/trade.csv", ,table(colNames, colTypes)) order by tradeTime

colNames, colTypes = getSnapshotColDefs()
snapshotData = select * from loadText("/data/snapshot.csv", ,table(colNames, colTypes)) order by tradeTime


// Create stream tables
tradeStreamName = "tradeStreamOrca"
unsubAndDropAll(tradeStreamName)
sche = schema(tradeData).colDefs
colNames, colTypes = sche.name, sche.typeString
try{
    share(streamTable(100:0, colNames, colTypes), tradeStreamName)
}catch(ex){print(ex)}

snapStreamName = "snapshotStreamOrca"
unsubAndDropAll(snapStreamName)
sche = schema(snapshotData).colDefs
colNames, colTypes = sche.name, sche.typeString
try{
    share(streamTable(100:0, colNames, colTypes), snapStreamName)
}catch(ex){print(ex)}

// Define the factor function dictionary
factorFuncDict = {
    "tradeTSE": getTrade1MTSFactor, // Function for retrieving time-series engine metrics from trade data
    "tradeRSE": getTrade1MReactiveFactor, // Function for retrieving reactive stateful engine metrics from trade data
    "snapTSE": getSnap1MTSFactor, // Function for retrieving time-series engine metrics from snapshot data
    "snapRSE": getSnap1MReactiveFactor, // Function for retrieving reactive stateful engine metrics from snapshot data
    "mergedRSE": minDerivedKlineFactors // Function for retrieving reactive stateful engine metrics from derived data
}

// Define the parallelism parameter dictionary
parallelDict = {
    "tradeTSE": 5,
    "tradeRSE": 5,
    "snapTSE": 5,
    "snapRSE": 5,
    "mergedRSE": 5
}

// Define the model path
extraParams = {
    "torchModelPath": "/data/pytorch_model.pt",
    "lgbmModelPath": "/data/lightgbm_model.txt"
}

// Define the persistent database and table path
factorDbname = "dfs://factor"
factorTbname = "minDerivedFactorsTbOrca"
Table 8. Table 7-1 buildOrcaFrame Parameters
Parameter Description Note
factorDbname Name of the database for storing factors Example: dfs://factor
factorTbname Name of table for storing factors Example: minDerivedFactorsTbOrca
tradeStreamName Name of tick-by-tick trade stream table Example: tradeStreamOrca. The table must have the same schema as the imported trade data.
snapStreamName Name of snapshot data stream table Example: snapshotStreamOrca. The table must have the same schema as the imported snapshot data.
factorFuncDict Dictionary of factor-processing functions Each key is a different name, and each value is the corresponding factor definition function. See the table below for details.
parallelDict Dictionary of parallel task counts The keys correspond to those in factorFuncDict, and the values specify the number of parallel tasks for each stage
extraParams Additional parameters, including the model path and low-latency settings Extended settings, such as the inference model path. See the table below for details.

The keys in factorFuncDict are defined as follows:

Table 9. Table 7-2 factorFuncDict Parameters
Parameter Description Note
tradeTSE Time-series trade factor function Corresponds to getTrade1MTSFactor and calculates 1-minute time-series metrics from trade data.
tradeRSE Reactive stateful trade factor function Corresponds to getTrade1MReactiveFactor and calculates reactive stateful metrics from trade data.
snapTSE Time-series snapshot factor function Corresponds to getSnap1MTSFactor and calculates 1-minute time-series metrics from snapshot data.
snapRSE Reactive stateful snapshot factor function Corresponds to getSnap1MReactiveFactor and calculates reactive stateful metrics from snapshot data.
mergedRSE Derived factor function Corresponds to minDerivedKlineFactors and calculates derived OHLC bar factors from the merged results.

The keys in parallelDict are defined as follows:

Table 10. Table 7-3 parallelDict Parameters
Parameter Description Note
tradeTSE Time-series trade parallelism Example: 5. Concurrently calculates 1-minute time-series trade factors, grouped by instrument.
tradeRSE Reactive stateful trade parallelism Example: 5. Concurrently calculates reactive stateful trade factors, grouped by instrument
snapTSE Time-series snapshot parallelism Example: 5. Concurrently calculates 1-minute time-series snapshot factors, grouped by instrument.
snapRSE Reactive stateful snapshot parallelism Example: 5. Concurrently calculates reactive stateful snapshot factors, grouped by instrument.
mergedRSE Derived factor parallelism Example: 5. Concurrently calculates derived OHLC bar factors, grouped by instrument.

The keys in extraParams are described below:

Table 11. Table 7-4 extraParams Parameters
Parameter Description Note
torchModelPath PyTorch model path Example: /data/pytorch_model.pt, which downstream inference loads.
lgbmModelPath LightGBM model path Example: /data/lightgbm_model.txt, which downstream inference loads.

7.1.2 Create the Framework

Call the buildOrcaFrame function in the module to set up the Orca stream processing framework. For parameter details, see Section 6.3.4. The function performs the following steps:

  1. Clean up the existing environment, including stream graphs, stream tables, streaming engines, and related subscriptions, to prevent leftover data from interfering with processing.

  2. Create a stream processing framework through the Orca API to generate minute-level derived factors from raw snapshot and trade data.

  3. Subscribe to the minute-level derived factor data and use a model to perform real-time inference and generate trading signals. Then construct orders and insert them into the order matching simulator to produce real-time simulated trading results.

buildOrcaFrame(factorDbname, factorTbname, tradeStreamName, snapStreamName, factorFuncDict, parallelDict, extraParams)

7.1.3 Replay the Data

Replay historical data to run the full Orca stream processing and simulated trading workflow and output simulated trading results in real time.

replay(
    [tradeData, snapshotData],
    [objByName(tradeStreamName, true), objByName(snapStreamName, true)],
    `tradeTime,
    `tradeTime,
    100,
    false
)

7.1.4 Clean Up the Environment

Run the following code to clean up the Orca environment.

clearOrcaEnv()
each(undef{, SHARED}, [tradeStreamName, snapStreamName])

7.2 Summary of Orca's Advantages

This example calculates minute-level indicators from raw trade data using Orca instead of a traditional stream processing workflow. The workflow starts with raw data input, passes through a reactive stateful engine and a time-series engine, and ends by writing the results to a persistent table. Unlike traditional stream processing, Orca lets you declare the required steps using chained functions. You do not need to define table schemas, shared tables, or other configurations unrelated to your business logic. This greatly simplifies development and improves efficiency and code readability:

tradePipeline = g.source(tradeStreamName, 1:0, colNames1, colTypes1)
                .dailyTimeSeriesEngine(
                    windowSize = 60000,
                    step = 60000,
                    metrics = tradetseMetrics,
                    timeColumn = `tradeTime,
                    useSystemTime=false,
                    keyColumn = `securityID,
                    useWindowStartTime=false,
                    parallelism=tradeparallelTSE,
                    sessionBegin=[09:30:00.000, 12:59:00.000],
                    sessionEnd=[11:30:00.000, 14:57:00.000]
                )
                .reactiveStateEngine(
                    metrics = traderseMetrics,
                    keyColumn = `securityId,
                    parallelism = tradeparallelRSE
                )
                .sink(factorDbname+"/"+tradeMinStreamTbname)

In summary, Orca replaces manually built distributed factor computation workflows with a declarative API and centralized, intelligent scheduling. Orca unifies and simplifies the entire data processing workflow, from ingesting raw market data and aggregating it at the minute level to computing complex factors. It significantly improves development efficiency and system reliability, reduces O&M complexity, and helps ensure efficient resource utilization and business continuity. These advantages make Orca a powerful platform-level foundation for efficient, stable real-time factor computation in quantitative applications.

8. Summary

This tutorial explains how to use Level 2 stock snapshot and trade data in DolphinDB to generate minute-level and derived factors in real time, and then perform inference based on those factors. It demonstrates end-to-end solutions for real-time minute-level and derived factor computation, inference, and simulated trading using both the traditional low-latency stream processing framework and the Orca framework. Comparing the workflows and characteristics of these two approaches helps you understand their respective use cases and advantages, so you can choose a real-time computing solution that meets your needs.

Note that the rules for generating minute-level and derived factors described in this tutorial may differ from those used in your actual applications. You can adapt the rules to your needs to accelerate project development.

Appendix

Version Requirements

  • The code in this tutorial was developed using DolphinDB 3.00.4. We recommend using version 3.00.4 or later.

  • This tutorial uses several plugins. Install and load them before you begin:

    • lgbm

    • LibTorch

    • MatchingEngineSimulator

    • OrderManagementEngine

Schema of Raw Snapshot Data Table

Field Data Type Description
Market SYMBOL Market symbol
tradeTime TIMESTAMP Trade time
MDStreamID INT Market data type
securityID SYMBOL Security symbol
numImageStatus INT Snapshot image status
securityIDSource INT Source of security symbol
tradingPhaseCode SYMBOL Trading phase
preCloPrice DOUBLE Previous closing price
numTrades INT Number of trades
totalVolumeTrade LONG Total trading volume
totalValueTrade DOUBLE Total trading value
lastPrice DOUBLE Latest traded price
openPrice DOUBLE Opening price
highPrice DOUBLE High price
lowPrice DOUBLE Low price
closePrice DOUBLE Closing price
difPrice1 DOUBLE Price change 1
difPrice2 DOUBLE Price change 2
PE1 DOUBLE Price-to-earnings ratio 1
PE2 DOUBLE Price-to-earnings ratio 2
preCloseIOPV DOUBLE Previous close IOPV
IOPV DOUBLE IOPV
totalBuyQty LONG Total buy order volume
weightedAvgBuyPx DOUBLE Weighted best bid price
totalOfferQty LONG Total sell order volume
weightedAvgOfferPx DOUBLE Weighted best ask price
upLimitPx DOUBLE Limit up price
downLimitPx DOUBLE Limit down price
openInt LONG Open interest
optPremiumRatio DOUBLE Option premium ratio
offerPrice DOUBLE[] Ask prices, levels 1–10
buyPrice DOUBLE[] Bid prices, levels 1–10
offerOrderQty LONG[] Ask volumes, levels 1–10
buyOrderQty LONG[] Bid volumes, levels 1–10
buyNumOrders LONG[] Bid order counts, levels 1–10
offerNumOrders LONG[] Ask order counts, levels 1–10
ETFBuyNumber INT Number of ETF creation orders
ETFBuyAmount LONG ETF creation units
ETFBuyMoney DOUBLE ETF creation amount
ETFSellNumber INT Number of ETF redemption orders
ETFSellAmount LONG Number of ETF redemption shares
ETFSellMoney DOUBLE ETF redemption amount
yieldToMatu DOUBLE Yield to maturity
withdrawBuyNumber INT Number of canceled buy orders
withdrawBuyAmount LONG Canceled buy order volume
withdrawBuyMoney DOUBLE Canceled buy amount
withdrawSellNumber INT Number of canceled sell orders
withdrawSellAmount LONG Canceled sell order volume
withdrawSellMoney DOUBLE Canceled sell amount
totalBuyNumber INT Total number of buy orders
totalOfferNumber INT Total number of sell orders
maxBuyDur INT Longest resting duration for buy orders
maxSellDur INT Longest resting duration for sell orders
buyNum INT Number of bid levels
sellNum INT Number of ask levels
localTime TIME Local timestamp (data collector's machine time)
seqNo INT Local sequence number
offerOrders LONG[] Sell order queue
buyOrders LONG[] Buy order queue

Schema of Raw Trade Data Table

Field Data Type Description
channelNo INT Channel symbol
applSeqNum LONG Message sequence number
MDStreamID INT Market data type
buyApplSeqNum INT Buyer order sequence number
offerApplSeqNum INT Seller order sequence number
securityID SYMBOL Security symbol
securityIDSource INT Source of security symbol
tradePrice DOUBLE Trade price (yuan)
tradeQty LONG Trading volume (shares)
execType INT Trade type
tradeTime TIMESTAMP Execution time
localTime TIME Local collection time
seqNo INT Local sequence number
tradeBSFlag SYMBOL Trade direction (B: active buy, S: active sell)
market SYMBOL Market symbol
dataStatus INT Data status
tradeIndex INT Trade index
tradeMoney DOUBLE Trading value (yuan)
bizIndex LONG Business index