s-ttp commited on
Commit
95ec185
·
verified ·
1 Parent(s): 191f5e8

Upload folder using huggingface_hub

Browse files
__pycache__/app.cpython-312.pyc CHANGED
Binary files a/__pycache__/app.cpython-312.pyc and b/__pycache__/app.cpython-312.pyc differ
 
__pycache__/rebalance_engine.cpython-312.pyc CHANGED
Binary files a/__pycache__/rebalance_engine.cpython-312.pyc and b/__pycache__/rebalance_engine.cpython-312.pyc differ
 
app.py CHANGED
@@ -157,6 +157,21 @@ def load_health():
157
  lines = [f"### 🩺 Agent health — {icon.get(r.get('overall'), '⚪')} {r.get('overall')} <sub>(checked {r.get('date')})</sub>"]
158
  for c in r.get("checks", []):
159
  lines.append(f"- {icon.get(c['status'], '⚪')} **{c['name']}** — {c['detail']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  lines.append("<sub>Deterministic daily checks: live tracking gap located in the 20-year backtest distribution, "
161
  "risk guardrails, and pipeline freshness. Flags only — the supervisor never trades.</sub>")
162
  return "\n".join(lines)
@@ -174,6 +189,11 @@ def load_rebalance():
174
  if not log:
175
  return ""
176
  e = log[-1]
 
 
 
 
 
177
  mode = "🟢 OFFENSE" if e.get("mode") == "OFFENSE" else "🛡️ DEFENSE"
