9. Intención de diseño y ventajas únicas
Este capítulo se centra en la intención de diseño y las ventajas únicas de qteasy: por qué se centra en el backtesting vectorizado de alta velocidad pero abandona la «vectorización de línea de tiempo completa», cómo equilibra la velocidad y la prevención del sesgo de anticipación a través de la «vectorización + aislamiento de datos» y cómo las opciones configurables como use_latest_data_cycle afectan los backtests de largo horizonte. Se hace eco de [Enfoque general de arquitectura y diseño] (00-overview.md) y [Datos en estrategias] (03-data-in-strategies.md).
9.1. 1. 为何不做全时间轴向量化
1.1 Riesgo de función futura
Si calcula los indicadores una vez a lo largo de toda la línea de tiempo (por ejemplo, una EMA de todo el historial), la serie histórica resultante no es consistente con el resultado de «calcular segmento por segmento usando solo la ventana histórica visible en ese momento»: una EMA de todos los ejes utilizará precios futuros, que no se pueden reproducir en operaciones reales, lo que causa divergencia entre las pruebas retrospectivas y las operaciones reales. Por lo tanto, qteasy no calcula indicadores “de una sola vez para todo el segmento” a lo largo de la dimensión temporal; en cambio, proporciona la estrategia, paso a paso, solo la ventana de datos visible en el paso actual.
1.2 Los estados y las reglas no se pueden expresar
Si se vectoriza completamente a lo largo de la dimensión temporal, resulta difícil implementarlo correctamente:
restricciones como liquidación T+1, tarifas comerciales y cantidad mínima de pedido (MOQ);
Una elección de modelado de si se permite el uso de los datos más recientes «en el momento de la negociación» (la vela/línea K actual).
Estos detalles pueden producir diferencias multiplicadas en los rendimientos en las pruebas retrospectivas a largo plazo (por ejemplo, en una prueba retrospectiva de diez años, la pequeña diferencia de «si se utilizan los datos de la línea K de la barra actual» puede cambiar significativamente los rendimientos acumulados). qteasy hace que “si se deben usar datos de la barra actual” sea una opción de configuración explícita use_latest_data_cycle en lugar de una suposición implícita.
9.2. 2. 事件驱动的替代与取舍
Un enfoque común está basado en eventos: para cada evento comercial, obtenga datos por instrumento y calcule los indicadores paso a paso, evitando así por completo el sesgo de anticipación y manteniéndose cerca del comercio en vivo, pero es lento. La compensación de qteasy es: preservar la corrección, “por paso de tiempo, usando solo datos visibles en ese momento”, mientras se usa vectorización + aceleración Numba en un solo paso, equilibrando la velocidad y garantizando la corrección.
9.3. 3. qteasy 的做法:向量化 + 数据隔离
3.1 Vectorización del núcleo de backtesting
El ciclo de backtest y los cálculos comerciales (volumen de compra/venta, tarifas, posiciones, liquidación) se implementaron con NumPy + Numba: avanzando secuencialmente a lo largo de la dimensión temporal y vectorizando todos los instrumentos dentro de cada paso. Consulte [Backtest del motor y el rendimiento (perspectiva de diseño)] (07-backtest-engine-and-rendimiento.md).
3.2 Datos de preenvasado y montaje por adelantado, e inyección paso a paso
Etapa de preparación: antes de realizar pruebas retrospectivas, extraiga y almacene en caché todos los datos históricos requeridos del DataSource a través de
check_and_prepare_backtest_data,prepare_data_buffer, etc.; luego usecreate_data_windowspara generar un flujo de ventana de datos alineado con la cadencia de ejecución conrolling_window(cada paso corresponde a una ventana histórica de longitud fija).Antes de ejecutar cada paso: el motor llama a update_running_data_window para escribir la ventana de datos correspondiente al paso actual en atributos de estrategia; dentro de
realize(), la estrategia solo puede obtener la ventana visible para ese paso a través de get_data(dtype_id), y calcula indicadores como EMA en esa ventana, consistente con «solo el historial que era visible en ese momento» en el comercio real, evitando así el sesgo de anticipación por diseño.
El flujo de datos es «declarar → el motor inyecta paso a paso → referenciado por get_data», y el código de estrategia no contiene lógica de «lectura de tablas» o de «búsqueda/extracción». Consulte [Cómo las estrategias declaran y utilizan datos] (03-data-in-strategies.md).
3.3 El significado y el impacto de use_latest_data_cycle (ulc)
use_latest_data_cycle controla si la ventana de datos incluye la barra actual (línea K):
Verdadero: La ventana puede incluir la barra actual (por ejemplo, el precio de cierre), correspondiente al modelado «si se permite operar con el último precio en ese momento».
Falso: La ventana es estrictamente anterior al momento actual y utiliza solo datos que “ya se cerraron” en el pasado.
Este cambio tiene un impacto significativo en los resultados del backtest a largo plazo. qteasy lo expone como una opción configurable en declaraciones de estrategia y configuraciones de ejecución; la documentación y la API deben indicarlo claramente y vincular a las notas de configuración.
9.4. 4. 独特优势小结
Alta velocidad + prevención de anticipación: al tiempo que garantiza que “cada paso utiliza solo datos visibles en ese momento”, logra un backtesting rápido con vectorización + Numba, equilibrando la velocidad y la credibilidad del backtest.
Modelado comercial detallado: se pueden modelar T+1, tarifas, liquidación, MOQ y “si se utilizan los datos de la barra actual”, etc. Las pruebas retrospectivas a largo plazo son sensibles a dichos detalles y qteasy admite una configuración explícita.
Interfaz concisa: los usuarios solo necesitan obtener datos a través de
get_data()enrealize()y calcular señales; el marco maneja la ventana de datos y la alineación temporal, lo que reduce la posibilidad de utilizar datos futuros por error.
9.5. 5. 代码依据摘要
Preparación de datos y ventanas:
check_and_prepare_backtest_dataenhistory.py;prepare_data_bufferycreate_data_windowsenqt_operator.py(aplicarrolling_windowabuffered_data.valuesy alinearlo con el cronograma de ejecución);ulcdetermina si la ventana incluye la barra actual.Inyección paso a paso: en
qt_operator.py,run_strategy(step_index)llama aupdate_running_data_window(..., window_index=step_index)a la estrategia en cada paso; enstrategy.py,update_running_data_windowasigna la ventana de datos correspondiente a los atributos de la estrategia, y lo que leeget_data()es exactamente la ventana de ese paso.
Para obtener más detalles de implementación, consulte los capítulos en Arquitectura y Diseño y la referencia de API.