feat: strategy builder
This commit is contained in:
@@ -10,13 +10,22 @@ from datetime import date, datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
def get_chain_slice(symbol: str, target_days: int = 8, n_expiries: int = 3) -> Dict[str, Any]:
|
||||
def get_chain_slice(
|
||||
symbol: str, target_days: int = 8, n_expiries: int = 3,
|
||||
dte_min: Optional[int] = None, dte_max: Optional[int] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Builds a chain slice from the latest accumulated Saxo snapshot rows for `symbol`
|
||||
(services/database.get_latest_saxo_snapshot_rows). Returns the `n_expiries`
|
||||
expirations closest to target_days, each with calls/puts rows shaped
|
||||
{strike, bid, ask, mid, last, iv, open_interest, volume} — same shape regardless
|
||||
of source, so vol_surface.py/strategy_engine.py need no changes.
|
||||
|
||||
`dte_min`/`dte_max`, when given, restrict the candidate expiries to that DTE window
|
||||
before picking the `n_expiries` closest to target_days — lets a caller evaluate a
|
||||
scenario at a short horizon (e.g. target_days=8) while still building legs from
|
||||
longer-dated options (e.g. dte_min=20, dte_max=60), which target_days alone can't
|
||||
express since it drives both the evaluation date and (until now) the expiry pick.
|
||||
"""
|
||||
from services.database import get_latest_saxo_snapshot_rows
|
||||
|
||||
@@ -39,7 +48,18 @@ def get_chain_slice(symbol: str, target_days: int = 8, n_expiries: int = 3) -> D
|
||||
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)]
|
||||
candidates = list(by_expiry.keys())
|
||||
if dte_min is not None or dte_max is not None:
|
||||
lo = dte_min if dte_min is not None else 0
|
||||
hi = dte_max if dte_max is not None else 10 ** 6
|
||||
candidates = [e for e in candidates if lo <= _days_to(e) <= hi]
|
||||
if not candidates:
|
||||
raise ValueError(
|
||||
f"Aucune échéance Saxo entre {dte_min}j et {dte_max}j pour '{symbol}' "
|
||||
f"— élargissez la fenêtre DTE ou laissez-la vide."
|
||||
)
|
||||
|
||||
selected = sorted(candidates, 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
|
||||
|
||||
Reference in New Issue
Block a user