10. Datos de proceso (proc.*) y backtesting dinámico (especificación de diseño)

Este capítulo explica la definición y los métodos de acceso de datos de proceso en qteasy, así como la selección de ramas estáticas frente a ramas dinámicas durante las pruebas retrospectivas y las convenciones de coherencia entre ellas, como referencia al implementar y ampliar. Para obtener explicaciones a nivel de uso, léalo junto con «Cómo las estrategias declaran y utilizan datos» y la documentación de la API.

10.1. 1. 背景与目标

1.1 Significado de los datos del proceso

Algunas estrategias deben depender de datos que cambian con la ruta de ejecución de operaciones en vivo o de prueba retrospectiva, por ejemplo:

  • Posiciones actuales/históricas, efectivo disponible;

  • Cantidad histórica surtida, precio de ejecución, costos de transacción;

  • Valor de mercado, activos totales, etc. derivados de posiciones y precios.

Estos datos no se pueden generar previamente de una sola vez antes de que comience una prueba retrospectiva; solo puede mantenerse durante el tiempo de ejecución mediante Backtester (backtesting) o Trader (comercio en vivo) y proporcionarse a la estrategia en cada paso de la generación de señales de acuerdo con el «alcance actualmente visible». Nos referimos colectivamente a ellos como datos de proceso.

1.2 Objetivos de diseño

  • Punto de entrada unificado: al igual que los datos históricos estáticos, los datos del proceso se obtienen a través de Strategy.get_data(), lo que reduce la curva de aprendizaje.

  • Sin anticipación: al generar la señal para el paso k, la estrategia no puede ver los resultados de la ejecución del paso k; sólo puede utilizar el historial de pasos completados.

  • Consistencia en backtest/en vivo: el mismo conjunto de estrategias y el mismo patrón de llamadas get_data('proc.xxx') se pueden usar tanto en backtesting como en operaciones en vivo; cuando se necesitan datos de proceso, sigue la ruta de ejecución dinámica; de lo contrario, puede permanecer coherente con la ruta estática original en términos de resultados.

10.2. 2. 过程数据的统一定义(proc.*)

2.1 Método de denominación y exposición

  • Todos los datos del proceso están expuestos a la estrategia en el formato proc.<field_name>, como proc.own_cash y proc.trade_records.

  • Los datos del proceso no necesitan declararse mediante data_types en el __init__ de la estrategia. Backtester / Trader lo inyecta en tiempo de ejecución, y la estrategia solo necesita llamar a get_data('proc.xxx', ...) en realize() según sea necesario.

2.2 Campos integrados implementados

Los campos de datos de proceso implementados en la versión actual y disponibles para su uso en estrategias incluyen:

Categoría

Nombre del campo

Significado

escalar de cuenta

proc.own_cash

Efectivo total en la cuenta al inicio del paso actual

proc.available_cash

Efectivo disponible para realizar pedidos al inicio del paso actual

proc.total_value

Valor total de mercado del activo al inicio del paso actual (valoración de la posición + efectivo)

Vector de posición

proc.own_amounts

Cantidad de posición total de cada instrumento al inicio del paso actual

proc.available_amounts

Cantidad vendible para cada instrumento al inicio del paso actual

proc.position_value

Valor de mercado de posición para cada instrumento al inicio del paso actual (calculado a partir del precio interno y las posiciones)

Resultados de ejecución

proc.trade_records

Cantidad real ejecutada para cada instrumento en cada paso (positiva para compras, negativa para ventas)

proc.trade_cost

Costos de transacción para cada instrumento en cada paso.

proc.trade_price

Precio de ejecución de cada instrumento en cada paso.

Para conocer la semántica de tiempo y las limitaciones de visibilidad de los campos anteriores en backtesting y trading real, consulte la Sección 4.

2.3 Campos extensibles futuros (opcional)

Los campos que se pueden ampliar aún más por diseño incluyen: proc.realized_pnl, proc.unrealized_pnl, proc.last_trade_price, proc.last_trade_volume, etc. Consulte la implementación y la documentación para obtener más detalles.

10.3. 3. 访问接口:Strategy.get_data() 与 proc.*

3.1 Datos estáticos (sin prefijo de proceso)

  • Fuente única: self.get_data('close_E_d'); múltiples fuentes: self.get_data('close_E_d', 'high_E_d').

  • Los datos estáticos no admiten los parámetros lag / window; si se proporciona, se genera un ValueError en inglés.

3.2 Datos de proceso (prefijo de proceso)

  • Ejemplos de llamadas:

    • self.get_data('proc.own_cash'): la serie de efectivo hasta el paso actual;

    • self.get_data('proc.own_cash', lag=0): el valor en efectivo en el paso más reciente;

    • self.get_data('proc.own_cash', lag='1d'): el paso correspondiente a mirar hacia atrás 1 día en el tiempo;

    • self.get_data('proc.own_cash', window='5d'): un segmento de ventana durante los últimos 5 días.

  • Restricciones:

    • Una sola llamada permite sólo un campo proc.*; si se utilizan varios campos o se mezcla con datos estáticos en una misma llamada, levantar un ValueError en inglés.

    • lag y window no se pueden especificar al mismo tiempo; lag puede ser un número entero (pasos) o una cadena (por ejemplo, '1d', '8h') y window es una cadena (por ejemplo, '5d', '8h').

  • Valor de retorno: siempre np.ndarray; la forma y el tipo de datos están sujetos a la documentación de la API.

10.4. 4. 回测分支选择与过程数据协作

