diff --git a/main.py b/main.py index 4ad4fd0..14b29b8 100644 --- a/main.py +++ b/main.py @@ -74,7 +74,19 @@ def run_ticker_lifecycle(client, yf_ticker, t212_ticker, tz): if now.hour == 9 and now.minute >= 45: logger.info(f"Evaluating opening candle for {yf_ticker}...") - if strategy.check_setup(): + + # Retry loop: wait for yfinance to publish the 09:30-09:45 candle (up to 3 minutes) + setup_found = False + max_retries = 12 + for attempt in range(max_retries): + if strategy.check_setup(): + setup_found = True + break + elif attempt < max_retries - 1: + logger.debug(f"Data not ready for {yf_ticker} yet, waiting 15s...") + time.sleep(15) + + if setup_found: params = strategy.get_trade_params() params['ticker'] = t212_ticker diff --git a/src/strategy/scanner.py b/src/strategy/scanner.py index 0b402e0..e5fc8ca 100644 --- a/src/strategy/scanner.py +++ b/src/strategy/scanner.py @@ -40,14 +40,20 @@ def scan_for_candidates(tickers: List[str] = DEFAULT_TICKERS, min_price: float = df.ta.atr(length=14, append=True) - latest = df.iloc[-1] + # Safely get the close price, falling back to yesterday if today's is NaN (common at exactly 09:30) + close_price = df['Close'].iloc[-1] + if pd.isna(close_price) and len(df) > 1: + close_price = df['Close'].iloc[-2] + yesterday_atr = df['ATRr_14'].iloc[-2] - close_price = latest['Close'] + # Safely get avg volume avg_volume = df['Volume'].tail(14).mean() + if pd.isna(avg_volume): + avg_volume = 0 # Filters - if close_price < min_price or avg_volume < min_volume or pd.isna(yesterday_atr): + if pd.isna(close_price) or close_price < min_price or avg_volume < min_volume or pd.isna(yesterday_atr): continue atr_percent = (yesterday_atr / close_price) * 100