9. Design Intent and Unique Advantages
This chapter focuses on qteasy’s design intent and unique advantages: why it targets high-speed vectorized backtesting yet abandons “full-timeline vectorization”, how it balances speed and prevention of look-ahead bias through “vectorization + data isolation”, and how configurable options such as use_latest_data_cycle affect long-horizon backtests. It echoes Overall Architecture and Design Approach and Data in Strategies.
9.1. 1. 为何不做全时间轴向量化
1.1 Future Function Risk
If you compute indicators once over the entire timeline (e.g., an all-history EMA), the resulting historical series is not consistent with the result of “computing segment by segment using only the historical window visible at the time”: an all-axis EMA will use future prices, which cannot be reproduced in live trading, causing divergence between backtests and live trading. Therefore, qteasy does not compute indicators “in one shot for the whole segment” along the time dimension; instead, it provides the strategy, step by step, only the data window visible at the current step.
1.2 States and Rules Cannot Be Expressed
If fully vectorized along the time dimension, it becomes difficult to implement correctly:
constraints such as T+1 settlement, trading fees, and minimum order quantity (MOQ);
A modeling choice of whether to allow using the latest data “at the time of trading” (the current candlestick/K-line).
These details can produce multiplied differences in returns in long-horizon backtests (e.g., in a ten-year backtest, the tiny difference of “whether to use the current bar’s K-line data” can significantly change cumulative returns). qteasy makes “whether to use current-bar data” an explicit use_latest_data_cycle configuration option rather than an implicit assumption.
9.2. 2. 事件驱动的替代与取舍
A common approach is event-driven: for each trading event, fetch data per instrument and compute indicators tick by tick, thereby completely avoiding look-ahead bias and staying close to live trading, but it is slow. qteasy’s trade-off is: preserve correctness—“by time step, using only data visible at that time”—while using vectorization + Numba acceleration within a single step, balancing speed while ensuring correctness.
9.3. 3. qteasy 的做法:向量化 + 数据隔离
3.1 Vectorization of the Backtesting Core
The backtest loop and trading calculations (buy/sell volume, fees, positions, settlement) have been implemented with NumPy + Numba: stepping forward sequentially along the time dimension, and vectorizing across instruments within each step. See Backtest Engine and Performance (Design Perspective).
3.2 Pre-packaging and assembling data in advance, and step-by-step injection
Preparation stage: before backtesting, pull and cache all required historical data from the DataSource via
check_and_prepare_backtest_data,prepare_data_buffer, etc.; then usecreate_data_windowsto generate a data-window stream aligned to the run cadence withrolling_window(each step corresponds to a fixed-length historical window).Before each step runs: the engine calls update_running_data_window to write the data window corresponding to the current step into strategy attributes; within
realize(), the strategy can only obtain the window visible for that step via get_data(dtype_id), and computes indicators such as EMA on that window—consistent with “only the history that was visible at the time” in live trading—thereby avoiding look-ahead bias by design.
The data flow is “declare → the engine injects step by step → referenced by get_data”, and strategy code contains no “read tables” or “fetch/pull” logic. See How Strategies Declare and Use Data.
3.3 The meaning and impact of use_latest_data_cycle (ulc)
use_latest_data_cycle controls whether the data window includes the current bar (K-line):
True: The window may include the current bar (e.g., the closing price), corresponding to modeling “whether trading is allowed to use the latest price at that moment”.
False: The window is strictly earlier than the current moment, using only data that has “already closed” in the past.
This switch has a significant impact on long-horizon backtest results. qteasy exposes it as a configurable option in strategy declarations and run configurations; the documentation and API should state it clearly and link to the configuration notes.
9.4. 4. 独特优势小结
High speed + look-ahead prevention: while ensuring “each step uses only data visible at that time”, it achieves fast backtesting with vectorization + Numba, balancing speed and backtest credibility.
Fine-grained trade modeling: T+1, fees, settlement, MOQ, and “whether to use the current bar’s data”, etc., can all be modeled. Long-horizon backtests are sensitive to such details, and qteasy supports explicit configuration.
Concise interface: Users only need to fetch data via
get_data()inrealize()and compute signals; the framework handles the data window and time alignment, reducing the chance of mistakenly using future data.
9.5. 5. 代码依据摘要
Data preparation and windows:
check_and_prepare_backtest_datainhistory.py;prepare_data_bufferandcreate_data_windowsinqt_operator.py(applyrolling_windowtobuffered_data.valuesand align it with the running schedule);ulcdetermines whether the window includes the current bar.Step-by-step injection: In
qt_operator.py,run_strategy(step_index)callsupdate_running_data_window(..., window_index=step_index)on the strategy at each step; instrategy.py,update_running_data_windowassigns the corresponding data window to the strategy’s attributes, and whatget_data()reads is exactly that step’s window.
For more implementation details, see the chapters in Architecture and Design and the API reference.