Spaces:
Sleeping
Sleeping
Upload signal_generator.py
Browse files- signal_generator.py +74 -1
signal_generator.py
CHANGED
|
@@ -25,12 +25,15 @@ sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
| 25 |
|
| 26 |
from core.config import (
|
| 27 |
TICKERS, STARTING_CAP, LEVERAGE, MIN_CONFIDENCE,
|
| 28 |
-
SIGNAL_HOUR, SIGNAL_MINUTE, TRADE_LOG,
|
| 29 |
)
|
| 30 |
from core.features import extract_semantic_features, extract_sequential_features
|
| 31 |
from core.models import train_models
|
| 32 |
from core.groww import fetch_groww_candles
|
| 33 |
|
|
|
|
|
|
|
|
|
|
| 34 |
IST = ZoneInfo("Asia/Kolkata")
|
| 35 |
SIGNALS_FILE = os.path.join(os.path.dirname(__file__), "signals.json")
|
| 36 |
|
|
@@ -44,6 +47,69 @@ if not logger.handlers:
|
|
| 44 |
logger.addHandler(_ch)
|
| 45 |
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
# ββ Trade Journal ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 48 |
|
| 49 |
def load_trade_journal():
|
|
@@ -87,6 +153,7 @@ def already_traded_today(trades, today_str):
|
|
| 87 |
def generate_signals():
|
| 88 |
"""
|
| 89 |
Full signal generation pipeline:
|
|
|
|
| 90 |
1. Train models on parquet data
|
| 91 |
2. Fetch live candles from Groww
|
| 92 |
3. Extract features from 09:15-09:30 window
|
|
@@ -99,6 +166,12 @@ def generate_signals():
|
|
| 99 |
|
| 100 |
logger.info(f"Starting signal generation for {today_str}...")
|
| 101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
# Step 1: Train models
|
| 103 |
logger.info("Training models on historical minute data...")
|
| 104 |
models = train_models(log_fn=logger.info)
|
|
|
|
| 25 |
|
| 26 |
from core.config import (
|
| 27 |
TICKERS, STARTING_CAP, LEVERAGE, MIN_CONFIDENCE,
|
| 28 |
+
SIGNAL_HOUR, SIGNAL_MINUTE, TRADE_LOG, DATA_DIR,
|
| 29 |
)
|
| 30 |
from core.features import extract_semantic_features, extract_sequential_features
|
| 31 |
from core.models import train_models
|
| 32 |
from core.groww import fetch_groww_candles
|
| 33 |
|
| 34 |
+
import pandas as pd
|
| 35 |
+
import numpy as np
|
| 36 |
+
|
| 37 |
IST = ZoneInfo("Asia/Kolkata")
|
| 38 |
SIGNALS_FILE = os.path.join(os.path.dirname(__file__), "signals.json")
|
| 39 |
|
|
|
|
| 47 |
logger.addHandler(_ch)
|
| 48 |
|
| 49 |
|
| 50 |
+
# ββ Minute Data Updater βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 51 |
+
|
| 52 |
+
def update_minute_training_data():
|
| 53 |
+
"""
|
| 54 |
+
Refresh minute OHLCV parquet files with the latest candle data from Groww.
|
| 55 |
+
Fetches the last 5 days for each ticker and appends any new rows that
|
| 56 |
+
aren't already in the parquet, so the training data stays current.
|
| 57 |
+
"""
|
| 58 |
+
import time as _time
|
| 59 |
+
|
| 60 |
+
logger.info("Updating minute OHLCV training data from Groww...")
|
| 61 |
+
updated_count = 0
|
| 62 |
+
|
| 63 |
+
for ticker in TICKERS:
|
| 64 |
+
fpath = DATA_DIR / f"{ticker}_minute.parquet"
|
| 65 |
+
|
| 66 |
+
try:
|
| 67 |
+
df_live = fetch_groww_candles(ticker, days=5)
|
| 68 |
+
if df_live is None or df_live.empty:
|
| 69 |
+
logger.warning(f"[{ticker}] No live candles fetched, skipping update.")
|
| 70 |
+
continue
|
| 71 |
+
|
| 72 |
+
# Prepare live data for merge
|
| 73 |
+
df_new = df_live.copy()
|
| 74 |
+
df_new.index.name = "date"
|
| 75 |
+
df_new = df_new.reset_index()
|
| 76 |
+
df_new["date"] = pd.to_datetime(df_new["date"])
|
| 77 |
+
|
| 78 |
+
if fpath.exists():
|
| 79 |
+
df_existing = pd.read_parquet(fpath)
|
| 80 |
+
df_existing["date"] = pd.to_datetime(df_existing["date"])
|
| 81 |
+
|
| 82 |
+
# Find the latest timestamp in existing data
|
| 83 |
+
max_existing = df_existing["date"].max()
|
| 84 |
+
|
| 85 |
+
# Only keep new rows that are after the existing max
|
| 86 |
+
df_append = df_new[df_new["date"] > max_existing]
|
| 87 |
+
|
| 88 |
+
if df_append.empty:
|
| 89 |
+
continue
|
| 90 |
+
|
| 91 |
+
df_merged = pd.concat([df_existing, df_append], ignore_index=True)
|
| 92 |
+
df_merged.sort_values("date", inplace=True)
|
| 93 |
+
df_merged.drop_duplicates(subset=["date"], keep="last", inplace=True)
|
| 94 |
+
new_count = len(df_append)
|
| 95 |
+
else:
|
| 96 |
+
df_merged = df_new
|
| 97 |
+
new_count = len(df_new)
|
| 98 |
+
|
| 99 |
+
df_merged.to_parquet(fpath, index=False)
|
| 100 |
+
logger.info(f"[{ticker}] Updated parquet with {new_count} new candles")
|
| 101 |
+
updated_count += 1
|
| 102 |
+
|
| 103 |
+
except Exception as e:
|
| 104 |
+
logger.error(f"[{ticker}] Failed to update minute data: {e}")
|
| 105 |
+
logger.debug(traceback.format_exc())
|
| 106 |
+
|
| 107 |
+
_time.sleep(0.3) # Rate limit
|
| 108 |
+
|
| 109 |
+
logger.info(f"Minute data update complete. {updated_count}/{len(TICKERS)} tickers refreshed.")
|
| 110 |
+
return updated_count
|
| 111 |
+
|
| 112 |
+
|
| 113 |
# ββ Trade Journal ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 114 |
|
| 115 |
def load_trade_journal():
|
|
|
|
| 153 |
def generate_signals():
|
| 154 |
"""
|
| 155 |
Full signal generation pipeline:
|
| 156 |
+
0. Update minute OHLCV training data from Groww
|
| 157 |
1. Train models on parquet data
|
| 158 |
2. Fetch live candles from Groww
|
| 159 |
3. Extract features from 09:15-09:30 window
|
|
|
|
| 166 |
|
| 167 |
logger.info(f"Starting signal generation for {today_str}...")
|
| 168 |
|
| 169 |
+
# Step 0: Update training data so models learn from recent market behavior
|
| 170 |
+
try:
|
| 171 |
+
update_minute_training_data()
|
| 172 |
+
except Exception as e:
|
| 173 |
+
logger.warning(f"Minute data update failed (non-fatal): {e}")
|
| 174 |
+
|
| 175 |
# Step 1: Train models
|
| 176 |
logger.info("Training models on historical minute data...")
|
| 177 |
models = train_models(log_fn=logger.info)
|