1. qteasy Overall Architecture and Design Approach
1.1. 1. 引言:为什么需要从“架构”理解 qteasy
qteasy is a localized, reproducible quantitative trading toolkit. Beyond the user guide and API documentation, understanding how the modules are layered and collaborate from the perspectives of system architecture and design rationale helps you use the interfaces more accurately, troubleshoot issues, and avoid detours when extending custom strategies or data. This chapter provides an overall summary of qteasy’s architecture and three main design threads; subsequent chapters expand on each module.
1.2. 2. 三条设计主线(简述)
2.1 Data: From “Raw Tables” to “Typed Information” (DataType)
After raw market, financial, and other data are fetched and cleaned, they are stored in DataSource (files or a database) using a unified table schema. qteasy does not let strategies read or write these tables directly; instead, it abstracts “information usable by strategies” into DataType (composed of name, freq, asset_type, etc., corresponding to a unique dtype_id). A strategy only declares which DataTypes it needs and how long a historical window (window_length) it requires; at runtime, the engine retrieves data from the DataSource according to the declaration, organizes it into a data window, and injects it into the strategy. The purposes include: using the same data view for backtesting and live trading, unifying the strategy interface, and preventing “look-ahead bias” at the mechanism level (a strategy can only see past data injected by the engine at each time step).
2.2 Strategy: Four Key Elements and a Unified realize()
Conceptually, a strategy contains four categories of elements:
Run timing: when and how often to run (e.g., daily at market close) — in qteasy 2.0 this is managed by the strategy’s Group, not hard-coded inside the strategy class.
Required data: which DataTypes are needed, how long the window is (data_types, window_length, etc.), declared during strategy initialization.
Tunable parameters: defined by Parameter, set before running via
set_parameterand the like; during optimization, the Optimizer searches the parameter space.Logic: implemented via a parameterless realize(); within the method, use
get_pars()andget_data(dtype_id)to obtain the current step’s parameters and data, compute and return trading signals.
The same realize() is reused in both backtesting and live trading to ensure consistent behavior.
2.3 Execution: Operator and Group, Driven by Time Steps, Unified run(config)
Operator is both the container for strategies and the entry point for execution: it holds multiple Groups, and each Group is a set of strategies with the same run_freq and run_timing (as well as signal_type and blender). group_timing_table is a “time step × Group” table indicating which Groups need to run at each time step. A single-step workflow can be summarized as: look up the table for the current step to get the Groups to run → prepare and inject a data window for each strategy in those Groups → call each strategy’s generate() (which internally calls realize()) → blend signals within the Group using the Group’s blender → obtain the merged signal for that step. Backtesting, live trading, and optimization are all based on the same mechanism of “running the Operator step by step according to group_timing_table”; the differences lie only in the data source (historical vs. real-time) and result handling (simulated fills vs. real orders vs. parameter search).
2.4 Visualization: HistoryPanel and Multi-Backend Charts
On top of data and execution, qteasy provides a HistoryPanel-based visualization stack:
As a 3D data container (shares × hdates × htypes), HistoryPanel is both the natural return value of
get_history_dataand the prerequisite data structure for visualization;HistoryPanel.plot(...)is the unified plotting entry point. It automatically selects the appropriate chart type (candlestick, volume, MACD, line chart, etc.) based on htypes and shares, and generates backend-agnostic chart specifications;The static backend (matplotlib) and the interactive backend (Plotly) render charts on top of the same set of specifications, ensuring consistency between static/interactive views in color scheme, layout, and semantics;
The upper-layer APIs such as
qt.candle(...)are merely wrappers for “fetch data → build a HistoryPanel → callhp.plot()”.
In this way, the visualization logic is decoupled from data retrieval and the backtesting engine, making it easier to evolve and test independently.
1.3. 3. 总体架构图(三层)
By responsibilities, qteasy can be divided into three layers, as shown in the figure below (text version):
Data layer: DataSource, data tables, DataType, and the external-facing get_history_data / HistoryPanel, providing “by type, by window” historical information for the strategy and runtime layers.
Strategy layer: BaseStrategy and its subclasses (e.g., RuleIterator, FactorSorter, GeneralStg), Parameter, data_types / window_length, responsible for declaring requirements and implementing realize().
Runtime layer: Operator, Group, group_timing_table, blender, and Backtester / Trader / Optimizer, responsible for driving strategies over time, aggregating signals, and executing backtesting / live trading / optimization.
1.4. 4. 核心对象关系图
An Operator contains multiple Groups, and each Group contains multiple Strategies.
Each Strategy declares the DataType (and window_length) it depends on, and defines tunable parameters via Parameter.
The entry point is
op.run(config, datasource, logger), driven by config and datasource.
1.5. 5. 数据与信号的大致流向
From data sources to strategies to backtesting/live trading/optimization, the overall flow can be simplified as:
Users enter backtesting (1), live trading (0), or optimization (2) via
qt.run(op, mode=...).Based on the config and strategy declarations, the Operator requests data from the DataSource, then calls strategies step by step according to the group_timing_table and blends the signals.
In backtesting, the signal sequence is handed to the Backtester for simulated fills and evaluation; in live trading it is handed to the Trader/Broker for execution; in optimization, the Optimizer runs backtests multiple times and aggregates the results.
1.6. 6. 本系列各章与教程、API、示例的阅读顺序建议
This series (architecture and design): It’s recommended to read this chapter (00) and Core Concepts at a Glance first, then read as needed Data Layer, Data in Strategies, Operator and Groups, Strategy Execution and Parameters, and Backtesting/Live Trading/Optimization to build an overall picture.
User guide: Focuses on “hands-on” step-by-step operations. It’s suitable to read selectively as needed after understanding the architecture, to complete specific tasks (such as downloading data, writing strategies, and running backtests).
Download and manage historical financial data, API reference: Used to look up data configurations, interface parameters, and return values; this series covers only “structure and mechanisms” and does not replace the API documentation.
Examples: Provide complete runnable examples that can be used together with the tutorials and this series.