178
  head = (f"### 🗓️ Latest rebalance — {e.get('date','')} · {mode}\n"
179
  f"{e.get('buys',0)} buys · {e.get('sells',0)} sells · {e.get('turnover_pct',0)}% turnover · "
@@ -231,7 +251,8 @@ with gr.Blocks(title="DeepQuant — live paper monitor", theme=gr.themes.Soft())
231
  "**Under the hood:**\n"
232
  "- 🧠 **[MarketFM](https://huggingface.co/spaces/s-ttp/marketfm-ranker)** — a survivorship-free quant model — ranks the universe and sets the target portfolio\n"
233
  "- 💬 **[MarketGuruLLM](https://huggingface.co/s-ttp/qwen25-14b-fin-lora)** — a fine-tuned analyst LLM — reasons over and narrates every rebalance\n"
234
- "- ⚡ **Alpaca API** — the brokerage rails the **quant execution engine** drives to place orders and stream live fills, positions and P&L\n\n"
 
235
  "**The strategy:** large-cap **momentum + quality** while the trend is up; when it turns down it de-risks into a "
236
  "blend of **stocks + bonds + gold** (60 / 20 / 20). Backtested 2002–2026: **~14% / yr vs the S&P 500's ~11%**, "
237
  "higher Sharpe, smaller drawdown.\n"
 
157
  lines = [f"### 🩺 Agent health — {icon.get(r.get('overall'), '⚪')} {r.get('overall')} <sub>(checked {r.get('date')})</sub>"]
158
  for c in r.get("checks", []):
159
  lines.append(f"- {icon.get(c['status'], '⚪')} **{c['name']}** — {c['detail']}")
160
+ try:
161
+ rr = json.load(open(hf_hub_download("s-ttp/marketfm-panel", "recalibration_report.json", repo_type="dataset",
162
+ token=tok, force_download=True)))
163
+ tr = rr.get("tracking", {})
164
+ inc = rr.get("incumbent", {})
165
+ flag = "🔁" if rr.get("changed") else ("🟡" if rr.get("triggered") else "🟢")
166
+ lines.append(f"- {flag} **Autonomous recalibration agent** (weekly) — 8-wk gap vs SPY "
167
+ f"{tr.get('gap_8w_pp', '—')}pp ({tr.get('pctile_8w', '—')}th pctile of backtest) · "
168
+ f"implementation drift {tr.get('impl_drift_pp', '—')}pp · {rr.get('decision', '')[:160]}")
169
+ lines.append(f"<sub>Live strategy config v{inc.get('version', '?')}: signal {inc.get('signal')} · regime "
170
+ f"{inc.get('regime')} · defense {inc.get('defense')} · cadence {inc.get('cadence_weeks')}w. "
171
+ f"Recalibration only adopts a variant that beats the incumbent on a full 20-year walk-forward, "
172
+ f"net of cost, across folds — never a re-fit to recent weeks; max one change per 90 days.</sub>")
173
+ except Exception:
174
+ pass
175
  lines.append("<sub>Deterministic daily checks: live tracking gap located in the 20-year backtest distribution, "
176
  "risk guardrails, and pipeline freshness. Flags only — the supervisor never trades.</sub>")
177
  return "\n".join(lines)
 
189
  if not log:
190
  return ""
191
  e = log[-1]
192
+ if e.get("type") == "recalibration":
193
+ head = f"### 🔁 Strategy recalibrated — {e.get('date','')}\n{e.get('summary','')}\n\n"
194
+ if e.get("narration"):
195
+ return head + f"> 💬 *{e['narration']}*\n\n<sub>— {e.get('narrated_by','MarketGuruLLM')}</sub>"
196
+ return head
197
  mode = "🟢 OFFENSE" if e.get("mode") == "OFFENSE" else "🛡️ DEFENSE"
198
  head = (f"### 🗓️ Latest rebalance — {e.get('date','')} · {mode}\n"
199
  f"{e.get('buys',0)} buys · {e.get('sells',0)} sells · {e.get('turnover_pct',0)}% turnover · "
 
251
  "**Under the hood:**\n"
252
  "- 🧠 **[MarketFM](https://huggingface.co/spaces/s-ttp/marketfm-ranker)** — a survivorship-free quant model — ranks the universe and sets the target portfolio\n"
253
  "- 💬 **[MarketGuruLLM](https://huggingface.co/s-ttp/qwen25-14b-fin-lora)** — a fine-tuned analyst LLM — reasons over and narrates every rebalance\n"
254
+ "- ⚡ **Alpaca API** — the brokerage rails the **quant execution engine** drives to place orders and stream live fills, positions and P&L\n"
255
+ "- 🔁 **Autonomous recalibration** — tracks live vs backtest over rolling 8-week windows; on statistically unusual deviation it re-runs the full walk-forward due diligence and adopts a better-validated variant on its own\n\n"
256
  "**The strategy:** large-cap **momentum + quality** while the trend is up; when it turns down it de-risks into a "
257
  "blend of **stocks + bonds + gold** (60 / 20 / 20). Backtested 2002–2026: **~14% / yr vs the S&P 500's ~11%**, "
258
  "higher Sharpe, smaller drawdown.\n"
rebalance_engine.py CHANGED
@@ -42,24 +42,51 @@ def cap_weights(w, mw=MAX_W):
42
  return w
43
 
44
 
45
- def detect_regime():
 
 
 
 
 
46
  try:
47
- u = f"https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY_ADJUSTED&symbol=SPY&apikey={AV}"
48
- ts = json.load(urllib.request.urlopen(u, timeout=25, context=_CTX))["Weekly Adjusted Time Series"]
49
- s = pd.Series({k: float(v["5. adjusted close"]) for k, v in ts.items()}).sort_index()
50
- sma10, sma40 = s.rolling(10).mean().iloc[-1], s.rolling(40).mean().iloc[-1]
51
- return {"offense": bool(sma10 >= sma40), "sma10": float(sma10), "sma40": float(sma40), "spy": float(s.iloc[-1])}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  except Exception as e:
53
- return {"offense": True, "sma10": None, "sma40": None, "spy": None,
54
  "warn": f"SPY fetch failed ({type(e).__name__}); defaulting to OFFENSE"}
55
 
56
 
57
- def equity_sleeve(token):
58
- p = hf_hub_download(PANEL_REPO, "marketfm_snapshot.parquet", repo_type="dataset", token=token)
59
- df = pd.read_parquet(p)
60
  df = df[df["marketcap"].notna() & (df["marketcap"] > 0)].copy()
61
  df = df.sort_values("marketcap", ascending=False).head(SEG_K)
62
- df["score"] = df["n_mom_12_1"].fillna(0) + df["n_roe"].fillna(0)
63
  df = df.sort_values("score", ascending=False)
64
  picked, sec = [], {}
65
  for _, r in df.iterrows():
@@ -75,18 +102,37 @@ def equity_sleeve(token):
75
  for r, wi in zip(picked, w)]
76
 
77
 
 
 
 
 
 
 
78
  def get_target(token):
79
- reg = detect_regime()
80
- sleeve = equity_sleeve(token)
 
 
81
  if reg["offense"]:
82
  rows = [dict(x) for x in sleeve]
83
  else:
84
- rows = [dict(x, weight=DEF_EQ * x["weight"]) for x in sleeve]
85
- for sym, (nm, sct) in ETF.items():
86
- rows.append({"ticker": sym, "name": nm, "sector": sct,
87
- "weight": DEF_AGG if sym == "AGG" else DEF_GLD})
 
 
 
 
 
 
 
 
 
 
 
88
  weights = {r["ticker"]: r["weight"] for r in rows}
89
- return {"regime": reg, "weights": weights, "rows": rows}
90
 
91
 
92
  def compute_orders(weights, positions, equity, band_pct=0.005, invest_frac=0.995, force_sells=False):
 
42
  return w
43
 
44
 
45
+ DEFAULT_CFG = {"signal": ["n_mom_12_1", "n_roe"], "regime": "cross10/40",
46
+ "defense": "mom60_agg20_gld20", "cadence_weeks": 1, "version": 1}
47
+
48
+
49
+ def load_config(token):
50
+ """Live strategy config written by the autonomous recalibration agent (falls back to the incumbent)."""
51
  try:
52
+ c = json.load(open(hf_hub_download(PANEL_REPO, "strategy_config.json", repo_type="dataset", token=token, force_download=True)))
53
+ return {**DEFAULT_CFG, **{k: c[k] for k in DEFAULT_CFG if k in c}}
54
+ except Exception:
55
+ return dict(DEFAULT_CFG)
56
+
57
+
58
+ def _spy_series():
59
+ u = f"https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY_ADJUSTED&symbol=SPY&apikey={AV}"
60
+ ts = json.load(urllib.request.urlopen(u, timeout=25, context=_CTX))["Weekly Adjusted Time Series"]
61
+ return pd.Series({k: float(v["5. adjusted close"]) for k, v in ts.items()}).sort_index()
62
+
63
+
64
+ def detect_regime(rule="cross10/40"):
65
+ try:
66
+ s = _spy_series()
67
+ p = float(s.iloc[-1]); s10 = float(s.rolling(10).mean().iloc[-1]); s20 = float(s.rolling(20).mean().iloc[-1])
68
+ s40 = float(s.rolling(40).mean().iloc[-1]); p_prev = float(s.iloc[-2]); s40_prev = float(s.rolling(40).mean().iloc[-2])
69
+ if rule == "single40":
70
+ off = p >= s40
71
+ elif rule == "dual20/40":
72
+ off = True if (p >= s20 and p >= s40) else (False if (p < s20 and p < s40) else (s10 >= s40))
73
+ elif rule == "hyst5":
74
+ off = True if p > 1.05 * s40 else (False if p < 0.95 * s40 else (s10 >= s40))
75
+ elif rule == "confirm2":
76
+ cur, prev = p >= s40, p_prev >= s40_prev
77
+ off = cur if cur == prev else (s10 >= s40)
78
+ else: # cross10/40 (incumbent)
79
+ off = s10 >= s40
80
+ return {"offense": bool(off), "rule": rule, "sma10": s10, "sma40": s40, "spy": p}
81
  except Exception as e:
82
+ return {"offense": True, "rule": rule, "sma10": None, "sma40": None, "spy": None,
83
  "warn": f"SPY fetch failed ({type(e).__name__}); defaulting to OFFENSE"}
84
 
85
 
86
+ def _rank_sleeve(df, cols, sign=1):
 
 
87
  df = df[df["marketcap"].notna() & (df["marketcap"] > 0)].copy()
88
  df = df.sort_values("marketcap", ascending=False).head(SEG_K)
89
+ df["score"] = sum(sign * df[c].fillna(0) for c in cols)
90
  df = df.sort_values("score", ascending=False)
91
  picked, sec = [], {}
92
  for _, r in df.iterrows():
 
102
  for r, wi in zip(picked, w)]
