From 86686125f33203fc7f45e4a15d9c288ea77a0b2f Mon Sep 17 00:00:00 2001 From: Bobby Date: Sat, 8 Feb 2025 10:53:21 -0800 Subject: [PATCH] feat: Add ATR EMA target scanner for stock analysis --- src/screener/t_atr_ema_v2.py | 126 +++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/screener/t_atr_ema_v2.py diff --git a/src/screener/t_atr_ema_v2.py b/src/screener/t_atr_ema_v2.py new file mode 100644 index 0000000..747b4bd --- /dev/null +++ b/src/screener/t_atr_ema_v2.py @@ -0,0 +1,126 @@ +import os +import pandas as pd +from datetime import datetime, timedelta +from db.db_connection import create_client +from indicators.three_atr_ema import ThreeATREMAIndicator +from trading.position_calculator import PositionCalculator +from screener.t_sunnyband import get_stock_data + +def run_atr_ema_target_scanner(min_price: float, max_price: float, min_volume: int, portfolio_size: float = None): + print(f"\nšŸ” Scanning for stocks ${min_price:.2f}-${max_price:.2f} with min volume {min_volume:,}") + + # Set time range + end_date = datetime.now() + start_date = end_date - timedelta(days=1) # Last trading day + start_ts = int(start_date.timestamp() * 1000000000) + end_ts = int(end_date.timestamp() * 1000000000) + + client = create_client() + + try: + # Fetch stock prices within the defined range + query = f""" + WITH latest_data AS ( + SELECT + ticker, + argMax(close, window_start) AS last_close, + sum(volume) AS total_volume, + max(window_start) AS last_update + FROM stock_db.stock_prices + WHERE window_start BETWEEN {start_ts} AND {end_ts} + GROUP BY ticker + HAVING last_close BETWEEN {min_price} AND {max_price} + AND total_volume >= {min_volume} + ) + SELECT + ticker, + last_close, + total_volume, + last_update + FROM latest_data + ORDER BY ticker + """ + + result = client.query(query) + stocks = [(row[0], row[1], row[2], row[3]) for row in result.result_rows] + + if not stocks: + print("āŒ No stocks found matching criteria.") + return + + print(f"\nāœ… Found {len(stocks)} stocks matching criteria") + + indicator = ThreeATREMAIndicator() + calculator = PositionCalculator(portfolio_size, risk_percentage=1.0, stop_loss_percentage=7.0) if portfolio_size else None + bullish_signals = [] + + for ticker, current_price, current_volume, last_update in stocks: + try: + df = get_stock_data(ticker, start_date, end_date, "1D") + if df.empty or len(df) < 50: + continue + + results = indicator.calculate(df) + last_row = results.iloc[-1] + prev_row = results.iloc[-2] + + # Check if entry condition is met + bullish_entry = ( + last_row['close'] < last_row['ema'] and + prev_row['close'] <= prev_row['lower_band'] and + last_row['close'] > prev_row['close'] + ) + + if bullish_entry: + entry_price = last_row['close'] + target_1 = entry_price * 1.10 # 10% profit + target_2 = entry_price * 1.20 # 20% profit + + # Trailing stop logic + trail_stop = None + trail_active = False + + if last_row['close'] >= last_row['upper_band']: + trail_active = True + highest_price = max(results['high'].iloc[-5:]) # Last 5 days + trail_stop = highest_price * 0.98 # 2% below high + + # Position sizing + position = calculator.calculate_position_size(entry_price, target_2) if calculator else None + position_size = position['position_value'] if position else None + + # Save signal + signal_data = { + 'ticker': ticker, + 'entry_price': entry_price, + 'target_1': target_1, + 'target_2': target_2, + 'volume': current_volume, + 'last_update': datetime.fromtimestamp(last_update / 1000000000), + 'trail_stop': trail_stop, + 'position_size': position_size + } + bullish_signals.append(signal_data) + + # Print result + print(f"\n🟢 {ticker} @ ${entry_price:.2f}") + print(f" šŸŽÆ Target 1: ${target_1:.2f} | Target 2: ${target_2:.2f}") + if trail_active: + print(f" 🚨 Trailing Stop: ${trail_stop:.2f}") + + except Exception as e: + print(f"āŒ Error processing {ticker}: {e}") + continue + + # Save results + if bullish_signals: + output_dir = 'reports' + os.makedirs(output_dir, exist_ok=True) + output_file = f'{output_dir}/atr_ema_targets_{datetime.now().strftime("%Y%m%d_%H%M")}.csv' + pd.DataFrame(bullish_signals).to_csv(output_file, index=False) + print(f"\nšŸ“ Saved bullish signals to {output_file}") + else: + print("āŒ No bullish signals found.") + + except Exception as e: + print(f"āŒ Error during scan: {e}") \ No newline at end of file