4.1 Rama estática y rama dinámica

  • Rama estática (_backtest_static_operator): llame a run_strategies una vez por todas para generar todas las señales, luego complete la prueba retrospectiva utilizando funciones vectorizadas de Numba como backtest_batch_steps. Adecuado para estrategias que no dependen de datos de proceso.

  • Rama dinámica (_backtest_dynamic_operator): bucle en pasos de tiempo; en cada paso genere señales → analice y simule rellenos → actualice posiciones y efectivo, luego pase al siguiente paso. Los datos del proceso son mantenidos por Backtester y se inyectan en Operator antes de cada paso, por lo que las estrategias pueden acceder a ellos a través de get_data('proc.xxx').

Antes de ejecutarse, Backtester decide qué rama tomar a través de Operator.check_dynamic_data().

4.2 Lógica de decisión de check_dynamic_data() (implementación actual)

Si alguna de las siguientes condiciones es verdadera, devuelve True y toma la rama dinámica:

  1. op_type == “stepwise”: Operator está configurado explícitamente en modo paso a paso.

  2. Usando proc. en el código fuente de la estrategia*: _strategies_use_proc_data() verifica si el código fuente de realize() de cada estrategia contiene 'proc.' o "proc.". Si es así, se considera que depende de los datos del proceso.

Por lo tanto, siempre que se llame a get_data('proc.xxx') en realizar(), automáticamente tomará la rama dinámica sin ninguna declaración. Solo se accede a los datos del proceso a través de proc.*; ya no se admite declararlo a través de DataType (se han eliminado los tipos op_* heredados).

4.3 Sin garantía anticipada

  • Cuando la estrategia genera una señal en el paso k:

    • Los datos de cuenta/posición (por ejemplo, own_cashes, own_amounts) son visibles como máximo hasta el índice [0..k] (es decir, el estado al inicio del paso actual);

    • Los datos relacionados con el comercio (por ejemplo, trade_records, trade_cost) son visibles como máximo hasta [0..k-1], excluyendo los intercambios que aún no se han producido en este paso.

  • En la implementación, el _current_signal_index de Operator y el _get_process_data_single() de la estrategia se dividen según los rangos anteriores. El Backtester actualiza el índice antes de generar señales en cada paso, lo que garantiza que no haya anticipación.

4.4 La relación de inyección entre Backtester y Operator

  • Backtester (rama dinámica): en el punto de entrada _backtest_dynamic_operator, inyecte own_cashes, available_cashes, own_amounts_array, available_amounts_array, trade_records_array, trade_cost_array, trade_price_array, trade_price_data, etc. en Operator como _process_data_sources, y establezca _process_time_index en una línea de tiempo alineada con op_signal_index.

  • Operator: En run_strategy(step_index), antes de cada llamada a stg.generate(), calcule y actualice _current_signal_index según group_timing_table y group_merge_type, para que la estrategia divida los datos del proceso «actualmente visibles».

4.5 Procesar datos en operaciones reales (Trader)

Cuando operator.check_dynamic_data() es Verdadero, Trader, en _run_strategy(), hará lo siguiente:

  • Reúna el efectivo de la cuenta corriente, las posiciones, las cantidades disponibles, los precios actuales, etc. en un solo paso _process_data_sources y _process_time_index (una sola ejecución de operaciones en vivo se trata como un solo paso);

  • Dentro de este paso, la estrategia puede llamar a get_data('proc.own_cash'), etc. para obtener la vista de cuenta/posición actual; el historial comercial está vacío dentro de este paso, lo que es consistente con la semántica de «aún no se han producido transacciones en este paso».

10.5. 5. 动态/静态路径一致性约定

  • Cuando una estrategia no utiliza datos de proceso: debe tomar la rama estática; Si toma la rama dinámica por otros motivos, los resultados del backtest deben ser exactamente idénticos numéricamente a los de la rama estática (bajo la misma configuración y datos).

  • Cuando una estrategia usa datos de proceso: debe pasar por la rama dinámica; de lo contrario, si no se inyecta _process_data_sources, se generará un RuntimeError.

  • Convención de prueba: use StaticSignalStg (puramente estático) y ProcAwareButStaticLogicStg (llama a proc pero no lo usa para señales) para realizar una prueba retrospectiva bajo la misma configuración y afirmar la coherencia numérica para own_cashes, own_amounts_array, trade_records_array, etc.; consulte el Grupo B en ⟦CÓDIGO5⟧.

10.6. 6. 测试与文档索引

  • Pruebas dedicadas: tests/test_process_data_api.py

    • Grupo A: el comportamiento de check_dynamic_data() bajo estrategias/estrategias puramente estáticas usando proc.*;

    • Grupo B: coherencia de la matriz de prueba retrospectiva entre estrategias estáticas y estrategias de «llamar a proc pero lógicamente equivalentes»;

    • Grupo C: get_data comportamiento de error para múltiples fuentes estáticas, rechazo de retraso/ventana, proc de campo único y llamadas mixtas;

    • Grupo D: sin validación anticipada para proc.trade_records;

    • Grupo E: corrección de la ruta real de la estrategia dinámica basada en los datos del proceso.

  • Memoria del proyecto: .cursor/rules/process-data-and-dynamic-backtest.mdc (resumen de implementación y convención).

  • Resumen de los datos de la estrategia: Cómo las estrategias declaran y utilizan los datos; Modos y puntos de entrada de backtest: Backtesting, operaciones en vivo y optimización.