Compare commits
7 Commits
main
...
Working-Co
| Author | SHA1 | Date | |
|---|---|---|---|
| bc7d20e179 | |||
| af392bbaec | |||
| 6af7ce4bcd | |||
| f7d6d5d136 | |||
| 95e1a2bcd0 | |||
| c5fdbaed09 | |||
| 1daf868a4d |
@ -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
|
||||
})
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user