Skip to content

Latest commit

 

History

History
218 lines (147 loc) · 7.69 KB

File metadata and controls

218 lines (147 loc) · 7.69 KB

API Reference — analysis/

Covers analysis/regime.py, analysis/scanner.py, and analysis/momentum.py.


analysis/regime.py — Regime classifier

Classifies a price timeseries into one of three regimes using two statistical tests.

Decision rule:

Condition Regime
ADF (Augmented Dickey-Fuller) p-value < 0.05 MEAN_REVERTING
ADF p-value ≥ 0.05 AND linear R² ≥ 0.25 TRENDING
Otherwise FLIP

FLIP is the default because the bid-ask spread strategy requires no assumptions about price dynamics.


Regime

Description: Enum of possible market regimes.

Values:

  • Regime.MEAN_REVERTING — prices oscillate around a stable level; buy dips, sell at mean.
  • Regime.TRENDING — prices drift persistently in one direction; momentum strategy.
  • Regime.FLIP — no strong structure detected; capture the bid-ask spread.

RegimeResult

Description: Output of classify_regime().

Fields:

Field Type Description
regime Regime Classified regime
adf_pvalue float p-value from the ADF unit-root test
trend_r2 float R² of a linear fit on mid-price
trend_slope_pct float Per-bar slope as fraction of mean price (positive = upward drift)
notes str Human-readable classification rationale

classify_regime

Description: Classify the market regime of a single item's price timeseries.

Arguments:

  • ts_df (pd.DataFrame): Timeseries with avgHighPrice and avgLowPrice columns, indexed by UTC datetime.
  • min_bars (int): Minimum non-NaN observations required. Returns FLIP if too short. Default 30.

Returns: RegimeResult.

Example:

from analysis.regime import classify_regime, Regime
from market.prices import get_timeseries

ts = get_timeseries(2)
result = classify_regime(ts)
print(result.regime, result.adf_pvalue, result.trend_r2)
# Regime.FLIP 0.723 0.04

analysis/scanner.py — 24h scanner

Bulk scorer with regime-aware strategy routing. The primary entry point for the live scan pipeline is run_scan(); individual items are scored via score_item_routed().


compute_features_st

Description: Compute short-term mean-reversion features (rolling mean, std, z-score, net margin) from a raw timeseries DataFrame.

Arguments:

  • ts_df (pd.DataFrame): Raw 1h timeseries from WikiClient.

Returns: Copy of ts_df with additional columns: mid, volume, net_margin, net_margin_pct, roll_mean, roll_std, z_score.

Example:

from analysis.scanner import compute_features_st
from market.prices import get_timeseries

ts = get_timeseries(2)
feat = compute_features_st(ts)
print(feat[["mid", "z_score"]].tail(5))

run_simulation_24h

Description: Walk-forward mean-reversion simulation on a pre-computed features DataFrame.

Entry when z ≤ ST_Z_ENTRY and volume is sufficient. Exit when z ≥ ST_Z_EXIT, z ≤ ST_Z_STOP (stop-loss), hold exceeds ST_MAX_HOLD_HOURS, or data ends.

Arguments:

  • feat (pd.DataFrame): Output of compute_features_st.
  • buy_limit (int): Per-4h GE (Grand Exchange) buy limit.
  • capital (float): Available GP (gold pieces) for position sizing.

Returns: list[dict] of trade records. Each dict has: entry_time, exit_time, entry_price, exit_price, pnl_pct, pnl_gp, hold_bars, units, exit_reason.


score_item_routed

Description: Classify an item's regime and route it to the appropriate strategy scorer.

Routing: MEAN_REVERTING → z-score simulation; TRENDING → momentum simulation; FLIP → margin-capture. Always computes the flip score as a baseline — the directional strategy is chosen only if it beats the flip score on expected_gp_24h.

Arguments:

  • ts_df (pd.DataFrame): 1h timeseries DataFrame.
  • item_id (int): OSRS item ID.
  • item_name (str): Human-readable name.
  • buy_limit (int): Per-4h GE buy limit.
  • capital (float): Available GP.

Returns: Score dict | None. The dict always includes "regime", "strategy", "expected_gp_24h", "win_rate", "avg_pnl_pct", and "trades_per_24h". Returns None if no scoreable signal is found or expected GP is non-positive.

Example:

from analysis.scanner import score_item_routed
from market.prices import get_timeseries, get_mapping

ts = get_timeseries(2)
mapping = get_mapping()
score = score_item_routed(ts, 2, "Cannonball", int(mapping.at[2, "limit"]), 10_000_000)
if score:
    print(score["strategy"], score["expected_gp_24h"])

run_scan

Description: Full 24h scan pipeline: pre-filter all items by fast score, fetch timeseries for the top candidates, classify regime, score with the appropriate strategy, and return results sorted by expected GP/day.

Arguments:

  • capital (float): GP available. Default config.TOTAL_CAPITAL.
  • top_n_display (int): Number of rows to print. Default 15.
  • force_refresh (bool): Force-refresh all cached timeseries. Default False.
  • show_cache (bool): Print cache status table before scanning. Default False.
  • quiet (bool): Suppress all terminal output. Default False.

Returns: pd.DataFrame sorted by expected_gp_24h descending. Empty DataFrame if no items pass the filters.

Example:

python -m analysis.scanner --top 10 --capital 50000000
from analysis.scanner import run_scan
df = run_scan(capital=10_000_000, quiet=True)
print(df[["item_name", "strategy", "expected_gp_24h"]].head(5))

analysis/momentum.py — Momentum signal

The momentum signal currently has negative Sharpe across all tested parameters. It is disabled in live trading via a sentinel threshold (MOM_SLOPE_ENTRY_THRESHOLD = 0.010). The code is retained for research and calibration purposes.

The primary signal is the rolling linear slope of mid-price, normalised by mean price. This is more robust than point-to-point momentum (φ) because it fits a line through all bars in the window rather than comparing two noisy endpoints.


compute_momentum_features

Description: Compute momentum features from a raw 1h timeseries DataFrame.

Arguments:

  • ts_df (pd.DataFrame): Raw timeseries with avgHighPrice, avgLowPrice, highPriceVolume, lowPriceVolume.
  • lookback (int | None): Linear fit window size in bars. Defaults to config.MOM_LOOKBACK_HOURS.

Returns: Copy of ts_df with additional columns:

Column Description
mid (avgHighPrice + avgLowPrice) / 2
volume highPriceVolume + lowPriceVolume
slope Rolling linear slope, normalised by mean price (entry/exit signal)

Example:

from analysis.momentum import compute_momentum_features
from market.prices import get_timeseries

ts = get_timeseries(11832)
feat = compute_momentum_features(ts, lookback=24)
print(feat[["mid", "slope"]].tail(5))

run_momentum_simulation_24h

Description: Walk-forward momentum simulation on a pre-computed features DataFrame.

Entry when slope ≥ entry_threshold and market is liquid. Exit when slope ≤ reversal_threshold, hold exceeds MOM_MAX_HOLD_HOURS, or data ends.

Arguments:

  • feat (pd.DataFrame): Output of compute_momentum_features.
  • buy_limit (int): Per-4h GE buy limit.
  • capital (float): Available GP.
  • entry_threshold (float | None): Override config.MOM_SLOPE_ENTRY_THRESHOLD.
  • reversal_threshold (float | None): Override config.MOM_SLOPE_REVERSAL_THRESHOLD.

Returns: list[dict] of trade records with keys: entry_time, exit_time, entry_price, exit_price, pnl_pct, pnl_gp, hold_bars, units, exit_reason.