DeepQuant / rebalance_engine.py
s-ttp's picture
Upload folder using huggingface_hub
e48ec63 verified
Raw
History Blame Contribute Delete
4.98 kB
"""DeepQuant — deterministic rebalance engine (Phase 1: compute only, no execution).
Reproduces the MarketFM Tactical target portfolio and diffs it against the live account in
DOLLARS, trading only the change. No order is placed here — compute_orders returns the list
the agent *would* submit. Execution (Phase 2) consumes this same output, guardrailed.
"""
import json
import os
import ssl
import urllib.request
import numpy as np
import pandas as pd
from huggingface_hub import hf_hub_download
try:
import certifi
_CTX = ssl.create_default_context(cafile=certifi.where())
except Exception:
_CTX = ssl.create_default_context()
PANEL_REPO = os.environ.get("MARKETFM_REPO", "s-ttp/marketfm-panel")
AV = os.environ.get("ALPHAVANTAGE_API_KEY", "")
SEG_K, SECTOR_CAP, SIZE, MAX_W = 1000, 5, 50, 0.10 # top-1000 by mktcap, ≤5/sector, 50 names, 10% cap
DEF_EQ, DEF_AGG, DEF_GLD = 0.60, 0.20, 0.20 # defense: 60% stocks + 20% bonds + 20% gold
ETF = {"AGG": ("iShares Core US Aggregate Bond ETF", "Bonds (ETF)"),
"GLD": ("SPDR Gold Shares", "Gold (ETF)")}
def cap_weights(w, mw=MAX_W):
w = np.asarray(w, float)
for _ in range(30):
over = w > mw + 1e-9
if not over.any():
break
excess = (w[over] - mw).sum()
w[over] = mw
under = ~over
if w[under].sum() <= 0:
break
w[under] += excess * w[under] / w[under].sum()
return w
def detect_regime():
try:
u = f"https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY_ADJUSTED&symbol=SPY&apikey={AV}"
ts = json.load(urllib.request.urlopen(u, timeout=25, context=_CTX))["Weekly Adjusted Time Series"]
s = pd.Series({k: float(v["5. adjusted close"]) for k, v in ts.items()}).sort_index()
sma10, sma40 = s.rolling(10).mean().iloc[-1], s.rolling(40).mean().iloc[-1]
return {"offense": bool(sma10 >= sma40), "sma10": float(sma10), "sma40": float(sma40), "spy": float(s.iloc[-1])}
except Exception as e:
return {"offense": True, "sma10": None, "sma40": None, "spy": None,
"warn": f"SPY fetch failed ({type(e).__name__}); defaulting to OFFENSE"}
def equity_sleeve(token):
p = hf_hub_download(PANEL_REPO, "marketfm_snapshot.parquet", repo_type="dataset", token=token)
df = pd.read_parquet(p)
df = df[df["marketcap"].notna() & (df["marketcap"] > 0)].copy()
df = df.sort_values("marketcap", ascending=False).head(SEG_K)
df["score"] = df["n_mom_12_1"].fillna(0) + df["n_roe"].fillna(0)
df = df.sort_values("score", ascending=False)
picked, sec = [], {}
for _, r in df.iterrows():
if sec.get(r["sector"], 0) >= SECTOR_CAP:
continue
sec[r["sector"]] = sec.get(r["sector"], 0) + 1
picked.append(r)
if len(picked) >= SIZE:
break
mc = np.array([r["marketcap"] for r in picked], float)
w = cap_weights(mc / mc.sum())
return [{"ticker": r["ticker"], "name": r.get("name"), "sector": r["sector"], "weight": float(wi)}
for r, wi in zip(picked, w)]
def get_target(token):
reg = detect_regime()
sleeve = equity_sleeve(token)
if reg["offense"]:
rows = [dict(x) for x in sleeve]
else:
rows = [dict(x, weight=DEF_EQ * x["weight"]) for x in sleeve]
for sym, (nm, sct) in ETF.items():
rows.append({"ticker": sym, "name": nm, "sector": sct,
"weight": DEF_AGG if sym == "AGG" else DEF_GLD})
weights = {r["ticker"]: r["weight"] for r in rows}
return {"regime": reg, "weights": weights, "rows": rows}
def compute_orders(weights, positions, equity, band_pct=0.005):
"""positions: {symbol: market_value $}. Returns order list (sells first), trading only the delta."""
band = band_pct * equity
orders = []
for sym in set(weights) | set(positions):
tw = weights.get(sym, 0.0)
tgt = tw * equity
cur = positions.get(sym, 0.0)
delta = tgt - cur
cw = (cur / equity) if equity else 0.0
if tw == 0 and cur > 0: # dropped from basket → full exit (always)
orders.append({"symbol": sym, "side": "SELL", "delta": -cur, "cur_w": cw, "tgt_w": 0.0, "exit": True})
elif cur == 0 and tgt > 1.0: # establish a new target position (always; band can't block it)
orders.append({"symbol": sym, "side": "BUY", "delta": tgt, "cur_w": 0.0, "tgt_w": tw, "exit": False})
elif abs(delta) < band: # churn guard: skip small adjustments to EXISTING positions
continue
else:
orders.append({"symbol": sym, "side": "BUY" if delta > 0 else "SELL",
"delta": delta, "cur_w": cw, "tgt_w": tw, "exit": False})
orders.sort(key=lambda o: (0 if o["side"] == "SELL" else 1, -abs(o["delta"]))) # sells first
return orders