103
 
104
 
105
+ def equity_sleeve(token, cols=None):
106
+ cols = cols or DEFAULT_CFG["signal"]
107
+ p = hf_hub_download(PANEL_REPO, "marketfm_snapshot.parquet", repo_type="dataset", token=token)
108
+ return _rank_sleeve(pd.read_parquet(p), cols)
109
+
110
+
111
  def get_target(token):
112
+ cfg = load_config(token)
113
+ reg = detect_regime(cfg["regime"])
114
+ snap = pd.read_parquet(hf_hub_download(PANEL_REPO, "marketfm_snapshot.parquet", repo_type="dataset", token=token))
115
+ sleeve = _rank_sleeve(snap, cfg["signal"])
116
  if reg["offense"]:
117
  rows = [dict(x) for x in sleeve]
118
  else:
119
+ d = cfg["defense"]
120
+ if d == "agg50_gld50":
121
+ rows = []
122
+ eq_frac, agg_w, gld_w = 0.0, 0.5, 0.5
123
+ elif d == "lowvol":
124
+ rows = _rank_sleeve(snap, ["n_vol_13w"], sign=-1)
125
+ eq_frac, agg_w, gld_w = 1.0, 0.0, 0.0
126
+ elif d == "mom40_agg30_gld30":
127
+ rows = [dict(x, weight=0.4 * x["weight"]) for x in sleeve]; eq_frac, agg_w, gld_w = 0.4, 0.3, 0.3
128
+ else: # mom60_agg20_gld20 (incumbent)
129
+ rows = [dict(x, weight=DEF_EQ * x["weight"]) for x in sleeve]; eq_frac, agg_w, gld_w = DEF_EQ, DEF_AGG, DEF_GLD
130
+ for sym, wv in [("AGG", agg_w), ("GLD", gld_w)]:
131
+ if wv > 0:
132
+ nm, sct = ETF[sym]
133
+ rows.append({"ticker": sym, "name": nm, "sector": sct, "weight": wv})
134
  weights = {r["ticker"]: r["weight"] for r in rows}
135
+ return {"regime": reg, "weights": weights, "rows": rows, "config": cfg}
136
 
137
 
138
  def compute_orders(weights, positions, equity, band_pct=0.005, invest_frac=0.995, force_sells=False):