Detects market regime (Bull/Bear/Sideways) for any asset via Markov models. Returns signal, transition matrix, and walk-forward backtest. Drops into existing trading agents without rewriting them.
How this skill is triggered — by the user, by Claude, or both
Slash command
/markov-hedge-fund-method:regimeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill answers one question for any asset: **what regime are we in, how
This skill answers one question for any asset: what regime are we in, how sticky is it, and what does that imply for risk and direction? It's built to slot into a trading agent the user already has — as a confirmation layer, a signal, or a risk gate — without them rewriting their strategy.
Framework: Roan (@RohOnChain). Refactored into this plugin by Lewis Jackson. Backtests are historical, not forward-looking.
One command. It takes EITHER a ticker OR the user's own CSV, so it drops into any pipeline regardless of asset:
# any ticker yfinance knows (stocks, ETFs, crypto, FX, futures):
uv run ${CLAUDE_PLUGIN_ROOT}/scripts/markov_regime.py --ticker BTC-USD --json
# the user's own price file (their data, their asset, their pipeline):
uv run ${CLAUDE_PLUGIN_ROOT}/scripts/markov_regime.py --csv ./my_prices.csv --json
--json for the on-camera pretty terminal output (matrix, persistence
diagonal, stationary mix, walk-forward Sharpe + max DD, HMM line).--csv needs only a date column and a close column. It auto-detects common
names (date/time/timestamp, close/adj close/price/last); if
there's exactly one numeric column it uses that. No reformatting required.--window 20, --threshold 0.05 (±5%), --years 10,
--min-train 252. All overridable. --no-hmm skips the HMM.uv run resolves dependencies once (~10–20s), then it's instant.--json prints exactly one JSON object to stdout and nothing else. On failure
it prints {"error": "..."} and exits non-zero. Fields:
| Field | Type | Meaning |
|---|---|---|
source | str | the ticker or CSV path analysed |
rows | int | number of price rows used |
date_start, date_end | str | ISO dates of the window |
params | obj | window, threshold, min_train actually used |
states | list | ["Bear","Sideways","Bull"] — fixed index order 0,1,2 |
current_regime | str | the regime as of the last bar |
next_state_probabilities | obj | bear/sideways/bull — P(next | current) |
signal | float | bull_prob − bear_prob in [-1, 1]. >0 long bias, <0 short bias, magnitude = conviction |
transition_matrix | 3×3 list | row = from-state, col = to-state, rows sum to 1 |
persistence_diagonal | obj | bear/sideways/bull — P(stay in same regime). High = sticky regime |
stationary_distribution | obj | bear/sideways/bull — long-run fraction of time in each regime, sums to 1 |
walk_forward | obj | sharpe, max_drawdown, n_trades from a re-estimated-every-step, no-lookahead backtest. sharpe/max_drawdown may be NaN if history is too short |
hmm | obj | available: true → regimes (label, latent_state, mean_daily_return) + caveat. available: false → reason (graceful degrade — everything else is still valid) |
framework, disclaimer | str | attribution + "historical, not forward-looking" |
signal and current_regime are the two fields most strategies consume.
stationary_distribution is the one most risk layers consume.
The user already has a trading agent or strategy on some asset. This skill is a layer they add, not a system they adopt. Run it, read one or two fields, gate their existing logic. Three patterns:
The user has entry logic that already fires. Wrap it: only take longs when the regime agrees, only short when it disagrees.
import json, subprocess
r = json.loads(subprocess.check_output(
["uv","run",f"{PLUGIN}/scripts/markov_regime.py","--ticker","SPY","--json"]))
if my_strategy_says_long and r["signal"] > 0:
enter_long() # momentum + regime agree → take it
elif my_strategy_says_long and r["signal"] <= 0:
skip() # momentum says go, regime says don't → stand down
One line of gating. Their strategy is untouched; the regime just vetoes trades that fight the prevailing chain.
The stationary mix is the asset's long-run baseline. A high baseline Bear share means this asset structurally spends a lot of time in drawdown — size down.
bear_baseline = r["stationary_distribution"]["bear"]
size = base_size * (1.0 - bear_baseline) # heavier bear regime → smaller bets
# or hard gate: if bear_baseline > 0.40: size = 0 # too tail-heavy to trade
No new model. The user keeps their sizing logic and scales it by a single number that reflects how regime-dangerous the asset actually is.
No existing strategy needed. The signal field is already a direction +
conviction in [-1, 1]:
position = r["signal"] # +0.6 → 60% long; -0.4 → 40% short; ~0 → flat
Sanity-check it first with the printed walk-forward Sharpe + max drawdown
(run without --json to see them on screen) before sizing real capital.
--ticker for anything yfinance covers;
--csv for the user's own data on any asset/timeframe their pipeline
produces. The math is identical either way.hmmlearn can't compile (e.g. Windows
without MSVC), hmm.available is false with a reason and every other
field is still correct. HMM states are labelled by ascending mean return, so
a positive "Bear" mean just means the worst latent state was still
net-positive over that window.--threshold 0.02 to reproduce the tighter labelling from the
original onboarding prompt.npx claudepluginhub jackson-video-resources/markov-hedge-fund-method --plugin markov-hedge-fund-methodIdentifies market regimes (trending/ranging, high/low volatility) using ATR percentile and ADX to guide strategy selection and position sizing.
Builds financial models, backtests trading strategies, and analyzes market data with risk metrics, portfolio optimization, and statistical arbitrage.
Backtests crypto/stock trading strategies on historical data. Computes Sharpe/Sortino ratios, drawdowns; plots equity curves; optimizes parameters via grid search.