feat: Add position sizing to Sunny Scanner with portfolio input

This commit is contained in:
Bobby Abellana (aider) 2025-02-06 22:37:03 -08:00
parent 843f78851f
commit 88b944851a
No known key found for this signature in database
GPG Key ID: 647714CC45F3647B
2 changed files with 41 additions and 6 deletions

View File

@ -20,7 +20,8 @@ def get_scanner_parameters():
min_price = get_float_input("Enter minimum stock price ($): ") min_price = get_float_input("Enter minimum stock price ($): ")
max_price = get_float_input("Enter maximum stock price ($): ") max_price = get_float_input("Enter maximum stock price ($): ")
min_volume = int(input("Enter minimum volume: ")) min_volume = int(input("Enter minimum volume: "))
return min_price, max_price, min_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 main(): def main():
print("\nStock Analysis System") print("\nStock Analysis System")
@ -82,9 +83,9 @@ def main():
print("✅ Scores saved in data/metrics/stock_scores.csv\n") print("✅ Scores saved in data/metrics/stock_scores.csv\n")
elif choice == "2": elif choice == "2":
min_price, max_price, min_volume = get_scanner_parameters() min_price, max_price, min_volume, portfolio_size = get_scanner_parameters()
from screener.t_sunnyband import run_sunny_scanner from screener.t_sunnyband import run_sunny_scanner
run_sunny_scanner(min_price, max_price, min_volume) run_sunny_scanner(min_price, max_price, min_volume, portfolio_size)
elif choice == "3": elif choice == "3":
from trading.main import main as trading_main from trading.main import main as trading_main

View File

@ -2,6 +2,7 @@ from datetime import datetime, timedelta
import pandas as pd import pandas as pd
from db.db_connection import create_client from db.db_connection import create_client
from indicators.sunny_bands import SunnyBands from indicators.sunny_bands import SunnyBands
from trading.position_calculator import PositionCalculator
def get_interval_choice() -> str: def get_interval_choice() -> str:
"""Get user's preferred time interval""" """Get user's preferred time interval"""
@ -268,7 +269,7 @@ def view_stock_details(ticker: str, interval: str, start_date: datetime, end_dat
except Exception as e: except Exception as e:
print(f"Error analyzing {ticker}: {str(e)}") print(f"Error analyzing {ticker}: {str(e)}")
def run_sunny_scanner(min_price: float, max_price: float, min_volume: int) -> None: def run_sunny_scanner(min_price: float, max_price: float, min_volume: int, portfolio_size: float = None) -> None:
"""Run the SunnyBand scanner and save results""" """Run the SunnyBand scanner and save results"""
print(f"\nInitializing scan for stocks between ${min_price:.2f} and ${max_price:.2f}") print(f"\nInitializing scan for stocks between ${min_price:.2f} and ${max_price:.2f}")
print(f"Minimum volume: {min_volume:,}") print(f"Minimum volume: {min_volume:,}")
@ -301,8 +302,11 @@ def run_sunny_scanner(min_price: float, max_price: float, min_volume: int) -> No
bearish_signals = [] bearish_signals = []
errors = [] errors = []
# Initialize SunnyBands indicator # Initialize SunnyBands indicator and position calculator
sunny = SunnyBands() sunny = SunnyBands()
calculator = None
if portfolio_size and portfolio_size > 0:
calculator = PositionCalculator(account_size=portfolio_size)
# Track progress # Track progress
total = len(tickers) total = len(tickers)
@ -337,8 +341,28 @@ def run_sunny_scanner(min_price: float, max_price: float, min_volume: int) -> No
'volume': last_day['volume'], 'volume': last_day['volume'],
'date': last_day['date'], 'date': last_day['date'],
'dma': results['dma'].iloc[-1], 'dma': results['dma'].iloc[-1],
'lower_band': results['lower_band'].iloc[-1] 'lower_band': results['lower_band'].iloc[-1],
'upper_band': results['upper_band'].iloc[-1]
} }
# Add position sizing if calculator exists
if calculator:
try:
position = calculator.calculate_position_size(
entry_price=last_day['close'],
target_price=results['upper_band'].iloc[-1]
)
signal_data.update({
'shares': position['shares'],
'position_value': position['position_value'],
'stop_loss': position['stop_loss'],
'potential_profit': position['potential_profit'],
'potential_loss': position['potential_loss'],
'risk_reward_ratio': position['risk_reward_ratio']
})
except ValueError as e:
print(f"Position sizing error for {ticker}: {str(e)}")
bullish_signals.append(signal_data) bullish_signals.append(signal_data)
print(f"🟢 Bullish Signal: {ticker} at ${last_day['close']:.2f}") print(f"🟢 Bullish Signal: {ticker} at ${last_day['close']:.2f}")
@ -383,6 +407,16 @@ def run_sunny_scanner(min_price: float, max_price: float, min_volume: int) -> No
print(f"Volume: {signal['volume']:,}") print(f"Volume: {signal['volume']:,}")
print(f"DMA: ${signal['dma']:.2f}") print(f"DMA: ${signal['dma']:.2f}")
print(f"Lower Band: ${signal['lower_band']:.2f}") print(f"Lower Band: ${signal['lower_band']:.2f}")
print(f"Upper Band: ${signal['upper_band']:.2f}")
if 'shares' in signal:
print("\nPosition Details:")
print(f"Shares: {signal['shares']}")
print(f"Position Value: ${signal['position_value']:.2f}")
print(f"Stop Loss: ${signal['stop_loss']:.2f}")
print(f"Potential Profit: ${signal['potential_profit']:.2f}")
print(f"Potential Loss: ${signal['potential_loss']:.2f}")
print(f"Risk/Reward Ratio: {signal['risk_reward_ratio']:.2f}")
if bearish_signals: if bearish_signals:
print(f"\n🔴 Found {len(bearish_signals)} Bearish Signals:") print(f"\n🔴 Found {len(bearish_signals)} Bearish Signals:")