P17 · Stochastic RSI¶
| Campo | Valor |
|---|---|
| Familia | Reversión · desde extremos del oscilador, confirmada por cruce de líneas K/D; sale a plano y nunca invierte |
| Features requeridas | rsi (ya registrada) y stochrsi (primitiva nueva de fase B, entrada close, salidas k, d, dependencia rsi). El catálogo nombra las etapas rsi, rolling_rsi_range, stoch_k, stoch_d; la especificación las agrupa en esas dos primitivas |
| Parámetros y defaults | rsi_period = 14, stoch_period = 14, smooth_k = 3, smooth_d = 3 (fijo); niveles 20/80 son constantes de la receta |
| Rejilla del catálogo | rsi_period ∈ {7, 14, 21}, stoch_period ∈ {7, 14, 21}, smooth_k ∈ {2, 3, 5} → 27 candidatos válidos; 3 curvas RSI y 27 pares (K, D) |
variant_keys |
rsi_period, stoch_period, smooth_k (smooth_d no está en la rejilla) |
| Restricciones | enteros ≥ 1, stoch_period >= 2 y smooth_d == 3 (si no, PolicyParamError) |
first_decision_index |
rsi_period + stoch_period + smooth_k + smooth_d − 3 = 31 con defaults (47 en el peor caso de la rejilla) |
| Activos y timeframes | NVDA y KO en los 7 TF (1min … 1d); AMZN, INTC, MSFT, TSLA en 1d; BME y FX en 1d con los calendarios de WP-01b. Solo consume close |
| 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 'stochrsi'". El Stochastics de Nautilus es un estocástico sobre precio, otro indicador, y no se usa ni como variante |
Fuentes: docs/estrategias/P17.md (especificación), catalog/strategy_catalog.json, docs/DATOS_POR_ESTRATEGIA.md, runs/parity/summary.json.
Qué vas a aprender¶
- Qué es un "indicador de indicador": el estocástico aplicado al RSI, y por qué su calentamiento suma etapas.
- Cómo se combinan un filtro de nivel (K < 20, K > 80) y un cruce de dos líneas (K frente a D) para entrar.
- Por qué la salida de un largo coincide con la mitad de la entrada corta y cómo la regla "no reabrir en la misma decisión" impide que P17 se convierta en un sistema de inversión continua.
- Por qué la escala 0..100 frente a 0..1 es el riesgo número uno de esta receta.
- Cómo se reparten 27 candidatos entre 3 RSI compartidos y 27 cadenas propias.
La idea en una frase¶
Cuando el RSI está en la parte baja de su propio rango reciente y la línea K empieza a girar hacia arriba cruzando a D, entrar largo (simétrico en la parte alta para el corto); salir en el primer cruce contrario.
Intuición de mercado¶
El RSI ya es un oscilador, pero en muchos activos pasa semanas entre 40 y 60 sin tocar 30 ni 70. El StochRSI pregunta otra cosa: dónde está el RSI dentro de su propio rango de las últimas stoch_period barras. Un 0 significa "el RSI está en su mínimo reciente"; un 100, "en su máximo". Eso lo hace mucho más sensible que el RSI. K suaviza ese valor y D suaviza K; el cruce de K sobre D indica que el suavizado rápido ha girado respecto al lento.
La receta exige las dos cosas a la vez: estar en un extremo (K < 20 o K > 80) y que K cruce a D en la dirección de la vuelta. Es una apuesta de reversión con confirmación de giro.
Analogía. Un péndulo: no lo empujas cuando está en el extremo, sino cuando ya ha empezado a volver. El nivel dice "está en el extremo"; el cruce dice "ya ha empezado a volver".
Por qué podría funcionar. A corto plazo, los excesos respecto a la volatilidad reciente tienden a corregirse en muchos activos; la confirmación por cruce evita comprar mientras la caída continúa.
Cuándo falla.
- Tendencias fuertes: el RSI puede quedarse pegado arriba; los cruces bajistas con K > 80 abren cortos contra la tendencia.
- Sin stop ni salida por tiempo: una posición abierta solo se cierra con el cruce contrario. Si K y D no vuelven a cruzarse, sigue abierta.
- Filtro muy restrictivo: con periodos cortos la mayoría de los cruces no llegan a entrada. La propia especificación encontró que solo 11 de 192.637 series aleatorias de 20 barras producían entrada larga y corta a la vez (comprobación informal).
Sesgos típicos al evaluarla.
- Pocas operaciones: un candidato con 5 trades en diez años tiene métricas sin valor estadístico.
- Escala mal adaptada: un K en 0..1 comparado con 20 da "K < 20" siempre; la estrategia entraría larga en cada cruce.
- Suavizados cambiados: EMA en lugar de SMA desplaza todos los cruces.
Las reglas exactas¶
La regla canónica del catálogo: "Stoch=100·(RSI−min_RSI)/(max_RSI−min_RSI); K=SMA(Stoch,3), D=SMA(K,3). Entrar largo en cruce K>D con K<20; corto en cruce K
Cruces (convención de dos series): cross_up[t] = k[t] > d[t] y k[t−1] <= d[t−1]; cross_down[t] = k[t] < d[t] y k[t−1] >= d[t−1].
| Estado previo | Situación en el cierre de t |
Objetivo | Motivo (reason) |
|---|---|---|---|
| — | t < first_decision_index |
0 | WARMUP |
| plano 0 | cross_up y k[t] < 20 |
+1 | ENTRY |
| plano 0 | cross_down y k[t] > 80 |
−1 | ENTRY |
| plano 0 | cualquier otro caso (cruce sin nivel, nivel sin cruce) | 0 | HOLD |
| largo +1 | cross_down (sin filtro de nivel) |
0 | EXIT |
| corto −1 | cross_up (sin filtro de nivel) |
0 | EXIT |
| en posición | sin cruce contrario | se conserva | HOLD |
| cualquiera | barra no operable | se congela el publicado; el estado interno avanza | NOT_TRADABLE |
- Entrada: cruce y nivel, los dos con
k[t]de la misma barra; desigualdades estrictas (k == 20no entra). - Salida: solo cruce contrario, sin filtro de nivel.
- Reversión: prohibida.
cross_downcierra un largo y es a la vez la primera mitad de una entrada corta; tras salir, la barra termina. - Empates:
k[t] == d[t]no es cruce;k[t−1] == d[t−1]sí permite el cruce ent.
# decisión en el cierre de t, fill en el open de t+1
target_prev = 0
para cada barra t:
si t < first_decision_index: target = 0 # WARMUP
up = k[t] > d[t] y k[t-1] <= d[t-1]
down = k[t] < d[t] y k[t-1] >= d[t-1]
si target_prev == +1: target = 0 si down si no +1 # salida primero
si no, si target_prev == -1: target = 0 si up si no -1
si no: # desde plano
si up y k[t] < 20: target = +1
si no, si down y k[t] > 80: target = -1
si no: target = 0
si target != target_prev:
orden(target - target_prev) -> fill a open[t+1] # siempre ±1
target_prev = target
Sin el return temprano, P17 se convierte en otra estrategia
Estando largo con K > 80, un cross_down cumple a la vez la salida y la entrada corta. Si el código evaluara la entrada tras salir en la misma barra, emitiría un delta de 2: inversión continua. La especificación lo considera el error más probable de esta receta y tiene un test dedicado (test_p17_never_inverts).
Los indicadores que usa¶
| Etapa | Fórmula | first_valid (con defaults) |
|---|---|---|
rsi |
Wilder: RMA de ganancias y pérdidas con semilla SMA de las primeras rsi_period diferencias; escala 0..100 |
rsi_period = 14 |
stoch (interna) |
ventana inclusiva rsi[t−stoch_period+1 .. t]; 100·(rsi − min)/(max − min), o 50 si max == min |
+ stoch_period − 1 = 27 |
stochrsi.k |
SMA(stoch, smooth_k) |
+ smooth_k − 1 = 29 |
stochrsi.d |
SMA(k, smooth_d) |
+ smooth_d − 1 = 31 |
first_decision_index = first_valid(d) = 31, sin sumar 1 por el t−1 del cruce (regla D-12): en la barra 31, d[30] es NaN, la comparación da False y la decisión legítima es 0 (HOLD, no WARMUP). La primera entrada posible es la 32. Con la rejilla va de 16 (7, 7, 2) a 47 (21, 21, 5).
La ventana del Stoch incluye la barra t, al contrario que la del Donchian de P11: así min <= rsi[t] <= max y 0 <= stoch <= 100 por construcción. El caso max == min da 50, no NaN, para no matar smooth_k + smooth_d − 2 barras por cada tramo plano. Detalle en Las 23 primitivas de fase B y el RSI paso a paso en Indicadores paso a paso.
Ejemplo barra a barra¶
Fixture §6 de docs/estrategias/P17.md: 20 barras, parámetros fuera de rejilla a propósito para que la cadena quepa: rsi_period = 3, stoch_period = 3 (emiten PolicyParamWarning), smooth_k = 3, smooth_d = 3. cash_inicial = 1.000,00, tick 0,01, sin costes. first_valid: rsi 3, stoch 5, k 7, d 9 → first_decision_index = 9.
Se muestran las barras 7 a 19. Las barras 0–6 son warmup con objetivo 0; sus cierres son 10,00; 9,70; 9,85; 9,55; 9,25; 9,15; 8,85 (RSI 20,0000 en la 3, 12,5000 en la 4, 10,5263 en la 5, 6,1538 en la 6; Stoch 0,0000 en la 5 y la 6).
| t | O | H | L | C | RSI(3) | Stoch | K | D | target | orden | fill | pos. | PnL acum. sin costes | equity con 5 bps* |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 7 | 8,85 | 9,05 | 8,75 | 8,95 | 22,2930 | 100,0000 | 33,3333 | NaN | 0 WARMUP | — | — | 0 | 0,00 | 1.000,00 |
| 8 | 8,95 | 9,05 | 8,65 | 8,75 | 14,7059 | 52,9894 | 50,9965 | NaN | 0 WARMUP | — | — | 0 | 0,00 | 1.000,00 |
| 9 | 8,75 | 8,90 | 8,65 | 8,80 | 24,3596 | 100,0000 | 84,3298 | 56,2199 | 0 HOLD | — | — | 0 | 0,00 | 1.000,00 |
| 10 | 8,80 | 9,05 | 8,70 | 8,95 | 49,8843 | 100,0000 | 84,3298 | 73,2187 | 0 HOLD | — | — | 0 | 0,00 | 1.000,00 |
| 11 | 8,95 | 9,05 | 8,75 | 8,85 | 37,2981 | 50,6902 | 83,5634 | 84,0743 | −1 ENTRY | SELL 1 | — | 0 | 0,00 | 1.000,00 |
| 12 | 8,85 | 9,00 | 8,75 | 8,90 | 47,2752 | 79,2706 | 76,6536 | 81,5156 | −1 | — | 8,85 | −1 | −0,05 | 999,95 |
| 13 | 8,90 | 9,20 | 8,80 | 9,10 | 73,0270 | 100,0000 | 76,6536 | 78,9569 | −1 | — | — | −1 | −0,25 | 999,75 |
| 14 | 9,10 | 9,30 | 9,00 | 9,20 | 80,2585 | 100,0000 | 93,0902 | 82,1325 | 0 EXIT | BUY 1 | — | −1 | −0,35 | 999,65 |
| 15 | 9,20 | 9,30 | 9,00 | 9,10 | 57,2394 | 0,0000 | 66,6667 | 78,8035 | 0 (cruce sin nivel) | — | 9,20 | 0 | −0,35 | 999,65 |
| 16 | 9,10 | 9,20 | 9,00 | 9,10 | 57,2394 | 0,0000 | 33,3333 | 64,3634 | 0 | — | — | 0 | −0,35 | 999,65 |
| 17 | 9,10 | 9,20 | 8,80 | 8,90 | 24,9882 | 0,0000 | 0,0000 | 33,3333 | 0 (nivel sin cruce) | — | — | 0 | −0,35 | 999,65 |
| 18 | 8,90 | 9,05 | 8,80 | 8,95 | 38,0729 | 40,5711 | 13,5237 | 15,6190 | 0 (nivel sin cruce) | — | — | 0 | −0,35 | 999,65 |
| 19 | 8,95 | 9,05 | 8,75 | 8,85 | 24,9936 | 0,0412 | 13,5375 | 9,0204 | +1 ENTRY | BUY 1 | sin fill | 0 | −0,35 | 999,65 |
OHLC, RSI, Stoch, K, D, objetivos y fills son literales del fixture (§6.1, §6.2, §6.3 y §6.5). El fixture da la serie de equity de las barras 10 a 19; en las barras 7–9 la posición es 0 y el PnL es trivialmente 0 (cálculo de este manual). *"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: fill 1 → 8,85 · 0,0005 = 0,004425 → 0,00; fill 2 → 9,20 · 0,0005 = 0,0046 → 0,00. Las dos comisiones redondean a cero: con 1 unidad de un precio inferior a 10, 5 bps no llegan a medio céntimo. Con el initial_cash de 100.000 de la API y precios reales, las comisiones sí aparecen.
Lo que hay que ver en la tabla:
- Barras 7–8: K ya existe, D no. Barra 9: primera decisión (D-12); el cruce necesita
d[8], que es NaN, así que el objetivo es 0. - Barra 10: K sigue por encima de D y también lo estaba en la 9: no hay cruce.
- Barra 11: K cae bajo D (
83,5634 < 84,0743) viniendo de arriba (84,3298 >= 73,2187) yK = 83,56 > 80→ corto. Fill al open de 12 (8,85). - Barra 14: K cruza sobre D (
93,0902 > 82,1325; antes76,6536 <= 78,9569) → sale del corto, aunque K esté en 93: la salida no lleva filtro de nivel. Fill de 1 unidad al open de 15 (9,20). El corto pierde 0,35. - Barra 15:
cross_downcon estado plano, peroK = 66,67no supera 80: cruce sin nivel, no entra. - Barras 17–18: K < 20, pero sin cruce hacia arriba: nivel sin cruce, no entra.
- Barra 19: cruce hacia arriba (
13,5375 > 9,0204; antes13,5237 <= 15,6190) conK < 20→ largo. No hay barra 20: sin fill, posición final 0, equity final 999,65.
Las cuentas de las barras clave¶
RSI semilla (t=3): difs = -0,30 ; +0,15 ; -0,30
avg_gain = (0 + 0,15 + 0)/3 = 0,05 ; avg_loss = (0,30 + 0 + 0,30)/3 = 0,20
rsi[3] = 100 - 100/(1 + 0,25) = 20,0000
Primer Stoch (t=5): ventana rsi[3..5] = 20,0000 ; 12,5000 ; 10,5263
stoch[5] = 100·(10,5263 - 10,5263)/9,4737 = 0,0000 (el RSI es el mínimo)
Primer K y D: k[7] = (0 + 0 + 100)/3 = 33,3333
k[9] = (100 + 52,9894 + 100)/3 = 84,3298
d[9] = (33,3333 + 50,9965 + 84,3298)/3 = 56,2199
Entrada corta (t=11):
stoch[11] = 100·(37,2981 - 24,3596)/(49,8843 - 24,3596) = 50,6902
k[11] = (100 + 100 + 50,6902)/3 = 83,5634
d[11] = (84,3298 + 84,3298 + 83,5634)/3 = 84,0743
83,5634 < 84,0743 y 84,3298 >= 73,2187 y 83,5634 > 80 -> -1 ENTRY
Salida (t=14): k[14] = (79,2706 + 100 + 100)/3 = 93,0902
d[14] = (76,6536 + 76,6536 + 93,0902)/3 = 82,1325
93,0902 > 82,1325 y 76,6536 <= 78,9569 -> 0 EXIT
Caja: 1.000,00 + 8,85 = 1.008,85 (open[12]) ; - 9,20 = 999,65 (open[15])
Un empate real. En las barras 15 y 16 los cierres son iguales (9,10 y 9,10): la diferencia es 0, los RMA de ganancias y pérdidas se contraen igual y rsi[15] == rsi[16] == 57,2394 exactamente. El Stoch vale 0 en las dos porque el RSI actual es el mínimo de su ventana.
Este fixture no sirve para probar el fill en la apertura siguiente
Sus barras están encadenadas (open[t+1] == close[t]), así que un fill a close[t] y uno a open[t+1] dan el mismo precio. La especificación usa otro fixture con gap para ese test.
Diagrama¶
flowchart LR
W(["WARMUP: t < first_valid(D)"]) --> FLAT["Plano 0"]
FLAT -->|"K cruza sobre D y K < 20 · BUY 1"| L["Largo +1"]
FLAT -->|"K cruza bajo D y K > 80 · SELL 1"| S["Corto -1"]
L -->|"K cruza bajo D · SELL 1"| FLAT
S -->|"K cruza sobre D · BUY 1"| FLAT
L -->|"sin cruce bajista"| L
S -->|"sin cruce alcista"| S
FLAT -->|"cruce sin nivel o nivel sin cruce"| FLAT
No hay flecha entre largo y corto: tras la salida, la entrada contraria solo puede darse en una barra posterior.
Cómo lanzarla¶
Backtest con ambos motores sobre KO diario (2015–2024), con los defaults (forma "inline" de la API de backtesting; el resto del payload como examples/api/01_backtest_p01_both.request.json):
BASE=http://localhost:8000
SID=$(curl -sS -X POST "$BASE/v1/backtest" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p17-0001",
"correlation_id": null,
"schema_version": "v1",
"payload": {
"strategy": {"template_id": "P17",
"params": {"rsi_period": 14, "stoch_period": 14, "smooth_k": 3, "smooth_d": 3},
"name": "StochRSI 14/14/3/3 base", "register": true},
"data": {"asset_id": "KO", "timeframe": "1d",
"window": {"start": "2015-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": "P17",
"params": {"rsi_period": 14, "stoch_period": 14,
"smooth_k": 3, "smooth_d": 3},
"register": True},
"data": {"asset_id": "KO", "timeframe": "1d",
"window": {"start": "2015-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"])
for res in r["results"]:
print(res["adapter"], res["run_metrics"]["final_equity"], res["run_metrics"]["n_fills"])
Con KO espera el aviso DATA_GAP_SESSIONS (271 sesiones ausentes). No pidas indicator_provider: "native" con NT_ONLINE: es UNSUPPORTED.
Derivar una variante con una ventana de estocástico más larga:
curl -sS -X POST "$BASE/v1/strategies/$SID/derive" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p17-derive", "correlation_id": null, "schema_version": "v1",
"payload": {"param_overrides": {"stoch_period": 21}, "name": "StochRSI 14/21/3/3",
"slug": "stochrsi-14-21-3-3", "tags": ["sensibilidad"]}}' | jq '.payload.strategy_id, .payload.cache_forecast'
stoch_period es parámetro de feature: según §5.3 de la especificación hay que rehacer stochrsi (Stoch, K y D), la política y el ledger, pero rsi(14) se reutiliza entero porque es una dependencia declarada. El cache_forecast debe anunciar el fallo de stochrsi y el acierto de rsi. Cambiar rsi_period, en cambio, invalida toda la cadena; y smooth_d no puede derivarse a otro valor que 3 (PolicyParamError).
Sweep de sus variant_keys (rejilla completa del catálogo, con la forma de 03_sweep_p01_4x4.request.json):
curl -sS -X POST "$BASE/v1/sweep" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p17-sweep", "correlation_id": null, "schema_version": "v1",
"payload": {"base_strategy_id": "'"$SID"'",
"grid": {"rsi_period": [7, 14, 21], "stoch_period": [7, 14, 21], "smooth_k": [2, 3, 5]},
"candidate_policy": {"drop_invalid": true, "drop_duplicates": true,
"register_strategies": true,
"name_template": "{template_name} {rsi_period}/{stoch_period}/{smooth_k}"},
"data": {"asset_id": "KO", "timeframe": "1d",
"window": {"start": "2015-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, 27 válidos; 3 curvas RSI compartidas (9 candidatos cada una) y 27 curvas stochrsi propias. El resultado se consulta en GET /v1/jobs/{job_id}.
Cuánto cuesta la rejilla¶
| Concepto | Cuenta |
|---|---|
| Candidatos cartesianos | 3 × 3 × 3 = 27 (smooth_d fijo no multiplica) |
| Inválidos | 0 (todos con stoch_period >= 2 y smooth_d = 3) |
| Curvas RSI | 3, factor 9× |
| Series Stoch sin suavizar | 9 combinaciones (rsi_period, stoch_period), pero no se comparten: la primitiva tiene cuatro parámetros y el planificador ve 27 specs. Partirla en dos daría otro 3×; la especificación decide no hacerlo porque el Stoch es mucho más barato que el RSI |
| Pares (K, D) | 27, uno por candidato |
| Entre estrategias | rsi(14) es exactamente la clave que pide P02: en un barrido conjunto se calcula una vez |
La factorización es parcial: buena en la raíz, nula en las hojas. Queda entre P14 (12 → 12) y P16 (36 → 6).
Qué mirar en los resultados¶
parity.verdictenNT_FEATURES: PASS en la matriz.- Cruces totales frente a entradas: mide cuánto filtra
K < 20/K > 80. La mayoría de cruces no deberían producir entrada. - Número de trades y duración media: la duración puede ser muy larga, porque no hay stop.
- Entradas largas frente a cortas: una asimetría fuerte sugiere un problema de escala o de niveles.
- Fills con
qty == 2: deben ser exactamente 0. - Fracción de barras con
stoch == 50por rango nulo: si es 0 en toda la muestra, esa regla solo está probada con datos sintéticos. - Salidas retrasadas por barras no operables: con la mecánica D-10 la salida no se pierde, pero se retrasa hasta la primera barra operable; hay que medir ese retraso y su deslizamiento de precio.
- Contador de caché del sweep: 27 candidatos, 3 cálculos de RSI.
Causalidad comprobada¶
El test de futuros alterados multiplica por 1,4 close desde la barra 300 de una serie de 400 y exige igualdad bit a bit de rsi, k, d, objetivos y fills hasta la barra 250. El complemento propio de P17 prueba la ventana: perturbando solo close[300], rsi[299] y stoch[299] no pueden cambiar. Si stoch[299] cambia, la ventana mira hacia delante: el error típico al alinear un sliding_window_view por el inicio de la ventana en vez de por el final.
El checkpoint para reanudar en modo continue es el más grande de su grupo: los dos RMA del RSI (avg_gain, avg_loss; rsi[T] solo no basta), el último cierre, los stoch_period últimos RSI, los smooth_k − 1 últimos Stoch, los smooth_d − 1 últimos K, k[T] y d[T] para el cruce y el objetivo vigente: 24 números con los defaults.
Errores típicos¶
| Error | Consecuencia | Cómo evitarlo |
|---|---|---|
stoch_period = 1 |
PolicyParamError: rango siempre 0, Stoch siempre 50, K == D, ningún cruce |
stoch_period >= 2 |
smooth_d != 3 |
PolicyParamError: la rejilla del catálogo lo fija en 3 |
Barrer smooth_d exige cambiar la entrada de catálogo |
| K en escala 0..1 | K < 20 siempre cierto: largo en cada cruce alcista |
Escala 0..100, verificada antes de emitir señales |
| Reabrir en la barra de salida | Inversión (fill de 2), prohibida | Tras salir, la barra termina |
| Filtro de nivel en la salida | Posiciones que no se cierran nunca | La salida es solo cruce contrario |
| EMA en lugar de SMA en K o D | Cruces desplazados | El catálogo dice SMA |
| Ventana desplazada (como Donchian) | Stoch fuera de [0, 100] |
Ventana inclusiva rsi[t−n+1 .. t] |
| Rango nulo como NaN | Barras muertas en cadena | Rango nulo da 50 |
Usar Stochastics de Nautilus o vector_ta.stoch |
Otro indicador (estocástico sobre precio) | El StochRSI se aplica al RSI |
Creer que está lista en rsi_period |
17 barras de señales espurias con defaults | La cadena suma: 31 |
Variantes y extensiones¶
- Niveles parametrizados (25/75, 10/90): serían parámetros de política;
stochrsise reutilizaría entero. - Salida con filtro (cerrar solo si el cruce ocurre en la zona contraria): es otra receta, con posiciones más largas.
- Stop o salida por tiempo: el catálogo no los incluye; añadirlos es otra estrategia.
- Solo largos:
target_lots = [0, 1]. - Parientes: P02 RSI (reversión con el RSI directamente), P09 WaveTrend (otro oscilador con cruce de líneas) y P03 MACD (cruce de una línea con su señal).
Resumen¶
- El StochRSI sitúa el RSI dentro de su propio rango reciente (0..100); K y D son dos SMA encadenadas.
- Entra con cruce K/D y nivel extremo; sale con el cruce contrario, sin nivel; nunca invierte.
- El calentamiento suma etapas: 31 barras con defaults, frente a las 14 del RSI solo.
- En el fixture: corto a 8,85, salida a 9,20 (−0,35) y una última entrada larga sin fill; con 1 unidad, 5 bps redondean a 0.
- 27 candidatos comparten 3 RSI; el resto de la cadena es propia de cada candidato.
Para practicar¶
- Con el fixture, calcula a mano
k[12]yd[12]:k[12] = (stoch[10] + stoch[11] + stoch[12])/3con 100,0000, 50,6902 y 79,2706, yd[12]conk[10..12]. Comprueba 76,6536 y 81,5156 y que no hay cruce alcista (76,6536 > 81,5156es falso). - Repite la columna "equity con 5 bps" suponiendo 100 unidades en lugar de 1. ¿Cuánto pagan ahora los dos fills y cuál es la equity final con cash inicial 1.000,00?
- Lanza el sweep de 27 candidatos sobre KO 1d y cuenta, para cada
smooth_k, cuántos trades hace el candidato14/14. Comprueba enfactorization_planque aparecen 3 curvas RSI.