Compare commits

...

7 Commits

2 changed files with 125 additions and 27 deletions

View File

@ -23,6 +23,34 @@ class SunnyBands:
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
return tr.rolling(window=self.length).mean()
def calculate_target(self, dma: float, upper_band: float, current_price: float) -> float:
"""
Calculate a realistic price target based on current price and volatility
Args:
dma (float): Current DMA value
upper_band (float): Current upper band value
current_price (float): Current stock price
"""
# Calculate band width as measure of volatility
band_width = upper_band - dma
# Calculate percentage moves based on price levels
if current_price <= 10:
target_percent = 0.03 # 3% move for low-priced stocks
elif current_price <= 20:
target_percent = 0.025 # 2.5% move for mid-priced stocks
else:
target_percent = 0.02 # 2% move for higher-priced stocks
# Calculate target based on current price plus percentage move
base_target = current_price * (1 + target_percent)
# Adjust target if it's below the upper band
target = max(base_target, upper_band + (band_width * 0.5))
return target
def calculate(self, df: pd.DataFrame) -> pd.DataFrame:
"""
Calculate SunnyBands indicator
@ -44,6 +72,9 @@ class SunnyBands:
upper_band = dma + (atr * self.atr_multiplier)
lower_band = dma - (atr * self.atr_multiplier)
# Calculate targets for each row
targets = [self.calculate_target(d, u, c) for d, u, c in zip(dma, upper_band, df['close'])]
# Generate signals
bullish = (df['close'] > lower_band) & (df['close'].shift(1) <= lower_band)
bearish = (df['close'] < upper_band) & (df['close'].shift(1) >= upper_band)
@ -53,6 +84,7 @@ class SunnyBands:
'dma': dma,
'upper_band': upper_band,
'lower_band': lower_band,
'target': targets, # Add targets to results
'bullish_signal': bullish,
'bearish_signal': bearish
})

View File

@ -30,10 +30,67 @@ def get_interval_choice() -> str:
else:
print("Invalid choice. Please try again.")
def get_current_price(ticker: str) -> float:
"""Get the most recent price for a ticker from stock_prices table"""
client = create_client()
query = f"""
SELECT close
FROM stock_db.stock_prices
WHERE ticker = '{ticker}'
ORDER BY window_start DESC
LIMIT 1
"""
try:
result = client.query(query)
if result.result_rows:
return float(result.result_rows[0][0])
return None
except Exception as e:
print(f"Error getting current price for {ticker}: {str(e)}")
return None
def get_stock_data(ticker: str, start_date: datetime, end_date: datetime, interval: str) -> pd.DataFrame:
"""Fetch stock data from the database"""
client = create_client()
# Add data validation
def validate_price_data(df: pd.DataFrame) -> pd.DataFrame:
if df.empty:
return df
# Convert numeric columns to float
for col in ['open', 'high', 'low', 'close']:
df[col] = pd.to_numeric(df[col], errors='coerce')
# Remove rows with NaN values
df = df.dropna()
# Remove rows with suspicious values
df = df[
(df['open'] > 0) &
(df['high'] > 0) &
(df['low'] > 0) &
(df['close'] > 0) &
(df['high'] >= df['low']) &
(df['high'] >= df['open']) &
(df['high'] >= df['close']) &
(df['low'] <= df['open']) &
(df['low'] <= df['close'])
]
# Sort by date and remove duplicates
df = df.sort_values('date', ascending=True).drop_duplicates(subset=['date'])
# Add validation message
if not df.empty:
print(f"Validated price for {ticker}:")
print(f"First price: ${df['close'].iloc[0]:.2f}")
print(f"Last price: ${df['close'].iloc[-1]:.2f}")
return df
# Calculate proper date range (looking back from today)
end_date = datetime.now()
start_date = end_date - timedelta(days=60) # 60 days of history
@ -94,6 +151,17 @@ def get_stock_data(ticker: str, start_date: datetime, end_date: datetime, interv
columns=['date', 'open', 'high', 'low', 'close', 'volume']
)
# Add validation step
df = validate_price_data(df)
if df.empty:
print(f"No valid data found for {ticker} after validation")
return df
# Add debug output for last price
last_price = df.iloc[-1]
print(f"Debug: Last valid price for {ticker}: ${last_price['close']:.2f}")
if interval != "daily" and interval != "5min":
# Resample to desired interval
df.set_index('date', inplace=True)
@ -296,6 +364,10 @@ def run_sunny_scanner(min_price: float, max_price: float, min_volume: int, portf
for ticker in tickers:
try:
current_price = get_current_price(ticker) # Get current price
if current_price is None:
continue
df = get_stock_data(ticker, start_date, end_date, interval)
if df.empty or len(df) < 50:
continue
@ -304,41 +376,35 @@ def run_sunny_scanner(min_price: float, max_price: float, min_volume: int, portf
last_day = df.iloc[-1]
if results['bullish_signal'].iloc[-1]:
entry_price = last_day['close']
dma = results['dma'].iloc[-1]
upper_band = results['upper_band'].iloc[-1]
band_range = upper_band - dma
target_price = upper_band + band_range
signal_data = {
'ticker': ticker,
'entry': entry_price,
'target': target_price
}
entry_price = current_price # Use current price instead of last_day['close']
target_price = results['target'].iloc[-1]
if calculator:
position = calculator.calculate_position_size(entry_price, target_price)
signal_data.update({
'shares': position['shares'],
'position_size': position['position_value'],
'stop_loss': position['stop_loss'],
'risk': position['potential_loss'],
'reward': position['potential_profit'],
'r_r': position['risk_reward_ratio']
})
bullish_signals.append(signal_data)
print(f"\n🟢 {ticker} Entry: ${entry_price:.2f} Target: ${target_price:.2f}")
if calculator:
print(f" Shares: {signal_data['shares']} | Risk: ${abs(signal_data['risk']):.2f} | "
f"Reward: ${signal_data['reward']:.2f} | R/R: {signal_data['r_r']:.2f}")
if position['shares'] > 0:
signal_data = {
'ticker': ticker,
'current': current_price, # Add current price to signal data
'entry': entry_price,
'target': target_price,
'shares': position['shares'],
'position_size': position['position_value'],
'stop_loss': position['stop_loss'],
'risk': position['potential_loss'],
'reward': position['potential_profit'],
'r_r': position['risk_reward_ratio']
}
bullish_signals.append(signal_data)
print(f"\n🟢 {ticker} Current: ${current_price:.2f} Entry: ${entry_price:.2f} Target: ${target_price:.2f}")
print(f" Shares: {signal_data['shares']} | Risk: ${abs(signal_data['risk']):.2f} | "
f"Reward: ${signal_data['reward']:.2f} | R/R: {signal_data['r_r']:.2f}")
elif results['bearish_signal'].iloc[-1]:
bearish_signals.append({
'ticker': ticker,
'price': last_day['close']
'price': current_price # Use current price here too
})
print(f"\n🔴 {ticker} at ${last_day['close']:.2f}")
print(f"\n🔴 {ticker} Current: ${current_price:.2f}")
except Exception as e:
continue