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)
|
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
|
||||||
return tr.rolling(window=self.length).mean()
|
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:
|
def calculate(self, df: pd.DataFrame) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
Calculate SunnyBands indicator
|
Calculate SunnyBands indicator
|
||||||
@ -44,6 +72,9 @@ class SunnyBands:
|
|||||||
upper_band = dma + (atr * self.atr_multiplier)
|
upper_band = dma + (atr * self.atr_multiplier)
|
||||||
lower_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
|
# Generate signals
|
||||||
bullish = (df['close'] > lower_band) & (df['close'].shift(1) <= lower_band)
|
bullish = (df['close'] > lower_band) & (df['close'].shift(1) <= lower_band)
|
||||||
bearish = (df['close'] < upper_band) & (df['close'].shift(1) >= upper_band)
|
bearish = (df['close'] < upper_band) & (df['close'].shift(1) >= upper_band)
|
||||||
@ -53,6 +84,7 @@ class SunnyBands:
|
|||||||
'dma': dma,
|
'dma': dma,
|
||||||
'upper_band': upper_band,
|
'upper_band': upper_band,
|
||||||
'lower_band': lower_band,
|
'lower_band': lower_band,
|
||||||
|
'target': targets, # Add targets to results
|
||||||
'bullish_signal': bullish,
|
'bullish_signal': bullish,
|
||||||
'bearish_signal': bearish
|
'bearish_signal': bearish
|
||||||
})
|
})
|
||||||
|
|||||||
@ -30,10 +30,67 @@ def get_interval_choice() -> str:
|
|||||||
else:
|
else:
|
||||||
print("Invalid choice. Please try again.")
|
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:
|
def get_stock_data(ticker: str, start_date: datetime, end_date: datetime, interval: str) -> pd.DataFrame:
|
||||||
"""Fetch stock data from the database"""
|
"""Fetch stock data from the database"""
|
||||||
client = create_client()
|
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)
|
# Calculate proper date range (looking back from today)
|
||||||
end_date = datetime.now()
|
end_date = datetime.now()
|
||||||
start_date = end_date - timedelta(days=60) # 60 days of history
|
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']
|
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":
|
if interval != "daily" and interval != "5min":
|
||||||
# Resample to desired interval
|
# Resample to desired interval
|
||||||
df.set_index('date', inplace=True)
|
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:
|
for ticker in tickers:
|
||||||
try:
|
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)
|
df = get_stock_data(ticker, start_date, end_date, interval)
|
||||||
if df.empty or len(df) < 50:
|
if df.empty or len(df) < 50:
|
||||||
continue
|
continue
|
||||||
@ -304,41 +376,35 @@ def run_sunny_scanner(min_price: float, max_price: float, min_volume: int, portf
|
|||||||
last_day = df.iloc[-1]
|
last_day = df.iloc[-1]
|
||||||
|
|
||||||
if results['bullish_signal'].iloc[-1]:
|
if results['bullish_signal'].iloc[-1]:
|
||||||
entry_price = last_day['close']
|
entry_price = current_price # Use current price instead of last_day['close']
|
||||||
dma = results['dma'].iloc[-1]
|
target_price = results['target'].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
|
|
||||||
}
|
|
||||||
|
|
||||||
if calculator:
|
if calculator:
|
||||||
position = calculator.calculate_position_size(entry_price, target_price)
|
position = calculator.calculate_position_size(entry_price, target_price)
|
||||||
signal_data.update({
|
if position['shares'] > 0:
|
||||||
'shares': position['shares'],
|
signal_data = {
|
||||||
'position_size': position['position_value'],
|
'ticker': ticker,
|
||||||
'stop_loss': position['stop_loss'],
|
'current': current_price, # Add current price to signal data
|
||||||
'risk': position['potential_loss'],
|
'entry': entry_price,
|
||||||
'reward': position['potential_profit'],
|
'target': target_price,
|
||||||
'r_r': position['risk_reward_ratio']
|
'shares': position['shares'],
|
||||||
})
|
'position_size': position['position_value'],
|
||||||
|
'stop_loss': position['stop_loss'],
|
||||||
bullish_signals.append(signal_data)
|
'risk': position['potential_loss'],
|
||||||
print(f"\n🟢 {ticker} Entry: ${entry_price:.2f} Target: ${target_price:.2f}")
|
'reward': position['potential_profit'],
|
||||||
if calculator:
|
'r_r': position['risk_reward_ratio']
|
||||||
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}")
|
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]:
|
elif results['bearish_signal'].iloc[-1]:
|
||||||
bearish_signals.append({
|
bearish_signals.append({
|
||||||
'ticker': ticker,
|
'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:
|
except Exception as e:
|
||||||
continue
|
continue
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user