mts1b-research
Strategy discovery + ladder-sweep orchestration + factor library + dual-paper executors + drift signal store + factor-discovery factory.
Repo: github.com/MTS1B/mts1b-research
Layer: 5
Wave: 2 (months 4-7)
Depends on: foundation, platform, quantkit, portfolio, GPUbacktester, datalake, llm
Audience: quant researchers + the live strategy executor in mts1b-oms
What it is
The strategy-discovery + live-execution glue. Wraps mts1b-GPUbacktester (compute engine) with the workflows MTS1B uses to find, validate, and deploy alpha:
- Ladder sweep — L1→L5 staged elimination across millions of param combinations
- Factor library — reusable factors registered via
@register - Strategy registry — versioned strategy configs that live trade
- Dual-paper executors — same strategy code on paper and live brokers
- Factor-discovery factory — automated factor mining + ensemble composition
- Drift monitor — watches live IC vs backtest IC, auto-throttles decayed factors
What it's NOT
- ❌ Not the backtest engine itself — that's
mts1b-GPUbacktester(pure compute) - ❌ Not the OMS / risk gates — those are
mts1b-omsandmts1b-riskengine - ❌ Not the sizer — that's
mts1b-portfolio
Module layout
mts1b_research/
├── ladder/
│ ├── flow.py # L1→L5 staged elimination
│ ├── stage.py # each ladder stage config
│ └── survivor_bridge.py # promote survivors to next stage
├── factory/
│ ├── canonical_alpha_factory.py
│ ├── canonical_signal_executor.py
│ ├── factory_ensemble.py
│ ├── paper_factory.py
│ └── multi_asset_pipeline.py
├── strategies/
│ ├── registry.py
│ ├── live_executor.py # listens to NATS, emits orders
│ └── dual_paper.py # paper + live in lockstep
├── factors/
│ ├── library/ # built-in factors (momentum, value, quality, ...)
│ └── registry.py
├── ops/
│ ├── drift_monitor.py
│ └── decay_throttle.py
└── api/
├── rest.py # FastAPI (register strategy, get drift status)
└── nats.py # publish signals, subscribe ladder events
Ladder sweep workflow
L1 (broad): 100k param combos
↓ keep top 1% by walk-forward Sharpe
L2: 1k param combos
↓ keep top 10% with stability_score > 0.5
L3: 100 combos
↓ multi-regime test (bull / chop / crash)
L4: 20 combos
↓ cost stress: must survive 2x backtested cost
L5: 5 final candidates → manual review → paper trade for 90d → live
Each stage writes to data/sweeps/ladder/<run_id>/{per_stage, disparities, survivors}.parquet. Survivors pass to the next stage.
from mts1b_research.ladder import run_ladder
await run_ladder(
factor_class="momentum",
param_grid={
"h_long": list(range(60, 365, 5)),
"h_skip": [0, 5, 21, 42],
"winsorize": [True, False],
},
universe="us-large-cap",
start="2010-01-01", end="2024-01-01",
cost_bps=5,
)
Factor registration
from mts1b_research.factors import register
from mts1b_quantkit.factors import zscore_cross_sectional
from mts1b_foundation.market_data import UniversePanel
@register("f_momentum_12_1")
def f_momentum_12_1(panel: UniversePanel, /, h_long: int = 252, h_skip: int = 21):
"""Classic 12-1 momentum."""
close = panel.close
ret = close[-h_skip-1] / close[-h_long-h_skip-1] - 1
return zscore_cross_sectional(ret)
The registry is queryable; backtest CLIs look up factors by name.
Strategy registry
from mts1b_research.strategies import StrategyRegistry, StrategySpec
await StrategyRegistry.register(
StrategySpec(
strategy_id="momentum_v3",
factor="f_momentum_12_1",
params={"h_long": 252, "h_skip": 21},
universe="us-large-cap",
rebal="monthly",
sizing="kelly_voltarget_12",
cost_bps=5,
enabled=True,
)
)
# List active strategies
for spec in await StrategyRegistry.active():
print(spec.strategy_id, spec.factor, spec.last_run)
Live executor
A long-running worker that:
- At each rebalance window, looks up active strategies.
- For each, computes the factor on the latest universe panel.
- Applies the sizer in
mts1b-portfolio. - Diffs against current
mts1b-omspositions. - Emits child orders via
mts1b-oms.
mts1b-research live-executor run --fund-id paper-momentum --strategies-from registry
Dual-paper executor
For each strategy: run exactly the same code against a paper book AND a live book, log both. Used during the 90-day "promote to live" gate:
from mts1b_research.strategies import DualPaperExecutor
executor = DualPaperExecutor(
strategy_id="momentum_v3",
paper_fund="paper-momentum-demo",
live_fund="live-momentum",
diff_threshold_bps=10.0, # alert if paper and live diverge > 10bps
)
await executor.run()
Divergences between paper and live trigger Telegram alerts via mts1b-platform/messaging.
Drift monitor
# Runs every hour
for strategy in await StrategyRegistry.active():
drift = await measure_drift(strategy)
# Live IC vs backtest IC, last 30d
if drift.zscore < -2.0:
await StrategyRegistry.shadow(strategy.strategy_id, reason="decay")
await alert(f"strategy {strategy.strategy_id} shadowed: drift_zscore={drift.zscore:.2f}")
elif drift.zscore < -1.0:
await StrategyRegistry.halve_allocation(strategy.strategy_id, reason="early decay")
Factory ensembles
canonical_alpha_factory + factory_ensemble automate the "many weak factors → one strong portfolio" workflow:
- Discover N candidate factors via ladder sweeps.
- Filter by walk-forward IC + stability.
- Compose into an ensemble using HRP (or BL) on factor returns.
- The composite is a meta-strategy registered in the registry.
mts1b-research factory run \
--asset-class equities \
--candidates-per-class 50 \
--ensemble-method hrp \
--ensemble-name equities_meta_v1
NATS subjects
| Subject | Direction | Payload |
|---|---|---|
mts.v1.research.signals.published | publish | Signal |
mts.v1.research.ladder.stage_complete | publish | stage summary |
mts.v1.research.drift.measured | publish | drift snapshot |
mts.v1.marketdata.bars.* | subscribe | latest bars for rebalancing |
Build + test
pip install -e ".[dev]"
pytest -m unit
docker compose up -d postgres nats redis
pytest -m integration
Roadmap
| Version | Items |
|---|---|
| 0.1 (Wave 2) | Ladder, factor library, registry, live executor, dual-paper |
| 0.2 (Wave 2) | Drift monitor, factory ensemble |
| 0.3 (Wave 3) | RL-based factor discovery (research) |
| 1.0 (LTS) | Stable strategy spec |
See also
mts1b-GPUbacktester— compute engine wrapped heremts1b-quantkit— math primitives- Concept: Factor system — factor API
- Tutorial: Custom strategy — write + validate one