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-methodBuilds 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.
Generates BUY/SELL trading signals with confidence scores using RSI, MACD, Bollinger Bands and more for crypto/stocks watchlists. Scans, ranks opportunities, adds stop-loss/take-profit.