P09 · WaveTrend¶
| Campo | Valor |
|---|---|
| Familia | Reversión · oscilador de canal normalizado con línea de señal; entra en extremos al cruce de wt1 sobre wt2 y sale al cruce contrario |
| Features requeridas | wavetrend (salidas wt1, wt2), que depende de hlc3 y de la cadena de EMA de parámetro channel |
| Parámetros y defaults | channel = 10, average = 21, level = 60 |
| Rejilla del catálogo | channel ∈ {7, 10, 14}, average ∈ {14, 21, 28}, level ∈ {50, 60, 70} → 27 candidatos válidos, 9 curvas wavetrend y 1 hlc3 |
variant_keys |
channel, average, level |
| Restricciones | channel entero ≥ 2, average entero ≥ 2, level > 0 (si no, PolicyParamError); sin restricción de orden entre channel y average; level no se valida contra 100 |
first_decision_index |
2·(channel − 1) + (average − 1) + 3 = 41 con defaults (56 en el peor caso de la rejilla) |
| Activos y timeframes | NVDA y KO en los 7 TF; AMZN, INTC, MSFT, TSLA en 1d; BME en 1d con reserva (calendario XMAD); FX con reserva grave (hlc3 contaminado por OHLC inválidos y domingos) |
| Paridad en la matriz | PASS en NT_FEATURES, NT_INTENT_REPLAY y NT_ONLINE(vectorta) (12/12 cada una). UNSUPPORTED en NT_ONLINE(native) (12/12): "nautilus_trader 1.231.0 no ofrece un indicador nativo para 'wavetrend'" |
Fuentes: docs/estrategias/P09.md (especificación), catalog/strategy_catalog.json, docs/DATOS_POR_ESTRATEGIA.md, runs/parity/summary.json.
Qué vas a aprender¶
- Cómo se encadenan cuatro suavizados (tres EMA y una SMA de 4) para construir un oscilador normalizado, y por qué su warmup se suma.
- Qué significa el
0.015de la fórmula y por quélevel = 60no es "60 sobre 100". - A combinar un cruce de dos series con un filtro de nivel evaluado en la misma barra.
- Por qué P09 nunca invierte y la salida no mira el nivel.
- Por qué cambiar
leveles gratis en caché y cambiaraverageno.
La idea en una frase¶
Cuando el oscilador está en una zona extrema y su línea rápida cruza la lenta hacia el centro, apuesta por la vuelta; sal en cuanto se crucen al revés.
Intuición de mercado¶
WaveTrend (LazyBear, a partir del Channel Index de Donald Lambert) mide cuánto se aleja el precio típico de su media, en unidades de la desviación media reciente. Es la misma idea que el CCI: la constante 0.015 hace que la mayor parte de los valores caigan entre −100 y +100. wt1 es el oscilador suavizado; wt2, su media de 4 barras. Cuando wt1 está muy abajo (sobreventa) y empieza a girar por encima de wt2, el movimiento bajista pierde fuerza.
Analogía. Una goma elástica: cuanto más se estira (más lejos de su media, en unidades de su "estiramiento habitual"), más probable es que vuelva. Pero no se apuesta mientras se sigue estirando: se espera a ver que empieza a volver (el cruce).
Por qué podría funcionar. Los excesos a corto plazo tienden a corregirse, y exigir el cruce evita entrar contra un movimiento que todavía acelera.
Cuándo falla.
- Tendencias fuertes: el oscilador se queda en la zona extrema y cada cruce hacia el centro es una trampa.
- Series casi planas (
1minen horas muertas, barras de volumen cero tras un cierre anticipado): la desviación mediadtiende a 0 ycise dispara o cae en la rama0/0. - Huecos: la recursión continúa a través del hueco (KO tiene 271 sesiones ausentes); la barra siguiente puede producir un
cienorme y una entrada espuria.
Sesgos típicos al evaluarla.
- Leer
levelcomo un porcentaje:cino está acotado; valores de ±150 son posibles. - Tolerancias en los cruces: una diferencia de 1e−12 en
wt1cerca de un cruce cambia la estrategia; las señales se comparan exactas. - Pocos trades: con
level = 70en1dpuede haber muy pocas entradas; mira el número de trades antes que cualquier ratio.
Las reglas exactas¶
La regla canónica del catálogo: "AP=HLC3; ESA=EMA(AP,n1); D=EMA(abs(AP-ESA),n1); CI=(AP-ESA)/(0.015·D); WT1=EMA(CI,n2); WT2=SMA(WT1,4). Desde plano, largo al cruce WT1 sobre WT2 si WT1<−level, corto simétrico si WT1>level. Salir al cruce contrario, sin inversión inmediata."
Cruces con la convención del proyecto (§5.1 del informe, D-09): cross_up[t] = wt1[t] > wt2[t] y wt1[t−1] <= wt2[t−1]; cross_down simétrico. wt2 es una serie, no un nivel, así que no sirve base.crossed_up.
Situación en el cierre de t |
Objetivo target[t] |
Motivo (reason) |
|---|---|---|
t < first_decision_index |
0 | WARMUP |
largo y cross_down[t] |
0 | EXIT (y termina la decisión) |
corto y cross_up[t] |
0 | EXIT (y termina la decisión) |
plano, cross_up[t] y wt1[t] < −level |
+1 | ENTRY |
plano, cross_down[t] y wt1[t] > level |
−1 | ENTRY |
cruce sin extremo, empate wt1 == wt2, o NaN |
el anterior | HOLD |
| barra no operable | se congela el publicado | NOT_TRADABLE |
- Entrada: solo desde plano; el nivel se exige en la misma barra del cruce y con desigualdad estricta (
wt1 == −60no entra). - Salida: al cruce contrario, sin condición de nivel.
- Reversión: prohibida ("sin inversión inmediata"). Una barra que cierra un largo y cumpliría la entrada corta deja el objetivo en 0; el ledger nunca ve un delta de 2.
- Excepción
d = 0: antes de inicializard,cies NaN; después,0/0daci = 0yx/0conx ≠ 0da NaN y pausa la cadena (la política conserva el objetivo).
# decisión en el cierre de t, fill en el open de t+1
state = 0
para cada barra t:
si t < first_decision_index: target = 0 # WARMUP
si no:
up = wt1[t] > wt2[t] y wt1[t-1] <= wt2[t-1]
down = wt1[t] < wt2[t] y wt1[t-1] >= wt2[t-1]
si state == +1: si down: state = 0 # salida y FIN
si no, si state == -1: si up: state = 0 # salida y FIN
si no: si up y wt1[t] < -level: state = +1 # entrada larga
si no, si down y wt1[t] > level: state = -1 # entrada corta
target = state
si target != target_prev: orden(target - target_prev) -> fill a open[t+1]
La semilla de d usa los primeros valores válidos de dev
dev = |ap − esa| solo existe desde channel − 1. Sembrar d con los índices 0..n1−1 produce números plausibles y falsos para siempre (es una EMA). Es lo que hace que el warmup de la cadena sume: esa en n1−1, d en 2n1−2, wt1 en 2n1−2 + n2−1, wt2 tres barras después.
Los indicadores que usa¶
| Feature | Fórmula | first_valid (defaults 10/21) |
|---|---|---|
hlc3 (ap) |
(H + L + C)/3 |
0 |
esa |
EMA(ap, n1), semilla SMA, α = 2/(n1+1) |
n1 − 1 = 9 |
d |
EMA(|ap − esa|, n1) sembrada con los primeros n1 valores válidos |
2n1 − 2 = 18 |
ci |
(ap − esa)/(0,015 · d) (excepción d = 0 arriba) |
2n1 − 2 = 18 |
wt1 |
EMA(ci, n2) |
2n1 − 2 + n2 − 1 = 38 |
wt2 |
SMA(wt1, 4), ventana hacia atrás e inclusiva (el 4 es fijo) |
wt1 + 3 = 41 |
first_decision_index = first_valid(wt2) (D-12, sin +1). En esa barra wt2[t−1] es NaN y no puede haber cruce: la decisión "no entro" es válida (HOLD), no calentamiento.
VectorTA expone wavetrend y WavetrendStream, pero que exista el nombre no acredita la fórmula: la especificación lista siete comprobaciones (entrada hlc3, constante 0,015, semilla de d, wt2 = SMA(wt1, 4), semillas SMA, comportamiento con d = 0 y NaN, índice del primer valor). Mientras no pasen, la primitiva es custom_reference. Ver Las 23 primitivas de fase B, Indicadores paso a paso e Indicadores y features.
Ejemplo barra a barra¶
Fixture §6 de docs/estrategias/P09.md: channel = 2, average = 2, level = 60 (los dos primeros fuera de rejilla, para que la aritmética quepa a mano), cash_inicial = 1.000,00, una unidad, sin costes. Con α = 2/3: first_valid(esa) = 1, first_valid(d) = first_valid(ci) = 2, first_valid(wt1) = 3, first_valid(wt2) = 6 → first_decision_index = 6. 16 barras con open[t] = close[t−1].
Barras 0–1: WARMUP (O=10,00 H=10,10 L=9,90 C=10,00 en ambas; ap = 10,0000; esa[1] = 10,0000, dev[1] = 0 exacto, pero d aún no es válido, así que ci[1] es NaN: el lado "no antes" de la excepción d = 0).
| t | O | H | L | C | ap | ci | wt1 | wt2 | target | orden | fill | pos. | PnL acum. sin costes* | equity con 5 bps* |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 10,00 | 10,10 | 9,80 | 9,90 | 9,9333 | −133,3333 | — | — | 0 WARMUP | — | — | 0 | 0,00 | 1.000,00 |
| 3 | 9,90 | 10,10 | 9,80 | 10,00 | 9,9667 | 40,0000 | −46,6667 | — | 0 WARMUP | — | — | 0 | 0,00 | 1.000,00 |
| 4 | 10,00 | 10,10 | 9,70 | 9,80 | 9,8667 | −91,2281 | −76,3743 | — | 0 WARMUP | — | — | 0 | 0,00 | 1.000,00 |
| 5 | 9,80 | 9,90 | 9,60 | 9,70 | 9,7333 | −82,4615 | −80,4324 | — | 0 WARMUP | — | — | 0 | 0,00 | 1.000,00 |
| 6 | 9,70 | 9,80 | 9,30 | 9,40 | 9,5000 | −81,1812 | −80,9316 | −71,1013 | 0 HOLD (decisión válida) | — | — | 0 | 0,00 | 1.000,00 |
| 7 | 9,40 | 9,50 | 9,30 | 9,40 | 9,4000 | −62,3501 | −68,5440 | −76,5706 | +1 ENTRY | BUY 1 | — | 0 | 0,00 | 1.000,00 |
| 8 | 9,40 | 9,80 | 9,30 | 9,70 | 9,6000 | 56,2100 | 14,6254 | −53,8207 | +1 HOLD | — | 9,40 | +1 | 0,30 | 1.000,30 |
| 9 | 9,70 | 9,80 | 9,50 | 9,60 | 9,6333 | 49,4864 | 37,8661 | −24,2460 | +1 HOLD | — | — | +1 | 0,20 | 1.000,20 |
| 10 | 9,60 | 9,90 | 9,50 | 9,80 | 9,7333 | 70,5294 | 59,6416 | 10,8973 | +1 HOLD | — | — | +1 | 0,40 | 1.000,40 |
| 11 | 9,80 | 9,90 | 9,50 | 9,60 | 9,6667 | −29,2594 | 0,3743 | 28,1268 | 0 EXIT | SELL 1 | — | +1 | 0,20 | 1.000,20 |
| 12 | 9,60 | 9,70 | 9,30 | 9,40 | 9,4667 | −88,1183 | −58,6208 | 9,8153 | 0 HOLD | — | 9,60 | 0 | 0,20 | 1.000,20 |
| 13 | 9,40 | 9,50 | 9,20 | 9,30 | 9,3333 | −72,0203 | −67,5538 | −16,5397 | 0 HOLD | — | — | 0 | 0,20 | 1.000,20 |
| 14 | 9,30 | 9,40 | 8,90 | 9,00 | 9,1000 | −76,2293 | −73,3375 | −49,7844 | 0 HOLD | — | — | 0 | 0,20 | 1.000,20 |
| 15 | 9,00 | 9,40 | 8,90 | 9,30 | 9,2000 | −0,2307 | −24,5996 | −56,0279 | 0 HOLD (cruce sin extremo) | — | — | 0 | 0,20 | 1.000,20 |
OHLC, ap, ci, wt1, wt2, objetivos y fills son literales del fixture §6 de docs/estrategias/P09.md (4 decimales). *El fixture da la equity en los cierres 8 (1.000,30), 12 (1.000,20) y 15 (1.000,20); las barras 9–11 de la columna "PnL acumulado sin costes" son cálculo de este manual con la misma regla (caja 990,60 + posición · cierre − 1.000,00). "Pos." es la posición tras el fill en la apertura de esa barra.
*"Equity con 5 bps" es cálculo de este manual: escenario hypothetical_5bps (fee_rate = 0,0005, sin spread ni deslizamiento), comisión por fill = |delta| · precio · 0,0005 redondeada al céntimo half-even y restada en la barra del fill:
- fill 1 (t=8):
1 · 9,40 · 0,0005 = 0,0047 → 0,00 - fill 2 (t=12):
1 · 9,60 · 0,0005 = 0,0048 → 0,00
Con precios de 9–10 unidades y una sola unidad, la comisión redondeada al céntimo es cero en ambos fills: la columna coincide con la de sin costes. Es un efecto del tamaño del fixture, no de la estrategia; con 100.000 de capital y precios reales sí pesa.
Lo que hay que ver en la tabla:
- Barra 6: primera decisión válida, pero
wt2[5]es NaN: no puede haber cruce. - Barra 7:
wt1(−68,5440) cruza sobrewt2(−76,5706) viniendo de debajo y está por debajo de −60 → largo. Fill enopen[8] = 9,40(coincide conclose[7]por construcción del fixture; el test comprueba el índice,fill_index = signal_index + 1). - Barra 11:
wt1(0,3743) cruza bajowt2(28,1268): salida, aunque 0,37 no sea ningún extremo. La salida no mira el nivel. - Barra 15: hay
cross_up(−24,5996 > −56,0279 viniendo de debajo), pero −24,5996 no es menor que −60: el filtro delevelrechaza la entrada.
Las cuentas de las barras clave¶
Semillas: ap[0] = ap[1] = (10,10 + 9,90 + 10,00)/3 = 10,000000
esa[1] = (10,000000 + 10,000000)/2 = 10,000000 ; dev[1] = 0
ap[2] = 29,80/3 = 9,933333
esa[2] = (2/3)·9,933333 + (1/3)·10,000000 = 9,955556 ; dev[2] = 0,022222
d[2] = (dev[1] + dev[2])/2 = 0,011111 <- primeros 2 dev VÁLIDOS
ci[2] = -0,022222 / (0,015 · 0,011111) = -133,333333
wt1[3] = (ci[2] + ci[3])/2 = (-133,333333 + 40,000000)/2 = -46,666667
wt2[6] = (wt1[3] + wt1[4] + wt1[5] + wt1[6])/4 = -284,405010/4 = -71,101253
Entrada (t=7):
esa[7] = (2/3)·9,400000 + (1/3)·9,596159 = 9,465386 ; dev[7] = 0,065386
d[7] = (2/3)·0,065386 + (1/3)·0,078967 = 0,069913
ci[7] = -0,065386 / (0,015 · 0,069913) = -62,350120
wt1[7] = (2/3)·(-62,350120) + (1/3)·(-80,931642) = -68,543961
wt2[7] = (-76,374269 - 80,432432 - 80,931642 - 68,543961)/4 = -76,570576
cross_up: -68,5440 > -76,5706 y -80,9316 <= -71,1013 -> V
extremo: -68,5440 < -60 -> V => +1 (ENTRY)
Salida (t=11):
cross_down: 0,3743 < 28,1268 y 59,6416 >= 10,8973 -> V ; estado +1 -> 0 (EXIT)
Caja: 1.000,00 - 9,40 = 990,60 (fill 1, open[8])
990,60 + 9,60 = 1.000,20 (fill 2, open[12])
Diagrama¶
flowchart LR
W(["WARMUP: t < first_decision_index"]) -->|"wt2 válido"| FLAT["Plano 0"]
FLAT -->|"cross_up y wt1 < -level · BUY 1"| L["Largo +1"]
FLAT -->|"cross_down y wt1 > level · SELL 1"| S["Corto -1"]
FLAT -->|"sin cruce, o cruce sin extremo"| FLAT
L -->|"cross_down · SELL 1"| FLAT
S -->|"cross_up · BUY 1"| FLAT
L -->|"sin cross_down"| L
S -->|"sin cross_up"| S
No hay flecha entre largo y corto: toda salida pasa por plano y la barra siguiente decide desde ahí.
Cómo lanzarla¶
Backtest con ambos motores sobre KO 1d con los defaults (estrategia por plantilla y parámetros, forma "inline" de la API de backtesting). En KO espera el aviso DATA_GAP_SESSIONS:
BASE=http://localhost:8000
SID=$(curl -sS -X POST "$BASE/v1/backtest" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p09-0001",
"correlation_id": null,
"schema_version": "v1",
"payload": {
"strategy": {"template_id": "P09", "params": {"channel": 10, "average": 21, "level": 60},
"name": "WaveTrend 10/21/60 base", "register": true},
"data": {"asset_id": "KO", "timeframe": "1d",
"window": {"start": "2010-01-01T00:00:00Z", "end": "2025-01-01T00:00:00Z",
"mode": "reset_flat"}},
"execution": {"lane": "SIM-S", "contract_version": "1", "initial_cash": 100000,
"target_lots": [-1, 0, 1], "cost_scenario": "hypothetical_5bps"},
"engine": "both",
"cache": {"cache_mode": "WARM_FEATURES", "write_through": true},
"output": {"output_mode": "metrics", "include_timings": true,
"include_parity": true, "tail_rows": 5},
"engine_options": {"vectorta": {"kernel": "scalar", "use_batch": true},
"nautilus": {"adapter": "NT_FEATURES"}}
}}' | jq -r '.payload.signature.strategy_id')
echo "$SID"
import uuid, requests
BASE = "http://localhost:8000"
env = lambda payload: {"request_id": str(uuid.uuid4()), "correlation_id": None,
"schema_version": "v1", "payload": payload}
payload = {
"strategy": {"template_id": "P09",
"params": {"channel": 10, "average": 21, "level": 60}, "register": True},
"data": {"asset_id": "KO", "timeframe": "1d",
"window": {"start": "2010-01-01T00:00:00Z",
"end": "2025-01-01T00:00:00Z", "mode": "reset_flat"}},
"execution": {"lane": "SIM-S", "contract_version": "1", "initial_cash": 100000,
"target_lots": [-1, 0, 1], "cost_scenario": "hypothetical_5bps"},
"engine": "both",
"output": {"output_mode": "metrics", "include_parity": True},
"engine_options": {"vectorta": {"kernel": "scalar", "use_batch": True},
"nautilus": {"adapter": "NT_FEATURES"}},
}
r = requests.post(f"{BASE}/v1/backtest", json=env(payload)).json()["payload"]
SID = r["signature"]["strategy_id"]
print(SID, r["parity"]["verdict"])
Derivar una variante con extremos más exigentes:
curl -sS -X POST "$BASE/v1/strategies/$SID/derive" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p09-derive", "correlation_id": null, "schema_version": "v1",
"payload": {"param_overrides": {"level": 70}, "name": "WaveTrend 10/21/70",
"slug": "wavetrend-10-21-70", "tags": ["sensibilidad"]}}' | jq '.payload.strategy_id, .payload.cache_forecast'
level es parámetro de política: no entra en FeatureSpec.params. Según la tabla de invalidación §5.3 de la especificación, cache_forecast debe anunciar wavetrend como acierto íntegro, ningún fallo, y rehacer solo política y ledger (como los umbrales de P02). En cambio, channel y average son de feature: cambiar average rehace la cadena wavetrend entera aunque esa, d y ci no cambien, porque la caché es por FeatureSpec completo; solo hlc3 se reutiliza.
Sweep de sus variant_keys:
curl -sS -X POST "$BASE/v1/sweep" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p09-sweep", "correlation_id": null, "schema_version": "v1",
"payload": {"base_strategy_id": "'"$SID"'",
"grid": {"channel": [7, 10, 14], "average": [14, 21, 28], "level": [50, 60, 70]},
"candidate_policy": {"drop_invalid": true, "drop_duplicates": true,
"register_strategies": true,
"name_template": "{template_name} {channel}/{average}/{level}"},
"data": {"asset_id": "KO", "timeframe": "1d",
"window": {"start": "2010-01-01T00:00:00Z", "end": "2025-01-01T00:00:00Z", "mode": "reset_flat"}},
"execution": {"lane": "SIM-S", "contract_version": "1", "initial_cash": 100000,
"cost_scenario": "hypothetical_5bps"},
"engine": "vectorta", "cache": {"cache_mode": "OPT_FULL"},
"output": {"output_mode": "metrics"},
"engine_options": {"vectorta": {"kernel": "scalar", "use_batch": true}}}}' | jq '.payload.job_id, .payload.factorization_plan'
Según §5.4 de la especificación: 27 candidatos, los 27 válidos, 9 curvas wavetrend y 1 hlc3. Un contador que muestre 27 cálculos de wavetrend es un fallo de factorización; lo correcto son 9 cálculos y 18 aciertos. Resultados en GET /v1/jobs/{job_id}.
Cuánto cuesta la rejilla¶
| Concepto | Cuenta |
|---|---|
| Candidatos cartesianos | 3 channel × 3 average × 3 level = 27 |
| Inválidos | 0 (ni channel/average < 2 ni level ≤ 0 en la rejilla) |
hlc3 |
1 (sin parámetros; compartida con P13) |
Curvas wavetrend |
9 (una por pareja channel, average; level no entra) |
| Si se cachease por eslabón | 3 esa/d/ci (una por channel) y 9 wt1/wt2 |
| Políticas / ledgers | 27 / 27 |
Qué mirar en los resultados¶
- Número de trades y de fills: con
|delta| ≡ 1,fills = 2 × tradessalvo el último trade abierto. Ningún delta de 2. - Fracción de cruces alcistas que el filtro de
levelrechaza: 0 % significa quelevelno hace nada en esa muestra (sospechoso); 100 %, que no hay trades. wt1en las barras de entrada: debe estar fuera de[−level, level]por construcción; si no, hay un bug.- Barras en cada rama de la excepción
d = 0y NaN tardíos enwt1/wt2: deben ser 0 o casi 0 en datos limpios; un recuento alto delata barras planas en el dataset. - Duración media de la posición y exposición overnight (permitida por diseño: P09 no cierra por sesión, a diferencia de P12 y P13).
first_decision_indexefectivo: compara candidatos sobre una ventana común (el peor caso de la rejilla necesita 56 barras).DATA_GAP_SESSIONS: cruzagap_sessions_beforecon las barras de entrada y descarta de las conclusiones (no del cálculo) las que sigan a un hueco grande.- Paridad: señales exactas en los carriles soportados; la especificación prohíbe el Sharpe como criterio de aceptación.
Causalidad comprobada¶
El test de "futuros alterados" de la especificación usa el propio fixture: se sustituyen las barras 12–15 (por ejemplo close × 1,5 con high/low recolocados) y se exige que wt1[0..11], wt2[0..11], targets[0..11] y todos los fills con signal_index ≤ 11 sean idénticos bit a bit. Detecta una EMA centrada, un wt2 centrado (wt2[6] debe usar wt1[3..6], hacia atrás) o un shift con el signo cambiado. Un segundo test recorta la serie en varios k y comprueba que los objetivos hasta k coinciden con los de la serie completa.
El checkpoint para continue cabe en menos de 130 bytes por candidato: el objetivo, wt1_prev y wt2_prev (política), esa_prev, d_prev, el último wt1 y los 4 últimos wt1 para la ventana de wt2 (cadena), y los acumuladores de semilla si el corte cae antes de que algún eslabón esté sembrado. Guardar solo el objetivo inventaría un cruce en la primera barra tras la reanudación.
Errores típicos¶
| Error | Consecuencia | Cómo evitarlo |
|---|---|---|
channel = 1 |
PolicyParamError: esa = ap, d ≡ 0, ci ≡ 0; la estrategia no opera jamás |
La API lo rechaza con mensaje explícito |
level <= 0 |
PolicyParamError: desaparece la noción de extremo |
level positivo; el signo lo pone la regla |
Validar level contra 100 |
Rechazar valores legítimos | ci no está acotado |
Sembrar d con los índices 0..n1−1 |
Números plausibles y falsos para siempre | Semilla con los primeros n1 valores válidos de dev |
Usar base.crossed_up (prev < level and cur >= level) |
Un empate wt1 == wt2 dispararía entrada |
Cruce de dos series: a[t] > b[t] y a[t−1] <= b[t−1] |
| Invertir al cruce contrario | Delta de 2 que la regla prohíbe | Salir a plano; decidir desde plano en la barra siguiente |
| Exigir el nivel también en la salida | Posiciones que no se cierran | La salida es solo el cruce contrario |
wt2 = EMA(wt1, 4) o SMA(wt1, 3) |
Otra estrategia | wt2 = SMA(wt1, 4), el 4 es fijo |
Variantes y extensiones¶
- Solo largos:
target_lots = [0, 1]ignorando la entrada corta. - Filtro de tendencia: operar solo en la dirección de una media larga (P01) para evitar comprar sobreventas en tendencias bajistas.
- Parientes de reversión: P02 RSI (oscilador acotado con umbrales fijos) y P17 Stoch RSI.
- Bandas de volatilidad: P04, reversión a la media con Bollinger.
- Armado con caducidad (entrar si
wt1estuvo en extremo en las últimaskbarras): exige un parámetro nuevo y otra entrada de catálogo.
Resumen¶
- P09 construye un oscilador normalizado (
ci) con tres EMA encadenadas y lo suaviza enwt1, conwt2 = SMA(wt1, 4)como señal. - Entra desde plano cuando
wt1cruzawt2estando más allá de±levelen esa misma barra; sale al cruce contrario sin mirar el nivel; nunca invierte. - El warmup de la cadena se suma: 41 barras con defaults.
- En el fixture: largo a 9,40, salida a 9,60 (equity final 1.000,20); la barra 15 muestra un cruce rechazado por el nivel.
leveles de política (acierto de caché íntegro); 27 candidatos cuestan 9 curvaswavetrend.
Para practicar¶
- Con el fixture, calcula a mano
wt1[8]a partir deci[8] = 56,2100ywt1[7] = −68,5440conα = 2/3, y comprueba 14,6254. Después calculawt2[8]con la ventanawt1[5..8]. - Repite la evaluación de la barra 15 con
level = 20. ¿Habría entrada? ¿En qué barra y a qué precio sería el fill? (Pista: la señal de la última barra no produce fill.) - Lanza el sweep 3×3×3 sobre KO 1d y NVDA 1d. Para
channel = 10, average = 21, compara el número de trades conlevel50, 60 y 70, y comprueba entimingsque las tres variantes comparten la misma curvawavetrend.