refactor: Modularize main application structure and separate concerns
This commit is contained in:
parent
590c6fd2b7
commit
3425539781
37
src/main.py
37
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:
|
||||
|
||||
71
src/screener/canslim_controller.py
Normal file
71
src/screener/canslim_controller.py
Normal file
@ -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
|
||||
25
src/screener/scanner_controller.py
Normal file
25
src/screener/scanner_controller.py
Normal file
@ -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.")
|
||||
13
src/trading/menu.py
Normal file
13
src/trading/menu.py
Normal file
@ -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")
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user