P08 · Squeeze Momentum (familia TTM/LazyBear)¶
| Campo | Valor |
|---|---|
| Familia | Volatilidad-ruptura · compresión de canales (Bollinger dentro de Keltner) + momento por regresión lineal; evento de liberación y salida por cambio de signo |
| Features requeridas | squeeze_momentum (salidas squeeze, val, m), compuesta de bbands, keltner_sma, tr, sma_of(tr), rolling_max, rolling_min, sma(close) y linreg_endpoint |
| Parámetros y defaults | period = 20, bb_k = 2.0, kc_k = 1.5 |
| Rejilla del catálogo | period ∈ {10, 20, 30}, bb_k ∈ {1.5, 2.0, 2.5}, kc_k ∈ {1.0, 1.5, 2.0} → 27 candidatos válidos; solo 3 regresiones rolling |
variant_keys |
period, bb_k, kc_k |
| Restricciones | period entero ≥ 2; bb_k > 0 y kc_k > 0 (si no, PolicyParamError); sin restricción cruzada entre bb_k y kc_k |
first_decision_index |
2·period − 2 = 38 con defaults (18 con period = 10, 58 con period = 30) |
| Activos y timeframes | NVDA y KO en los 7 TF; AMZN, INTC, MSFT, TSLA en 1d; BME en 1d; FX solo con OHLC reparado |
| 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 'squeeze_momentum'" (Nautilus no tiene regresión lineal rolling; su Bollinger usa precio típico y su Keltner es EMA ± ATR) |
Fuentes: docs/estrategias/P08.md (especificación), catalog/strategy_catalog.json, docs/DATOS_POR_ESTRATEGIA.md, runs/parity/summary.json.
Versión de la fórmula fijada
El catálogo avisa: "La referencia de LazyBear documenta una corrección histórica del multiplicador: fijar la versión de la fórmula" y "Esta salida es nuestra variante explícita, no la salida por cambio de color de otras versiones". Esta ficha describe nuestra versión; no se afirma paridad con ningún script publicado.
Qué vas a aprender¶
- Qué es un squeeze (Bollinger estrictamente dentro de Keltner) y por qué la entrada es la liberación, una transición de una sola barra.
- Cómo se calcula el momento
m: el extremo final de una regresión lineal rolling sobreval, por sumas suficientes. - Por qué el Keltner de P08 usa
SMA(TR)y no ATR, y por qué eso exige un nombre de primitiva propio. - A leer la barra 13 del fixture, donde salida y liberación coinciden y el objetivo es 0, no la posición contraria.
- Por qué 27 candidatos solo cuestan 3 regresiones.
La idea en una frase¶
Cuando la volatilidad lleva un tiempo comprimida y de pronto se expande, entra en la dirección del momento; sal en cuanto el momento cambie de signo.
Intuición de mercado¶
Las bandas de Bollinger miden la volatilidad con la desviación típica del cierre; las de Keltner, con el rango medio de las barras. Cuando el mercado se queda quieto, la desviación típica se encoge más deprisa que el rango y las Bollinger quedan dentro del Keltner: es el squeeze, una fase de compresión. La idea (TTM de John Carter, popularizada por LazyBear) es que a la compresión le sigue una expansión, y que el primer tramo de esa expansión tiende a continuar.
Analogía. Un muelle comprimido: mientras está sujeto no pasa nada; lo interesante es el momento en que se suelta, y hacia dónde apunta al soltarse. El squeeze es el muelle sujeto; la liberación, el momento en que se suelta; el signo de m, hacia dónde apunta.
Por qué podría funcionar. La volatilidad se agrupa en regímenes: a periodos tranquilos les siguen periodos agitados. Entrar al principio de la expansión, a favor del momento, captura el movimiento inicial.
Cuándo falla.
- Falsas rupturas: la volatilidad se expande una barra y vuelve a comprimirse;
mcambia de signo y la salida llega con pérdida. - Liberaciones perdidas: si en la barra de liberación
m == 0o hay una salida simultánea, el evento se pierde (no se guarda ni tiene caducidad). - Huecos de sesión: el TR de la barra tras el hueco ensancha el Keltner, favorece un squeeze artificial y produce una liberación espuria al deshacerse; contamina justo el evento de entrada.
Sesgos típicos al evaluarla.
- Pocos trades: P08 pasa mucho tiempo plano; un Sharpe con 5 operaciones no significa nada. Mira
squeeze_fractiony el número de trades antes de ordenar. - Comparar con el indicador de una plataforma: el Keltner EMA ± ATR, las desigualdades no estrictas o la salida "por color" son otras estrategias.
- Regresión centrada:
center=Trueen pandas produce unmmás suave y "mejor" usando el futuro.
Las reglas exactas¶
La regla canónica del catálogo: "Squeeze cuando BB está estrictamente dentro de KC. Liberación=squeeze[t-1] y no squeeze[t]. m[t]=valor final de una regresión lineal rolling de C-(midpoint(HH(n),LL(n))+SMA(C,n))/2. Entrar largo/corto en liberación según signo m; salir al cambiar de signo. Esta salida es nuestra variante explícita, no la salida por cambio de color de otras versiones."
Con sign(v) = +1 si v > 0, −1 si v < 0 y 0 si v == 0 o NaN:
Situación en el cierre de t |
Objetivo target[t] |
Motivo (reason) |
|---|---|---|
t < 2·period − 2 |
0 | WARMUP |
en posición y sign(m[t]) != sign(m[t−1]) |
0 | EXIT (prioridad sobre la entrada) |
plano, release[t] y sign(m[t]) = +1, sin salida en esta barra |
+1 | ENTRY |
plano, release[t] y sign(m[t]) = −1, sin salida en esta barra |
−1 | ENTRY |
plano, release[t] y m[t] == 0 |
0 (la liberación se pierde) | HOLD |
| otro caso | el anterior | HOLD |
| barra no operable | se congela el publicado | NOT_TRADABLE |
- Squeeze:
bb.lower > kc.lowerybb.upper < kc.upper, ambas estrictas;Falsesi alguna banda es NaN. - Liberación:
release[t] = squeeze[t−1] and not squeeze[t]. Es un flanco, no un nivel. - Salida: cambio de signo de
m;sign(0) = 0cuenta como signo (ratificado, D-18), así quemque pasa de positivo a 0 exacto cierra. Un NaN tardío enmtambién cierra. - Reversión: no permitida. P08 nunca produce un delta de 2.
- Filtros: ninguno más.
# decisión en el cierre de t, fill en el open de t+1
state = 0
para cada barra t:
si t < 2*period - 2: target = 0 # WARMUP
si no:
release = squeeze[t-1] y no squeeze[t]
exited = False
si state != 0 y sign(m[t]) != sign(m[t-1]): # SALIDA primero
state = 0; exited = True
si no exited y state == 0 y release y sign(m[t]) != 0:
state = sign(m[t]) # ENTRADA
target = state
si target != target_prev: orden(target - target_prev) -> fill a open[t+1]
Liberación = squeeze[t−1] y no squeeze[t], nunca squeeze[t+1]
squeeze[t] and not squeeze[t+1] marca la última barra del squeeze en vez de la primera fuera: entra una barra antes con información que en t no existe. Y usar not squeeze[t] como condición de entrada no es una fuga, sino otra estrategia: en el fixture entraría 7 veces en lugar de 1.
Los indicadores que usa¶
| Feature | Fórmula | first_valid |
|---|---|---|
bbands{n, bb_k} |
SMA(C, n) ± bb_k · stddev(C, n) (desviación poblacional) |
n − 1 |
tr |
TR[0] = H[0] − L[0]; después max(H − L, |H − Cprev|, |L − Cprev|) |
0 |
keltner_sma{n, kc_k} |
SMA(C, n) ± kc_k · SMA(TR, n) — no EMA ± ATR (eso es el Keltner de P16) |
n − 1 (dependencias paralelas) |
rolling_max{n} / rolling_min{n} |
máximo de high / mínimo de low en [t − n + 1, t], inclusivos (no el Donchian desplazado) |
n − 1 |
val |
C − ((HH + LL)/2 + SMA(C, n))/2 (promedio doble, literal) |
n − 1 |
m = linreg_endpoint(val, n) |
con x = 0..n−1: slope = (n·Sxy − Sx·Sy)/(n·Sxx − Sx²), intercept = (Sy − slope·Sx)/n, m = intercept + slope·(n−1) |
2n − 2 |
first_decision_index = 2·period − 2 (el máximo sobre las tres salidas; D-12, sin +1). En esa barra m[t−1] es NaN, pero squeeze[t−1] existe desde period − 1, así que la regla es evaluable: "no entro" es una decisión válida.
SMA(TR) no es ATR: en la semilla coinciden, pero en el fixture ya divergen en la segunda barra (ATR[4] = 0,13125 frente a SMA(TR,4)[4] = 0,1450). Más detalle en Las 23 primitivas de fase B, Indicadores paso a paso e Indicadores y features.
Ejemplo barra a barra¶
Fixture §6 de docs/estrategias/P08.md: period = 4, bb_k = 2.0, kc_k = 1.0 (period y kc_k fuera de rejilla), cash_inicial = 1.000,00, tick 0,01, sin costes. first_valid(squeeze) = first_valid(val) = 3, first_valid(m) = 6 → first_decision_index = 6. 18 barras (0–17), con open[i] = close[i−1].
Barras 0–4: WARMUP (m NaN; el squeeze ya está activo desde la barra 3). Se muestran la 5 (última de warmup) y de la 6 a la 17.
| t | O | H | L | C | squeeze | release | val | m | target | orden | fill | pos. | PnL acum. sin costes* | equity con 5 bps* |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5 | 10,08 | 10,18 | 10,04 | 10,14 | V | F | 0,0438 | NaN | 0 WARMUP | — | — | 0 | 0,00 | 1.000,00 |
| 6 | 10,14 | 10,18 | 10,06 | 10,10 | V | F | −0,0025 | 0,0051 | 0 HOLD | — | — | 0 | 0,00 | 1.000,00 |
| 7 | 10,10 | 10,20 | 10,06 | 10,16 | V | F | 0,0400 | 0,0301 | 0 HOLD | — | — | 0 | 0,00 | 1.000,00 |
| 8 | 10,16 | 10,54 | 10,12 | 10,50 | F | V | 0,2425 | 0,1768 | +1 ENTRY | BUY 1 | — | 0 | 0,00 | 1.000,00 |
| 9 | 10,50 | 11,04 | 10,46 | 11,00 | F | F | 0,5050 | 0,4550 | +1 HOLD | — | 10,50 | +1 | 0,50 | 1.000,49 |
| 10 | 11,00 | 11,44 | 10,96 | 11,40 | F | F | 0,6425 | 0,6680 | +1 HOLD | — | — | +1 | 0,90 | 1.000,89 |
| 11 | 11,40 | 11,44 | 11,26 | 11,30 | F | F | 0,3850 | 0,5285 | +1 HOLD | — | — | +1 | 0,80 | 1.000,79 |
| 12 | 11,30 | 11,34 | 10,96 | 11,00 | V | F | −0,0625 | 0,0735 | +1 HOLD | — | — | +1 | 0,50 | 1.000,49 |
| 13 | 11,00 | 11,04 | 10,56 | 10,60 | F | V | −0,4375 | −0,4213 | 0 EXIT | SELL 1 | — | +1 | 0,10 | 1.000,09 |
| 14 | 10,60 | 10,64 | 10,26 | 10,30 | F | F | −0,5250 | −0,6258 | 0 HOLD | — | 10,60 | 0 | 0,10 | 1.000,08 |
| 15 | 10,30 | 10,34 | 10,06 | 10,10 | F | F | −0,5000 | −0,5912 | 0 HOLD | — | — | 0 | 0,10 | 1.000,08 |
| 16 | 10,10 | 10,24 | 10,06 | 10,20 | F | F | −0,2250 | −0,3225 | 0 HOLD | — | — | 0 | 0,10 | 1.000,08 |
| 17 | 10,20 | 10,44 | 10,16 | 10,40 | V | F | 0,1000 | 0,0350 | 0 HOLD | — | — | 0 | 0,10 | 1.000,08 |
OHLC, squeeze, release, val, m (4 decimales, half-even), objetivos, motivos y fills son literales del fixture §6 de docs/estrategias/P08.md. *El fixture solo da la equity en el cierre 9 (1.000,50) y en el 14 (1.000,10); el resto de la columna "PnL acumulado sin costes" es cálculo de este manual con la misma regla (caja tras fills + 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 también 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=9):
1 · 10,50 · 0,0005 = 0,00525 → 0,01 - fill 2 (t=14):
1 · 10,60 · 0,0005 = 0,0053 → 0,01
Equity final con 5 bps: 1.000,10 − 0,02 = 1.000,08.
Lo que hay que ver en la tabla:
- Barras 3–7: squeeze continuado;
releasees falso en todas, porque la liberación es una transición. - Barra 8:
squeeze[7] = V,squeeze[8] = F→ liberación conm = 0,1768 > 0→ largo. Fill enopen[9] = 10,50. - Barras 9–11: sin squeeze pero sin liberación: una implementación "por nivel" habría entrado otra vez aquí.
- Barra 12: el squeeze vuelve (transición inversa): no es liberación.
- Barra 13, la clave: es a la vez salida (
sign(m)pasa de +1 a −1) y liberación conm < 0. La prioridad de la salida y el "no reabrir en la barra de salida" dan objetivo 0, no −1; el fill es de 1 unidad, no de 2. - Barra 17: nuevo squeeze y cambio de signo de
m, pero plano y sin liberación: no pasa nada. Termina plano.
Las cuentas de las barras clave¶
Squeeze (t=3): SMA(C,4) = (10,00+10,10+10,05+10,12)/4 = 10,0675
varianza = 0,008675/4 = 0,00216875 -> stddev = 0,0465698
bb = 10,0675 ± 2·0,0465698 = [9,9743603 ; 10,1606397]
TR = 0,080; 0,180; 0,130; 0,150 -> SMA(TR,4) = 0,540/4 = 0,1350
kc = 10,0675 ± 1,0·0,1350 = [9,9325 ; 10,2025]
9,97436 > 9,9325 y 10,16064 < 10,2025 -> squeeze[3] = V
val (t=3): HH = 10,16; LL = 9,96; midpoint = 10,06
val = 10,12 - (10,06 + 10,0675)/2 = 0,05625
m (t=6): ventana val[3..6] = (0,05625; 0,00625; 0,04375; -0,00250), x = 0..3
Sx = 6; Sxx = 14; den = 4·14 - 36 = 20
Sy = 0,10375; Sxy = 0,08625
slope = (4·0,08625 - 6·0,10375)/20 = -0,013875
intercept = (0,10375 + 0,013875·6)/4 = 0,046750
m[6] = 0,046750 - 0,013875·3 = 0,005125
Liberación (t=8): bb.lower[8] = 10,2250 - 2·0,1602342 = 9,9045316 ; kc.lower[8] = 10,2250 - 0,2050 = 10,0200
9,9045 > 10,0200 es FALSO -> squeeze[8] = F ; squeeze[7] = V -> release[8] = V
ventana val[5..8]: Sy = 0,32375 ; Sxy = 0,80500
slope = (3,22000 - 1,94250)/20 = 0,063875 ; intercept = -0,014875
m[8] = -0,014875 + 0,063875·3 = 0,176750 > 0 -> +1 (ENTRY)
Salida (t=13): sign(m[12]) = +1 ; sign(m[13]) = -1 -> EXIT, exited = True
release[13] = V pero "no reabrir" -> target 0
Caja: 1.000,00 - 10,50 = 989,50 (fill 1, open[9])
989,50 + 10,60 = 1.000,10 (fill 2, open[14])
Nota de la especificación sobre m[8]: su valor exacto es 707/4000 = 0,17675, que a 4 decimales half-even es 0,1768; el kernel f64 devuelve 0.1767499999999993. Los tests comparan con atol = 1e-9, no con cadenas redondeadas.
Diagrama¶
flowchart LR
W(["WARMUP: t < 2·period-2"]) -->|"m válido"| FLAT["Plano 0"]
FLAT -->|"release y m > 0 · BUY 1"| L["Largo +1"]
FLAT -->|"release y m < 0 · SELL 1"| S["Corto -1"]
FLAT -->|"sin release, o m == 0"| FLAT
L -->|"sign(m) cambia · SELL 1"| FLAT
S -->|"sign(m) cambia · BUY 1"| FLAT
L -->|"mismo signo"| L
S -->|"mismo signo"| S
No hay flecha directa entre largo y corto: toda salida pasa por plano, aunque en esa misma barra haya una liberación del signo contrario.
Cómo lanzarla¶
Backtest con ambos motores sobre NVDA 5min en 2024, con los defaults (estrategia por plantilla y parámetros, forma "inline" de la API de backtesting):
BASE=http://localhost:8000
SID=$(curl -sS -X POST "$BASE/v1/backtest" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p08-0001",
"correlation_id": null,
"schema_version": "v1",
"payload": {
"strategy": {"template_id": "P08", "params": {"period": 20, "bb_k": 2.0, "kc_k": 1.5},
"name": "Squeeze 20/2.0/1.5 base", "register": true},
"data": {"asset_id": "NVDA", "timeframe": "5min",
"window": {"start": "2024-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": "P08",
"params": {"period": 20, "bb_k": 2.0, "kc_k": 1.5}, "register": True},
"data": {"asset_id": "NVDA", "timeframe": "5min",
"window": {"start": "2024-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 un Keltner más ancho (squeeze más frecuente):
curl -sS -X POST "$BASE/v1/strategies/$SID/derive" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p08-derive", "correlation_id": null, "schema_version": "v1",
"payload": {"param_overrides": {"kc_k": 2.0}, "name": "Squeeze 20/2.0/2.0",
"slug": "squeeze-20-2-0-2-0", "tags": ["sensibilidad"]}}' | jq '.payload.strategy_id, .payload.cache_forecast'
kc_k es parámetro de feature (forma parte de squeeze_momentum), pero barato: según la tabla de invalidación §5.3 de la especificación, se reutilizan sma{20}, sma_of(tr), bbands íntegro y val y m íntegros; solo se rehacen las bandas Keltner, el booleano squeeze, la política y el ledger. Con bb_k pasa lo simétrico. Cambiar period, en cambio, solo reutiliza tr.
Sweep de sus variant_keys:
curl -sS -X POST "$BASE/v1/sweep" -H 'Content-Type: application/json' -d '{
"request_id": "manual-p08-sweep", "correlation_id": null, "schema_version": "v1",
"payload": {"base_strategy_id": "'"$SID"'",
"grid": {"period": [10, 20, 30], "bb_k": [1.5, 2.0, 2.5], "kc_k": [1.0, 1.5, 2.0]},
"candidate_policy": {"drop_invalid": true, "drop_duplicates": true,
"register_strategies": true,
"name_template": "{template_name} {period}/{bb_k}/{kc_k}"},
"data": {"asset_id": "NVDA", "timeframe": "5min",
"window": {"start": "2024-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 (no hay restricción bb_k > kc_k, y no debe añadirse). Resultados en GET /v1/jobs/{job_id}.
Cuánto cuesta la rejilla¶
| Nivel | Cuenta |
|---|---|
tr |
1 |
sma(C), stddev, sma_of(tr), rolling_max, rolling_min |
3 de cada (una por period) |
val y m (la regresión, el kernel caro) |
3 y 3: no dependen de bb_k ni de kc_k |
bbands / keltner_sma |
9 / 9 |
squeeze |
27 (comparación de cuatro series, barata) |
| Políticas / ledgers | 27 / 27 |
La regresión se calcula 3 veces para 27 candidatos: un ahorro del 89 % en el kernel caro. Es el extremo opuesto a P07, donde el multiplicador está dentro de la recursión y el ahorro en el kernel caro es 0 %.
Qué mirar en los resultados¶
squeeze_fraction: cerca de 0 % o de 100 % no hay liberaciones y el candidato no tiene señal (no es inválido; se conserva en el denominador).- Liberaciones por 1.000 barras y cuántas se convierten en entrada; las perdidas, separadas por causa (
m == 0, salida simultánea como la barra 13, barra no operable). - Duración media de los ciclos de squeeze: 50 % de barras en ciclos de 2 barras es ruido; en ciclos de 30 es régimen.
- Exposición temporal: P08 pasa mucho tiempo plano (12 de 18 barras en el fixture).
- Ausencia de deltas ±2: propiedad estructural comprobable en cualquier corrida.
- Número de trades antes que Sharpe.
DATA_GAP_SESSIONSen KO: el hueco ensancha el Keltner y puede fabricar una liberación.- Paridad: exacta en señales en los carriles soportados; la ruta nativa de Nautilus es UNSUPPORTED por falta de regresión lineal rolling.
Causalidad comprobada¶
El test de "futuros alterados" de la especificación (300 barras, parámetros 20/2.0/1.5, t0 = 200, high, low y close × 1,5 tras t0) exige igualdad bit a bit hasta t0 en todas las columnas intermedias, no solo en target: M es donde se esconde una regresión centrada (dependería de close[t0+9]) y RELEASE donde se esconde el desplazamiento squeeze[t+1]. Un segundo test perturba solo la barra t0 y exige que m[t0−1] no cambie, que m[t0] y m[t0+n−1] sí, y que m[t0+n] ya no: fija que la ventana es finita y termina en t.
Como todo son ventanas finitas (sin EMA ni RMA), el checkpoint para continue son los últimos period cierres, máximos y mínimos, los últimos period valores de val, el squeeze previo y el objetivo vigente.
Errores típicos¶
| Error | Consecuencia | Cómo evitarlo |
|---|---|---|
period = 1, bb_k <= 0 o kc_k <= 0 |
PolicyParamError (con period = 1 la regresión divide por cero) |
La API lo rechaza; en un sweep cuenta como inválido |
Exigir bb_k > kc_k "por sentido común" |
Eliminar candidatos válidos de la rejilla | σ y SMA(TR) tienen escalas distintas; las 27 son válidas |
| Keltner con ATR o con EMA | Otro squeeze, otras liberaciones | keltner_sma = SMA(C) ± kc_k · SMA(TR) |
Entrar con not squeeze[t] (nivel) |
7 entradas en el fixture en vez de 1 | La liberación es un flanco |
| Invertir en la barra 13 | Delta de 2 donde debe haber 1 | Salida primero y "no reabrir" en la barra de salida |
Regresión centrada o tsf (extrapolar a x = n) |
Fuga de futuro, u otra serie m |
Endpoint en x = n − 1 con ventana [t − n + 1, t] |
m como pendiente, o salida "por color" (val[t] > val[t−1]) |
Otra estrategia | m es el valor ajustado final; la salida es por signo |
Reutilizar el Donchian desplazado para HH/LL |
Otra fórmula y otro first_valid |
rolling_max/rolling_min inclusivos |
Variantes y extensiones¶
- Salida por "cambio de color" (LazyBear): excluida explícitamente; sería otra entrada de catálogo.
- Ruptura armada con caducidad: P05 es la receta de compresión/ruptura con armado; el catálogo insiste en que no es P08.
- Keltner EMA ± ATR: el de P16; comparte
trcon P08 pero no el Keltner. - Bandas de Bollinger solas: P04.
- Solo largos:
target_lots = [0, 1]ignorando las liberaciones conm < 0.
Resumen¶
- P08 entra en la liberación del squeeze (Bollinger que sale del Keltner tras estar estrictamente dentro), en la dirección de
sign(m). mes el extremo final de una regresión lineal rolling sobreval; se sale cuando cambia de signo (incluido el paso a 0).- Nunca invierte: la barra 13 del fixture es salida y liberación a la vez, y el objetivo es 0.
- En el fixture: largo a 10,50, salida a 10,60; equity final 1.000,10 (1.000,08 con 5 bps, cálculo propio).
- 27 candidatos, 3 regresiones: los multiplicadores quedan fuera del kernel caro.
Para practicar¶
- Con el fixture, calcula a mano
m[7]a partir de la ventanaval[4..7] = (0,00625; 0,04375; −0,00250; 0,04000)y comprueba 0,0301 (valor exacto 0,030125). - ¿Qué pasaría en la barra 13 con una implementación que olvidase la guarda "no reabrir en la barra de salida" (
if not exited)? Escribe el fill resultante (lado, cantidad, precio) y la caja tras él. - Lanza el sweep 3×3×3 sobre NVDA 5min y anota
squeeze_fractiony el número de trades por candidato. ¿Qué combinaciones debb_k/kc_kse quedan sin señal? Pide lostimingsy comprueba que el tiempo de regresión no crece con los multiplicadores.