184 lines
6.4 KiB
Python
184 lines
6.4 KiB
Python
import pandas as pd
|
|
from datetime import datetime, timedelta
|
|
import talib
|
|
from db.db_connection import create_client
|
|
from utils.data_utils import (
|
|
get_stock_data, validate_signal_date, print_signal,
|
|
save_signals_to_csv, get_qualified_stocks
|
|
)
|
|
from utils.scanner_utils import initialize_scanner, process_signal_data
|
|
from trading.position_calculator import PositionCalculator
|
|
|
|
# Dictionary mapping pattern names to their functions and descriptions
|
|
CANDLESTICK_PATTERNS = {
|
|
'BULLISH_ENGULFING': {
|
|
'function': talib.CDLENGULFING,
|
|
'description': 'Bullish Engulfing Pattern'
|
|
},
|
|
'HAMMER': {
|
|
'function': talib.CDLHAMMER,
|
|
'description': 'Hammer Pattern'
|
|
},
|
|
'MORNING_STAR': {
|
|
'function': talib.CDLMORNINGSTAR,
|
|
'description': 'Morning Star Pattern'
|
|
},
|
|
'PIERCING_LINE': {
|
|
'function': talib.CDLPIERCING,
|
|
'description': 'Piercing Line Pattern'
|
|
},
|
|
'THREE_WHITE_SOLDIERS': {
|
|
'function': talib.CDL3WHITESOLDIERS,
|
|
'description': 'Three White Soldiers Pattern'
|
|
},
|
|
'MORNING_DOJI_STAR': {
|
|
'function': talib.CDLMORNINGDOJISTAR,
|
|
'description': 'Morning Doji Star Pattern'
|
|
},
|
|
'DRAGONFLY_DOJI': {
|
|
'function': talib.CDLDRAGONFLYDOJI,
|
|
'description': 'Dragonfly Doji Pattern'
|
|
},
|
|
'HARAMI': {
|
|
'function': talib.CDLHARAMI,
|
|
'description': 'Bullish Harami Pattern'
|
|
},
|
|
'INVERTED_HAMMER': {
|
|
'function': talib.CDLINVERTEDHAMMER,
|
|
'description': 'Inverted Hammer Pattern'
|
|
},
|
|
'THREE_INSIDE_UP': {
|
|
'function': talib.CDL3INSIDE,
|
|
'description': 'Three Inside Up Pattern'
|
|
},
|
|
'THREE_OUTSIDE_UP': {
|
|
'function': talib.CDL3OUTSIDE,
|
|
'description': 'Three Outside Up Pattern'
|
|
},
|
|
'BELT_HOLD': {
|
|
'function': talib.CDLBELTHOLD,
|
|
'description': 'Bullish Belt Hold Pattern'
|
|
},
|
|
'LADDER_BOTTOM': {
|
|
'function': talib.CDLLADDERBOTTOM,
|
|
'description': 'Ladder Bottom Pattern'
|
|
},
|
|
'MATCHING_LOW': {
|
|
'function': talib.CDLMATCHINGLOW,
|
|
'description': 'Matching Low Pattern'
|
|
},
|
|
'UNIQUE_THREE_RIVER': {
|
|
'function': talib.CDLUNIQUE3RIVER,
|
|
'description': 'Unique Three River Bottom Pattern'
|
|
}
|
|
}
|
|
|
|
def check_entry_signal(df: pd.DataFrame, selected_patterns: list = None) -> list:
|
|
"""
|
|
Check for bullish candlestick patterns across the entire date range
|
|
|
|
Args:
|
|
df (pd.DataFrame): DataFrame with OHLCV data
|
|
selected_patterns (list): List of patterns to scan for
|
|
|
|
Returns:
|
|
list: List of tuples (signal, date, signal_data) for each signal found
|
|
"""
|
|
if len(df) < 14: # Need minimum bars for pattern recognition
|
|
return []
|
|
|
|
signals = []
|
|
|
|
# Use selected patterns or all patterns if none selected
|
|
patterns_to_scan = {k: v for k, v in CANDLESTICK_PATTERNS.items()
|
|
if selected_patterns is None or k in selected_patterns}
|
|
|
|
# Calculate patterns
|
|
pattern_signals = {}
|
|
for pattern_name, pattern_info in patterns_to_scan.items():
|
|
pattern_signals[pattern_name] = pattern_info['function'](
|
|
df['open'].values,
|
|
df['high'].values,
|
|
df['low'].values,
|
|
df['close'].values
|
|
)
|
|
|
|
# Look for signals across all candles
|
|
for i in range(14, len(df)): # Start after lookback period
|
|
found_patterns = []
|
|
|
|
for pattern_name, pattern_values in pattern_signals.items():
|
|
# Check if we have a bullish signal (value > 0)
|
|
if pattern_values[i] > 0:
|
|
found_patterns.append(CANDLESTICK_PATTERNS[pattern_name]['description'])
|
|
|
|
if found_patterns:
|
|
signal_data = {
|
|
'price': df.iloc[i]['close'],
|
|
'patterns': ', '.join(found_patterns),
|
|
'pattern_count': len(found_patterns)
|
|
}
|
|
signals.append((True, df.iloc[i]['date'], signal_data))
|
|
|
|
return signals
|
|
|
|
def run_candlestick_scanner(min_price: float, max_price: float, min_volume: int,
|
|
portfolio_size: float = None, interval: str = "1d",
|
|
start_date: datetime = None, end_date: datetime = None,
|
|
selected_patterns: list = None) -> None:
|
|
"""
|
|
Run candlestick pattern scanner to find bullish patterns
|
|
"""
|
|
try:
|
|
# Initialize scanner components
|
|
interval, start_date, end_date, qualified_stocks, calculator = initialize_scanner(
|
|
min_price=min_price,
|
|
max_price=max_price,
|
|
min_volume=min_volume,
|
|
portfolio_size=portfolio_size,
|
|
interval=interval,
|
|
start_date=start_date,
|
|
end_date=end_date
|
|
)
|
|
|
|
if not qualified_stocks:
|
|
return
|
|
|
|
bullish_signals = []
|
|
|
|
for ticker, current_price, current_volume, last_update, stock_type in qualified_stocks:
|
|
try:
|
|
df = get_stock_data(ticker, start_date, end_date, interval)
|
|
|
|
if df.empty or len(df) < 14: # Need minimum bars
|
|
continue
|
|
|
|
signals = check_entry_signal(df, selected_patterns)
|
|
for signal, signal_date, signal_data in signals:
|
|
entry_data = {
|
|
'ticker': ticker,
|
|
'signal_date': signal_date,
|
|
'entry_price': signal_data['price'],
|
|
'patterns': signal_data['patterns'],
|
|
'pattern_count': signal_data['pattern_count'],
|
|
'volume': current_volume,
|
|
'last_update': last_update,
|
|
'stock_type': stock_type
|
|
}
|
|
bullish_signals.append(entry_data)
|
|
|
|
# Custom print for candlestick signals
|
|
print(f"🕯️ {ticker}: {entry_data['patterns']} on {signal_date.strftime('%Y-%m-%d')} "
|
|
f"at ${signal_data['price']:.2f}")
|
|
|
|
except Exception as e:
|
|
print(f"Error processing {ticker}: {str(e)}")
|
|
continue
|
|
|
|
save_signals_to_csv(bullish_signals, 'candlestick')
|
|
return bullish_signals
|
|
|
|
except Exception as e:
|
|
print(f"Error during scan: {str(e)}")
|
|
return []
|