feat: srategy builder
This commit is contained in:
@@ -292,65 +292,3 @@ def snapshot_options_chain(symbol: str, target_days: int = 30) -> List[Dict[str,
|
||||
if not rows:
|
||||
raise ValueError(f"Snapshot Saxo vide pour '{symbol}' (clés reçues: {list(snapshot.keys())})")
|
||||
return rows
|
||||
|
||||
|
||||
def get_chain_slice_saxo(symbol: str, target_days: int = 8, n_expiries: int = 3) -> Dict[str, Any]:
|
||||
"""
|
||||
Saxo-backed equivalent of services/option_chain.get_chain_slice — same output shape
|
||||
({symbol, proxy, spot, expiries: [{expiry_date, days_to_expiry, calls, puts}]}, each row
|
||||
{strike, bid, ask, mid, last, iv, open_interest, volume}) so vol_surface.py/strategy_engine.py
|
||||
work unchanged regardless of data source. Reuses snapshot_options_chain (already flat,
|
||||
already bid/ask/mid/greeks per contract) and reshapes/filters it down to n_expiries.
|
||||
"""
|
||||
instrument = resolve_instrument(symbol)
|
||||
flat_rows = snapshot_options_chain(symbol)
|
||||
spot = flat_rows[0]["spot"] if flat_rows else None
|
||||
today = date.today()
|
||||
|
||||
by_expiry: Dict[str, List[Dict[str, Any]]] = {}
|
||||
for r in flat_rows:
|
||||
if r.get("expiry_date"):
|
||||
by_expiry.setdefault(r["expiry_date"], []).append(r)
|
||||
|
||||
def _days_to(expiry_date: str) -> int:
|
||||
return (datetime.strptime(expiry_date[:10], "%Y-%m-%d").date() - today).days
|
||||
|
||||
selected = sorted(by_expiry.keys(), key=lambda e: abs(_days_to(e) - target_days))[:max(1, n_expiries)]
|
||||
|
||||
def _row_shape(r: Dict[str, Any]) -> Dict[str, Any]:
|
||||
bid = r.get("bid") or 0.0
|
||||
ask = r.get("ask") or 0.0
|
||||
mid = r.get("mid") or (round((bid + ask) / 2, 4) if (bid > 0 and ask > 0) else 0.0)
|
||||
vol_pct = r.get("volatility_pct")
|
||||
return {
|
||||
"strike": float(r["strike"]),
|
||||
"bid": float(bid),
|
||||
"ask": float(ask),
|
||||
"mid": float(mid),
|
||||
"last": float(mid), # Saxo's chain snapshot has no separate last-traded field
|
||||
"iv": float(vol_pct) / 100.0 if vol_pct is not None else 0.0,
|
||||
"open_interest": 0,
|
||||
"volume": 0,
|
||||
}
|
||||
|
||||
expiries_out = []
|
||||
for expiry_date in sorted(selected, key=_days_to):
|
||||
rows = by_expiry[expiry_date]
|
||||
calls = sorted([_row_shape(r) for r in rows if r["option_type"] == "call"], key=lambda x: x["strike"])
|
||||
puts = sorted([_row_shape(r) for r in rows if r["option_type"] == "put"], key=lambda x: x["strike"])
|
||||
expiries_out.append({
|
||||
"expiry_date": expiry_date,
|
||||
"days_to_expiry": _days_to(expiry_date),
|
||||
"calls": calls,
|
||||
"puts": puts,
|
||||
})
|
||||
|
||||
if not expiries_out:
|
||||
raise ValueError(f"Aucune chaîne exploitable pour '{symbol}' via Saxo")
|
||||
|
||||
return {
|
||||
"symbol": symbol.upper(),
|
||||
"proxy": instrument["symbol"] or symbol.upper(),
|
||||
"spot": round(float(spot), 4) if spot is not None else None,
|
||||
"expiries": expiries_out,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user