diff --git a/src/main.py b/src/main.py index 9c1b1b2..61b3a6d 100644 --- a/src/main.py +++ b/src/main.py @@ -2,38 +2,13 @@ import warnings from urllib3.exceptions import NotOpenSSLWarning warnings.filterwarnings('ignore', category=NotOpenSSLWarning) -from trading.journal import (create_trades_table, TradeEntry, add_trade, - update_trade_exit, get_open_trades, get_trade_history) +from trading.menu import print_main_menu, print_technical_scanner_menu +from trading.journal import journal_menu +from screener.scanner_controller import run_technical_scanner +from screener.canslim_controller import run_canslim_screener +from trading.main import main as trading_main -import datetime -from screener.data_fetcher import validate_date_range, fetch_financial_data, get_stocks_in_time_range -from screener.t_sunnyband import run_sunny_scanner -from screener.t_atr_ema import run_atr_ema_scanner -from screener.c_canslim import check_quarterly_earnings, check_return_on_equity, check_sales_growth -from screener.a_canslim import check_annual_eps_growth -from screener.l_canslim import check_industry_leadership -from screener.i_canslim import check_institutional_sponsorship -from screener.csv_appender import append_scores_to_csv -from screener.screeners import SCREENERS -from screener.user_input import get_user_screener_selection, get_interval_choice -from indicators.three_atr_ema import ThreeATREMAIndicator - -def get_float_input(prompt: str) -> float: - while True: - try: - return float(input(prompt)) - except ValueError: - print("Please enter a valid number") - -def get_scanner_parameters(): - """Get user input for scanner parameters""" - min_price = get_float_input("Enter minimum stock price ($): ") - max_price = get_float_input("Enter maximum stock price ($): ") - min_volume = int(input("Enter minimum volume: ")) - portfolio_size = get_float_input("Enter portfolio size ($) or 0 to skip position sizing: ") - return min_price, max_price, min_volume, portfolio_size - -def journal_menu(): +def main(): create_trades_table() # Ensure table exists while True: diff --git a/src/screener/canslim_controller.py b/src/screener/canslim_controller.py new file mode 100644 index 0000000..3488146 --- /dev/null +++ b/src/screener/canslim_controller.py @@ -0,0 +1,71 @@ +from screener.data_fetcher import validate_date_range, fetch_financial_data, get_stocks_in_time_range +from screener.user_input import get_user_screener_selection +from screener.c_canslim import check_quarterly_earnings, check_return_on_equity, check_sales_growth +from screener.a_canslim import check_annual_eps_growth +from screener.l_canslim import check_industry_leadership +from screener.i_canslim import check_institutional_sponsorship +from screener.csv_appender import append_scores_to_csv + +def run_canslim_screener(): + """Run the CANSLIM screener""" + user_start_date = input("Enter start date (YYYY-MM-DD): ") + user_end_date = input("Enter end date (YYYY-MM-DD): ") + + start_date, end_date = validate_date_range(user_start_date, user_end_date, required_quarters=4) + selected_screeners = get_user_screener_selection() + symbol_list = get_stocks_in_time_range(start_date, end_date) + + if not symbol_list: + print("No stocks found within the given date range.") + return + + print(f"Processing {len(symbol_list)} stocks within the given date range...\n") + + for symbol in symbol_list: + data = fetch_financial_data(symbol, start_date, end_date) + process_symbol(symbol, data, selected_screeners) + + print("✅ Scores saved in data/metrics/stock_scores.csv\n") + +def process_symbol(symbol, data, selected_screeners): + """Process individual symbol for CANSLIM screening""" + if not + print(f"⚠️ Warning: No data returned for {symbol}. Assigning default score.\n") + scores = {screener: 0.25 for category in selected_screeners + for screener in selected_screeners[category]} + else: + scores = calculate_scores(symbol, data, selected_screeners) + + scores["Total_Score"] = sum(scores.values()) + append_scores_to_csv(symbol, scores) + +def calculate_scores(symbol, data, selected_screeners): + """Calculate scores for each selected screener""" + scores = {} + for category, screeners in selected_screeners.items(): + for screener, threshold in screeners.items(): + scores[screener] = get_screener_score( + screener, data, symbol, threshold + ) + return scores + +def get_screener_score(screener, data, symbol, threshold): + """Get score for specific screener""" + if screener == "EPS_Score": + score = check_quarterly_earnings(data.get("quarterly_eps", [])) + elif screener == "Annual_EPS_Score": + score = check_annual_eps_growth(data.get("annual_eps", [])) + elif screener == "Sales_Score": + score = check_sales_growth(data.get("sales_growth", [])) + elif screener == "ROE_Score": + score = check_return_on_equity(data.get("roe", [])) + elif screener == "L_Score": + score = check_industry_leadership(symbol) + print(f"🟢 {symbol} - L_Score: {score}") + elif screener == "I_Score": + score = check_institutional_sponsorship(symbol) + print(f"🏢 {symbol} - I_Score: {score}") + else: + score = 0 + + return score >= threshold if isinstance(threshold, (int, float)) else score diff --git a/src/screener/scanner_controller.py b/src/screener/scanner_controller.py new file mode 100644 index 0000000..f90b903 --- /dev/null +++ b/src/screener/scanner_controller.py @@ -0,0 +1,25 @@ +from screener.t_sunnyband import run_sunny_scanner +from screener.t_atr_ema import run_atr_ema_scanner +from screener.t_atr_ema_v2 import run_atr_ema_scanner_v2 +from utils.data_utils import get_float_input + +def get_scanner_parameters(): + """Get user input for scanner parameters""" + min_price = get_float_input("Enter minimum stock price ($): ") + max_price = get_float_input("Enter maximum stock price ($): ") + min_volume = int(input("Enter minimum volume: ")) + portfolio_size = get_float_input("Enter portfolio size ($) or 0 to skip position sizing: ") + return min_price, max_price, min_volume, portfolio_size + +def run_technical_scanner(scanner_choice: str): + """Run the selected technical scanner""" + min_price, max_price, min_volume, portfolio_size = get_scanner_parameters() + + if scanner_choice == "1": + run_sunny_scanner(min_price, max_price, min_volume, portfolio_size) + elif scanner_choice == "2": + run_atr_ema_scanner(min_price, max_price, min_volume, portfolio_size) + elif scanner_choice == "3": + run_atr_ema_scanner_v2(min_price, max_price, min_volume, portfolio_size) + else: + print("Invalid scanner choice.") diff --git a/src/trading/menu.py b/src/trading/menu.py new file mode 100644 index 0000000..ecd1b44 --- /dev/null +++ b/src/trading/menu.py @@ -0,0 +1,13 @@ +def print_main_menu(): + print("\nStock Analysis System") + print("1. Run CANSLIM Screener") + print("2. Run Technical Scanners (SunnyBands/ATR-EMA)") + print("3. Launch Trading System") + print("4. Trading Journal") + print("5. Exit") + +def print_technical_scanner_menu(): + print("\nTechnical Scanner Options:") + print("1. SunnyBands Scanner") + print("2. Standard ATR-EMA Scanner") + print("3. Enhanced ATR-EMA v2 Scanner") diff --git a/src/utils/data_utils.py b/src/utils/data_utils.py index 2fd2bca..325cc4b 100644 --- a/src/utils/data_utils.py +++ b/src/utils/data_utils.py @@ -5,6 +5,13 @@ from db.db_connection import create_client from screener.user_input import get_interval_choice, get_date_range from trading.position_calculator import PositionCalculator +def get_float_input(prompt: str) -> float: + while True: + try: + return float(input(prompt)) + except ValueError: + print("Please enter a valid number") + def validate_signal_date(signal_date: datetime) -> datetime: """ Validate and adjust signal date if needed