5. Uso de HistoryPanel para estudiar factores transversales de selección de acciones
Selección de acciones transversal: ponderación multifactorial -> comparar rentabilidades de carteras agrupadas y CAGR
Al realizar una selección transversal de acciones, a menudo nos encontramos con una situación que parece «muy real pero muy incómoda»:
Claramente tenemos un montón de acciones (docenas, cientos), y también sabemos que debemos usar múltiples métricas para filtrar (PE, PB, EBITDA, impulso, volatilidad…)
Pero una vez que escribe las condiciones en el código, es fácil encontrarse con «las formas no coinciden», «la transmisión de condiciones es incorrecta» o «las existencias analizadas son inestables».
Al final, se obtiene una única curva de cartera, pero no se puede explicar claramente: qué condiciones están impulsando realmente las diferencias de rentabilidad.
Este tutorial sigue un ritmo de “hacer que funcione primero, luego mejorarlo” para convertir este proceso en un flujo de trabajo de investigación reutilizable: multifactor (transversal) -> condiciones transversales -> where máscara -> portfolio + benchmark -> normalize/cum_return + CAGR -> plot/highlight explicación.
Nuevamente, para aclarar el posicionamiento: esto sigue siendo una agregación burda orientada a la investigación, no un motor de backtesting comercial.
5.1. 0. 开场:先跑通一个“横向筛选 -> 组合曲线对比”的最小版本
Por ahora no analizaremos muchos factores ni ajustaremos parámetros. Una prueba mínima ejecutable sólo necesita hacer dos cosas:
Con varias acciones, puede realizar una evaluación transversal;
Al final, podrás trazar una curva combinada y compararla con
000300.SH.
import qteasy as qt
benchmark = '000300.SH'
shares = [
'000001.SZ', '600519.SH', '300750.SZ', '000333.SZ', '600036.SH',
'601318.SH', '002415.SZ', '000858.SZ', '600276.SH', '000725.SZ',
benchmark,
]
hp = qt.get_kline(
shares=shares,
start='20220101',
end='20221231',
freq='D',
as_panel=True,
)
fig = hp.plot(interactive=True)
fig
Sin embargo, el simple hecho de “ser capaz de planearlo” está lejos de ser suficiente. Si realmente queremos utilizarlo como herramienta de investigación de selección de acciones en el día a día, nos encontraremos con al menos los siguientes problemas:
Cómo alinear condiciones de múltiples factores: conceptos como PE/PB/momentum/volatilidad son fáciles de entender, pero una vez que escribes el código te toparás con obstáculos: algunos factores son
(M,L), otros los escribiste como(M,L,1), y además de eso,HistoryPanelviene con su propia tercera dimensión para las columnas de campo, por lo que es fácil para la transmisión «coincidir, pero no en la forma en que lo haces». pensar”. La peor parte es que estos errores a menudo no generan errores; simplemente hacen que los resultados parezcan «apagados».La selección de acciones es una decisión transversal: la selección transversal no es «hacer un juicio sobre cada acción individualmente», sino «el mismo día, elegir un subconjunto de un grupo de acciones». Esto significa que su conjunto de tenencias puede cambiar todos los días: 10 acciones hoy, 3 mañana y posiblemente 0 al día siguiente. Si no solidifica explícitamente «la canasta seleccionada cada día» (por ejemplo, con una máscara), simplemente no podrá revisar «cuáles fueron realmente seleccionadas ese día».
Sin una comparación de referencia, no hay conclusión: una curva combinada que se vea bien no significa que sea efectiva; puede que simplemente esté en la fase beta del mercado. Traiga
000300.SHcomo referencia; como mínimo, responde a la pregunta más crítica: ¿esta pantalla está creando alfa o simplemente sigue al mercado en general?Devoluciones sin explicación: el análisis transversal puede terminar fácilmente con “sólo queda una curva”. Pero la investigación real necesita una explicación: en los días con las mayores diferencias de rentabilidad, ¿se produjo una rotación de estilos? ¿Los criterios de selección nos arrinconaron con una alta volatilidad/alta caída de la inversión? ¿Podemos identificar rápidamente el “segmento de divergencia clave” y luego decidir qué factor fortalecer a continuación?
Afortunadamente, todas estas capacidades se pueden completar paso a paso. A continuación, comenzaremos con la «construcción de factores».
5.2. 0.5 Primero, muestra el resultado final (con qué terminaremos)
Después de seguir este artículo, obtendrá tres resultados muy «favorables para la investigación»:
Un cuadro comparativo de curvas de cartera:
LONG / SHORT / 000300.SH(o al menosLONG / 000300.SH) normalizado para comenzar desde 1.0, para que pueda saber de un vistazo si la regla de selección tiene poder discriminativo dentro del período de muestra.Una tabla de resumen CAGR: convierta los rendimientos acumulados en una métrica anualizada, lo que facilita la comparación entre diferentes períodos.
Un único gráfico destacado de “día de divergencia clave/segmento de divergencia”: marque los puntos donde la cartera larga diverge más claramente del índice de referencia, lo que facilita su revisión y explicación.
5.3. 1. 目标(我们这篇文章要完成什么)
Obtenga el
HistoryPanelpara múltiples recursos compartidos +000300.SHConstruir multifactores (dos rutas en paralelo: una versión de umbral fijo de factor proxy + una versión opcional de valoración verdadera)
Combine condiciones de múltiples factores en un bool
(M,L)y usewhere()para generar la máscara de investigaciónUtilice
portfolio(mask=...)para obtener las curvas de cartera larga/corta y compararlas con el punto de referenciaUtilice
normalize/cum_returnpara derivar la tabla CAGRUtilice
plot(highlight=...)para resaltar el “día de diferencia máxima/segmento de divergencia clave” y hacerlo explicableAl final, proporcionamos un fragmento de código completo que «se ejecuta como una única función».
5.5. 3. 多因子构造(两条路线并列):固定阈值代理版 + 可选真实估值版
3.1 Qué pretende resolver esta sección
En este paso, lo que queremos es: una matriz de factores que pueda combinarse directamente en condiciones de detección. En una «sección transversal», queremos que cada factor finalmente tenga la misma forma: (M, L).
Para simplificar, aquí nos dividimos en dos pistas:
Ruta A (recomendada para principiantes): derivar “factores proxy” enteramente a partir de datos de mercado/indicadores técnicos, junto con umbrales fijos, para garantizar que todos puedan ejecutarlo
Pista B (mejora opcional): si su fuente de datos local ya tiene campos de valoración como PE/PB/EBITDA, reemplácelos aquí
En última instancia, ambas rutas generan el mismo cond_long/cond_short y el flujo de trabajo posterior es exactamente el mismo.
3.2 Principios mínimos necesarios
HistoryPanel.kline.* devolverá un nuevo panel con columnas adicionales, por ejemplo:
sma_20macd_hist_12_26_9⟦CÓDIGO0⟧ etc.
Todas estas columnas se pueden ubicar a través de hp.htypes.index(name) en una matriz 2D de (M, L). Luego podemos escribir condiciones multifactor con umbrales fijos y asegurarnos de que la condición final sea un (M, L) bool.
3.3 Vía A: Factores proxy (umbrales fijos; se recomienda que esto funcione primero)
3.3.1 Qué resolver
Utilizamos tres factores proxy «muy intuitivos» para ayudar a los lectores a entrar en el flujo rápidamente:
Valor proxy:
close / sma20(cuanto menor es la desviación de la media móvil, más “barato” es)Proxy de impulso:
macd_hist(fuerza/debilidad)Proxy de riesgo: ancho de la banda de Bollinger (magnitud de la volatilidad)
Vale la pena enfatizar aquí dos puntos de “honestidad en las convenciones de investigación”:
Todos estos son valores aproximados, no “valoración real” en el sentido financiero. Su valor radica en que todos pueden derivarlos de datos de mercado y, bajo ciertos estilos, pueden producir un efecto de estratificación.
También tienen limitaciones claras:
close/sma20puede confundir una tendencia con barata/cara; El MACD puede seguir cayendo en un rango entrecortado; un filtro de ancho de banda también filtrará las «oportunidades creadas por la volatilidad». El objetivo de este artículo es conseguir que el proceso funcione de un extremo a otro; los umbrales en sí son sólo un punto de partida reproducible.
3.3.2 Código ejecutable + resultados esperados
import numpy as np
hp2 = hp.kline.sma(window=20, price_htype='close') # sma_20
hp2 = hp2.kline.macd(price_htype='close') # macd_hist_12_26_9
hp2 = hp2.kline.bbands(window=20, price_htype='close') # bbands_*_20_2_2
vals = hp2.values.astype(float)
close = vals[:, :, hp2.htypes.index('close')]
sma20 = vals[:, :, hp2.htypes.index('sma_20')]
macd_hist = vals[:, :, hp2.htypes.index('macd_hist_12_26_9')]
upper = vals[:, :, hp2.htypes.index('bbands_upper_20_2_2')]
mid = vals[:, :, hp2.htypes.index('bbands_middle_20_2_2')]
lower = vals[:, :, hp2.htypes.index('bbands_lower_20_2_2')]
value_proxy = close / sma20
momentum_proxy = macd_hist
risk_proxy = (upper - lower) / mid
# 固定阈值(入门优先:简单、可跑、可理解)
A_VALUE = 1.02 # 强一点:价格明显强于均线才算“强势”
C_MOM = 0.0 # MACD 柱 > 0 视为偏多
B_RISK = 0.18 # 带宽太大视为波动过强,先过滤掉
cond_long = (value_proxy > A_VALUE) & (momentum_proxy > C_MOM) & (risk_proxy < B_RISK)
cond_short = (value_proxy < 1.0 / A_VALUE) & (momentum_proxy < -C_MOM) & (risk_proxy < B_RISK)
print('cond_long shape:', cond_long.shape) # 期望 (M, L)
print('cond_short shape:', cond_short.shape)
print('selected_count_last_day(long):', int(cond_long[:, -1].sum()))
En este punto hemos obtenido los «ingredientes principales» para el filtrado transversal: cond_long/cond_short (M,L bool).
A continuación, todavía tenemos que hacer una comprobación de cordura muy práctica: cuántas acciones se seleccionan cada día. Si este número suele ser 0 (una cesta vacía), la curva de su cartera será intermitente y sus conclusiones serán inestables; Si este número es casi siempre de todas las acciones, entonces la selección no tiene sentido.
Puede agregar esta verificación (no cambia la lógica; solo nos ayuda a juzgar si los umbrales son «demasiado estrictos/demasiado flexibles»):
selected_count_by_day = cond_long.sum(axis=0) # (L,)
print('selected_count stats (long):')
print(' min/max:', int(selected_count_by_day.min()), int(selected_count_by_day.max()))
print(' mean:', float(selected_count_by_day.mean()))
print(' p10/p50/p90:', np.quantile(selected_count_by_day.astype(float), [0.1, 0.5, 0.9]))
3.4 Ruta B: factores de valoración verdaderos (mejora opcional; los nombres de los campos están sujetos a hp.htypes)
3.4.1 Qué hay que resolver
Si su fuente de datos local ya ha descargado campos de valoración (por ejemplo, PE/PB/EBITDA), entonces podemos intercambiarlos. En esta sección no codificaremos los nombres de los campos, porque la denominación htype puede diferir entre las fuentes de datos/tipos de datos. El enfoque más sólido es:
Primera impresión
hp.htypesEncuentre los nombres de las columnas reales en su configuración local
Luego escriba las condiciones usando umbrales fijos.
3.4.2 Código ejecutable (ilustrativo)
print('available htypes:', hp.htypes)
# 假设你在 htypes 里找到了这三个字段(名称以你的本地为准)
# pe_name = 'pe' or 'pe_ttm' ...
# pb_name = 'pb' ...
# ebitda_name = 'ebitda' ...
# pe = hp.values[:, :, hp.htypes.index(pe_name)]
# pb = hp.values[:, :, hp.htypes.index(pb_name)]
# ebitda = hp.values[:, :, hp.htypes.index(ebitda_name)]
# 固定阈值示例(仅示意,阈值需要你按资产池与口径调整)
# cond_long = (pe < 15.0) & (pb < 2.0) & (ebitda > 1e9)
Mientras termines con (M, L) cond_long/cond_short, el resto del flujo de trabajo es exactamente el mismo que la Ruta A.
Si descubre que sus datos locales simplemente no tienen estos campos, está bien: es exactamente por eso que colocamos la «versión de factor proxy» en la ruta narrativa principal. La ruta B existe como una rama mejorada, pero aún puedes ejecutar el ciclo de investigación completo de un extremo a otro sin depender de él.
5.6. 4. 横向筛选:条件组合 -> where() 研究 mask
4.1 Qué pretende resolver esta sección
Convertimos cond_long/cond_short en una máscara de investigación que se puede introducir directamente en portfolio(mask=...).
4.2 Principios mínimos necesarios
hp.where() admite remodelar las condiciones booleanas (M, L) en una máscara (M, L, N). Este paso solidifica las “reglas de selección transversal” en una “definición de investigación”, y todos los cálculos de agregación y retorno posteriores siguen esto como estándar.
Este es también uno de los hábitos más valiosos que se pueden desarrollar en la investigación transversal: nunca mantengas una única “curva de cartera”; mantén también explícitamente la regla de “qué nombres entran en la canasta cada día”. La máscara es esa regla. Le permite responder preguntas de revisión como «¿a quién elegimos exactamente ese día?»
4.3 Código ejecutable + resultado esperado
mask_long = hp2.where(cond_long)
mask_short = hp2.where(cond_short)
print('mask_long shape:', mask_long.shape) # 期望 (M,L,N)
print('mask_long dtype:', mask_long.dtype)
5.7. 5. 两组组合曲线 + benchmark:portfolio(mask=...)
5.1 Qué pretende resolver esta sección
Para cada día de negociación, agregue el conjunto de acciones que cumplen las condiciones en una curva de cartera (larga/corta) y compárela con 000300.SH.
5.2 Los principios mínimos necesarios
portfolioes para agregación de investigación general, no para ejecución comercialbenchmark_output='tag_along'agregará la fila de referencia a la salida, lo que facilitará la comparación en el mismo gráfico
Puedes pensar en ello como: para cada día, toma un promedio de igual peso del “conjunto de acciones seleccionadas ese día” para obtener una curva. Debido a que el conjunto cambia todos los días, esta curva esencialmente responde a una pregunta:
Si cada día solo tengo “el lote que cumple con los criterios”, ¿cómo se comportará esta cesta dinámica a largo plazo?
5.3 Código ejecutable + resultados esperados
benchmark = '000300.SH'
pf_long = hp2.portfolio(
htypes='close',
mode='equal',
mask=mask_long,
benchmark=benchmark,
benchmark_output='tag_along',
new_share_name='LONG',
)
pf_short = hp2.portfolio(
htypes='close',
mode='equal',
mask=mask_short,
benchmark=benchmark,
benchmark_output='tag_along',
new_share_name='SHORT',
)
print('pf_long.shares:', pf_long.shares)
print('pf_long.shape:', pf_long.shape)
5.8. 6. normalize / cum_return + CAGR:给出可比较的年化摘要表
6.1 Qué pretende resolver esta sección
Queremos «mirar la curva» y «tener un resumen numérico reutilizable». Entonces hacemos dos cosas:
normalize: alinear el punto inicial (para una comparación visual más sencilla)cum_return + CAGR: generar una tabla resumen
6.2 El principio de necesidad mínima
cum_return genera el retorno acumulado cumret_*; el uso del valor final junto con el número de años le permite derivar la CAGR. (Para obtener la definición exacta de CAGR, consulte la discusión anterior; no ampliaremos la derivación aquí).
6.3 Código ejecutable (ilustrativo)
import pandas as pd
def _years_between(hdates) -> float:
idx = pd.DatetimeIndex(hdates)
days = (idx[-1] - idx[0]).days
return max(1e-9, days / 365.25)
def _cagr_from_cumret(cumret_end: float, years: float) -> float:
return (1.0 + cumret_end) ** (1.0 / years) - 1.0
years = _years_between(pf_long.hdates)
cr_long = pf_long.cum_return(htypes='close', method='simple')
cumret_long_end = float(cr_long.values[cr_long.shares.index('LONG'), -1, 0])
cumret_bm_end = float(cr_long.values[cr_long.shares.index('000300.SH'), -1, 0])
print('CAGR(long):', _cagr_from_cumret(cumret_long_end, years))
print('CAGR(bm):', _cagr_from_cumret(cumret_bm_end, years))
Le sugiero que lo organice en una tabla pequeña (que incluya al menos tres filas: LARGA/CORTA/punto de referencia), para que los lectores puedan comparar de un vistazo:
cr_short = pf_short.cum_return(htypes='close', method='simple')
cumret_short_end = float(cr_short.values[cr_short.shares.index('SHORT'), -1, 0])
summary = pd.DataFrame(
{
'cum_return_end': [cumret_long_end, cumret_short_end, cumret_bm_end],
'CAGR': [
_cagr_from_cumret(cumret_long_end, years),
_cagr_from_cumret(cumret_short_end, years),
_cagr_from_cumret(cumret_bm_end, years),
],
},
index=['LONG', 'SHORT', '000300.SH'],
)
print('\\n[CAGR summary]')
print(summary)
5.9. 7. 可视化与解释:用 plot(highlight=...) 高亮“关键差异日”
7.1 Qué pretende resolver esta sección
Cuando se lleva la investigación transversal hasta el final, el tipo de pregunta que más necesita explicación es: en el tramo donde el largo y el punto de referencia divergen más, ¿qué pasó exactamente?
Por eso usamos highlight para resaltar un segmento del “día de divergencia clave” (por ejemplo, el punto de rendimiento acumulado máximo/mínimo, o un intervalo que usted mismo elija), devolviendo la atención del lector al gráfico.
7.2 El principio de necesidad mínima
resaltado admite la abreviatura 'max'/'min' y también admite una condición bool 1D. Para mantener estable el tutorial, primero demostraremos la versión abreviada (la que es menos probable que te haga tropezar) y luego proporcionaremos el formato bool 1D.
7.3 Código ejecutable + resultado esperado
fig = pf_long.plot(interactive=True, highlight='max')
fig
Resultado esperado: el gráfico marcará el punto máximo de la curva de cartera LARGA (o el punto máximo definido en un gráfico determinado). En la investigación transversal, normalmente tratamos esto como un “recordatorio”: alrededor del punto máximo suele ser el período en el que durante mucho tiempo hubo el viento de cola más fuerte en relación con el mercado, y vale la pena mirar hacia atrás para ver si los criterios de selección nos llevaron a un estilo particular (por ejemplo, tendencia fuerte, baja volatilidad, etc.).
Si desea alinearse más estrechamente con «la divergencia entre largo y punto de referencia», puede utilizar un enfoque más práctico: primero calcule la curva de exceso de rendimiento (la diferencia entre largo y punto de referencia), luego extraiga la posición del punto máximo en esa curva de exceso y conviértala en una condición de resaltado bool 1D. (Esta es una mejora opcional; no es necesario ampliarla en la narrativa principal).
5.10. 8. 完整代码(单函数可跑版本)
A continuación se muestra una versión completa “ejecutable con una sola función” que puede copiar en una computadora portátil y ejecutar con un solo clic. Cubre:
Pista A (factor proxy con un umbral fijo) como línea principal;
Un control de cordura sobre el número seleccionado cada día;
Un circuito cerrado completo de
where -> portfolio -> cum_return -> CAGR;Una demostración estable de
plot(highlight=...).
import numpy as np
import pandas as pd
import qteasy as qt
def demo_horizontal_multifactor(
shares: list,
benchmark: str = '000300.SH',
start: str = '20220101',
end: str = '20221231',
):
\"\"\"演示横向多因子截面筛选:代理因子 -> 每日篮子 -> portfolio -> CAGR -> 高亮解释。
Parameters
----------
shares : list
股票池(必须包含 benchmark;建议 30~80 只更像“横向筛选”)。
benchmark : str, default '000300.SH'
基准指数代码。
start : str, default '20220101'
起始日期(YYYYMMDD)。
end : str, default '20221231'
结束日期(YYYYMMDD)。
Returns
-------
dict
包含 hp/hp2/pf_long/pf_short/summary/fig 等结果对象。
\"\"\"
if benchmark not in shares:
raise ValueError('benchmark must be included in shares')
hp = qt.get_kline(
shares=shares,
start=start,
end=end,
freq='D',
as_panel=True,
)
if 'close' not in hp.htypes:
raise ValueError('Missing close column in htypes')
if hp.shape[1] < 50:
raise ValueError(
'Not enough data points loaded (too few hdates). '
'Please check your local datasource and date range.'
)
# 1) 路线 A:代理因子(固定阈值)
hp2 = hp.kline.sma(window=20, price_htype='close')
hp2 = hp2.kline.macd(price_htype='close')
hp2 = hp2.kline.bbands(window=20, price_htype='close')
vals = hp2.values.astype(float)
close = vals[:, :, hp2.htypes.index('close')]
sma20 = vals[:, :, hp2.htypes.index('sma_20')]
macd_hist = vals[:, :, hp2.htypes.index('macd_hist_12_26_9')]
upper = vals[:, :, hp2.htypes.index('bbands_upper_20_2_2')]
mid = vals[:, :, hp2.htypes.index('bbands_middle_20_2_2')]
lower = vals[:, :, hp2.htypes.index('bbands_lower_20_2_2')]
value_proxy = close / sma20
momentum_proxy = macd_hist
risk_proxy = (upper - lower) / mid
A_VALUE = 1.02
C_MOM = 0.0
B_RISK = 0.18
cond_long = (value_proxy > A_VALUE) & (momentum_proxy > C_MOM) & (risk_proxy < B_RISK)
cond_short = (value_proxy < 1.0 / A_VALUE) & (momentum_proxy < -C_MOM) & (risk_proxy < B_RISK)
# 2) sanity check:每天入选数量
selected_count_by_day = cond_long.sum(axis=0)
print('\\n[Selection count stats]')
print(' min/max:', int(selected_count_by_day.min()), int(selected_count_by_day.max()))
print(' mean:', float(selected_count_by_day.mean()))
print(' p10/p50/p90:', np.quantile(selected_count_by_day.astype(float), [0.1, 0.5, 0.9]))
# 3) 条件 -> mask(研究口径)
mask_long = hp2.where(cond_long)
mask_short = hp2.where(cond_short)
# 4) 组合聚合 + benchmark
pf_long = hp2.portfolio(
htypes='close',
mode='equal',
mask=mask_long,
benchmark=benchmark,
benchmark_output='tag_along',
new_share_name='LONG',
)
pf_short = hp2.portfolio(
htypes='close',
mode='equal',
mask=mask_short,
benchmark=benchmark,
benchmark_output='tag_along',
new_share_name='SHORT',
)
# 5) cum_return -> CAGR
def _years_between(hdates) -> float:
idx = pd.DatetimeIndex(hdates)
days = (idx[-1] - idx[0]).days
return max(1e-9, days / 365.25)
def _cagr_from_cumret(cumret_end: float, years: float) -> float:
return (1.0 + cumret_end) ** (1.0 / years) - 1.0
years = _years_between(pf_long.hdates)
cr_long = pf_long.cum_return(htypes='close', method='simple')
cr_short = pf_short.cum_return(htypes='close', method='simple')
cumret_long_end = float(cr_long.values[cr_long.shares.index('LONG'), -1, 0])
cumret_short_end = float(cr_short.values[cr_short.shares.index('SHORT'), -1, 0])
cumret_bm_end = float(cr_long.values[cr_long.shares.index(benchmark), -1, 0])
summary = pd.DataFrame(
{
'cum_return_end': [cumret_long_end, cumret_short_end, cumret_bm_end],
'CAGR': [
_cagr_from_cumret(cumret_long_end, years),
_cagr_from_cumret(cumret_short_end, years),
_cagr_from_cumret(cumret_bm_end, years),
],
},
index=['LONG', 'SHORT', benchmark],
)
print('\\n[CAGR summary]')
print(summary)
# 6) 图:组合对比(归一化更直观)
fig = pf_long.normalize(htypes='close', base_index=0).plot(interactive=True, highlight='max')
return {
'hp': hp,
'hp2': hp2,
'pf_long': pf_long,
'pf_short': pf_short,
'summary': summary,
'fig': fig,
}
res = demo_horizontal_multifactor(
shares=[
'000001.SZ', '600519.SH', '300750.SZ', '000333.SZ', '600036.SH',
'601318.SH', '002415.SZ', '000858.SZ', '600276.SH', '000725.SZ',
'000300.SH',
],
benchmark='000300.SH',
start='20220101',
end='20221231',
)
res['fig']
5.11. 9. 小结与边界
En este punto, hemos completado el ciclo de investigación para la «selección de acciones transversal y multifactorial». Es necesario enfatizar nuevamente: portfolio/cum_return es una agregación burda orientada a la investigación y no incluye una semántica real de ejecución comercial. Si desea migrar esta lógica de detección a una prueba retrospectiva de estrategia, se recomienda generar las condiciones/factores como señales de estrategia y entregárselas al Operator/Backtester para manejar los detalles de la capa comercial.
5.12. Apéndice: índice de figuras (recomendado generar/captura de pantalla en el Notebook)
Figura |
Ubicación sugerida |
lo que veras |
|---|---|---|
|
§0 |
El gráfico mínimo ejecutable con más acciones |
|
§0.5 o §6 |
Comparación de curvas normalizadas de |
|
§7 |
Ejemplo de cómo resaltar un «punto clave» (por ejemplo, el punto máximo) |