Spaces:
Running
Running
| from pathlib import Path | |
| from datetime import datetime, timedelta | |
| import time as tm | |
| import pandas as pd | |
| from strategy_engine import scan_symbol_for_day, OUTPUT_COLUMNS | |
| BASE_DIR = Path(__file__).resolve().parent | |
| UNIVERSE_PATH = BASE_DIR / "option_stock_universe.csv" | |
| OUT_DIR = BASE_DIR / "outputs" | |
| OUT_DIR.mkdir(exist_ok=True) | |
| MODE = "priority" # "priority" or "all" | |
| VARIANT = "v2" | |
| OUTPUT_FILE = OUT_DIR / f"live_paper_trades_{MODE}_{VARIANT}.csv" | |
| def load_universe(): | |
| df = pd.read_csv(UNIVERSE_PATH) | |
| df["symbol"] = df["symbol"].astype(str).str.upper() | |
| df["sector"] = df["sector"].fillna("").astype(str) | |
| df["priority_rank"] = pd.to_numeric(df["priority_rank"], errors="coerce").fillna(999) | |
| if MODE == "priority": | |
| df = df[df["priority_rank"] == 1].copy() | |
| return df.reset_index(drop=True) | |
| def now_ist(): | |
| return pd.Timestamp.now(tz="Asia/Kolkata") | |
| def is_market_time(ts): | |
| if ts.weekday() >= 5: | |
| return False | |
| t = ts.time() | |
| return t >= datetime.strptime("09:30", "%H:%M").time() and t <= datetime.strptime("15:30", "%H:%M").time() | |
| def next_5min_boundary(ts): | |
| base = ts.floor("5min") | |
| if ts == base: | |
| return ts + pd.Timedelta(minutes=5) | |
| return base + pd.Timedelta(minutes=5) | |
| def load_existing(): | |
| if OUTPUT_FILE.exists(): | |
| df = pd.read_csv(OUTPUT_FILE) | |
| if "BUY TIME" in df.columns: | |
| df["BUY TIME"] = pd.to_datetime(df["BUY TIME"], errors="coerce") | |
| return df | |
| return pd.DataFrame(columns=OUTPUT_COLUMNS) | |
| def dedupe(existing_df, new_df): | |
| if new_df.empty: | |
| return existing_df | |
| combined = pd.concat([existing_df, new_df], ignore_index=True) | |
| combined["BUY TIME"] = pd.to_datetime(combined["BUY TIME"], errors="coerce") | |
| combined = combined.drop_duplicates( | |
| subset=["STOCK NAME", "OPTION SYMBOL", "CALL/PUT", "BUY TIME"], | |
| keep="first" | |
| ).sort_values("BUY TIME") | |
| return combined.reset_index(drop=True) | |
| def scan_once(): | |
| ts = now_ist() | |
| trade_date = str(ts.date()) | |
| universe = load_universe() | |
| option_cache = {} | |
| frames = [] | |
| print(f"\n[{ts}] Scanning {len(universe)} symbols | mode={MODE} | variant={VARIANT}") | |
| for _, row in universe.iterrows(): | |
| symbol = row["symbol"] | |
| sector = row["sector"] | |
| try: | |
| df = scan_symbol_for_day( | |
| stock_symbol=symbol, | |
| sector=sector, | |
| trade_date=trade_date, | |
| variant=VARIANT, | |
| option_cache=option_cache, | |
| ) | |
| if not df.empty: | |
| frames.append(df) | |
| except Exception as e: | |
| print(f"Error scanning {symbol}: {e}") | |
| if frames: | |
| new_df = pd.concat(frames, ignore_index=True) | |
| else: | |
| new_df = pd.DataFrame(columns=OUTPUT_COLUMNS) | |
| existing_df = load_existing() | |
| final_df = dedupe(existing_df, new_df) | |
| final_df.to_csv(OUTPUT_FILE, index=False) | |
| print(f"Saved {len(final_df)} total paper trades to {OUTPUT_FILE}") | |
| def main(): | |
| print("Starting live paper scanner...") | |
| print(f"Mode: {MODE}") | |
| print(f"Variant: {VARIANT}") | |
| print(f"Output: {OUTPUT_FILE}") | |
| while True: | |
| ts = now_ist() | |
| if is_market_time(ts): | |
| scan_once() | |
| nxt = next_5min_boundary(now_ist()) + pd.Timedelta(seconds=10) | |
| sleep_secs = max((nxt - now_ist()).total_seconds(), 5) | |
| print(f"Sleeping {int(sleep_secs)} seconds until next cycle...") | |
| tm.sleep(sleep_secs) | |
| else: | |
| print(f"[{ts}] Outside market time. Sleeping 60 seconds...") | |
| tm.sleep(60) | |
| if __name__ == "__main__": | |
| main() |