Saltar a contenido

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 (1min1d); 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 K80. Salir con cruce contrario. Cuando el rango RSI es cero tras inicialización, Stoch=50."

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 == 20 no entra).
  • Salida: solo cruce contrario, sin filtro de nivel.
  • Reversión: prohibida. cross_down cierra 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 en t.
# 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:

  1. 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.
  2. Barra 10: K sigue por encima de D y también lo estaba en la 9: no hay cruce.
  3. Barra 11: K cae bajo D (83,5634 < 84,0743) viniendo de arriba (84,3298 >= 73,2187) y K = 83,56 > 80 → corto. Fill al open de 12 (8,85).
  4. Barra 14: K cruza sobre D (93,0902 > 82,1325; antes 76,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.
  5. Barra 15: cross_down con estado plano, pero K = 66,67 no supera 80: cruce sin nivel, no entra.
  6. Barras 17–18: K < 20, pero sin cruce hacia arriba: nivel sin cruce, no entra.
  7. Barra 19: cruce hacia arriba (13,5375 > 9,0204; antes 13,5237 <= 15,6190) con K < 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.verdict en NT_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 == 50 por 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; stochrsi se 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

  1. Con el fixture, calcula a mano k[12] y d[12]: k[12] = (stoch[10] + stoch[11] + stoch[12])/3 con 100,0000, 50,6902 y 79,2706, y d[12] con k[10..12]. Comprueba 76,6536 y 81,5156 y que no hay cruce alcista (76,6536 > 81,5156 es falso).
  2. 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?
  3. Lanza el sweep de 27 candidatos sobre KO 1d y cuenta, para cada smooth_k, cuántos trades hace el candidato 14/14. Comprueba en factorization_plan que aparecen 3 curvas RSI.