acedanger revised this gist 2 months ago. Go to revision
No changes
acedanger revised this gist 2 months ago. Go to revision
2 files changed, 23 insertions, 24 deletions
stock-entry-exit-technical-signals.md(file created)
| @@ -0,0 +1,22 @@ | |||
| 1 | + | For entering positions, I'd focus on a combination of these technical indicators: | |
| 2 | + | ||
| 3 | + | **For Entry Signals:** | |
| 4 | + | ||
| 5 | + | - **Moving Average Crossovers** - When a shorter-term MA (like 20-day) crosses above a longer-term MA (like 50-day), it can signal upward momentum | |
| 6 | + | - **RSI (Relative Strength Index)** - Looking for oversold conditions (RSI below 30) that might indicate a bounce opportunity | |
| 7 | + | - **MACD** - When the MACD line crosses above the signal line, especially after being in negative territory | |
| 8 | + | - **Volume confirmation** - Ensuring any breakout or signal is accompanied by higher-than-average volume | |
| 9 | + | - **Support and Resistance levels** - Entering near strong support levels or after a clean breakout above resistance | |
| 10 | + | ||
| 11 | + | **For Exit Signals:** | |
| 12 | + | ||
| 13 | + | - **Trailing stops** based on ATR (Average True Range) to let winners run while protecting against major reversals | |
| 14 | + | - **RSI overbought conditions** (above 70) combined with bearish divergence | |
| 15 | + | - **Moving average violations** - When price closes below a key moving average that previously provided support | |
| 16 | + | - **MACD bearish crossover** - When MACD crosses below the signal line | |
| 17 | + | - **Volume patterns** - Selling into high volume spikes, especially if accompanied by reversal candlestick patterns | |
| 18 | + | - **Predetermined risk/reward ratios** - Taking profits at 2:1 or 3:1 reward-to-risk levels | |
| 19 | + | ||
| 20 | + | The key is using multiple indicators together rather than relying on any single one, and always having a clear exit plan before entering any position. Volume confirmation is crucial for validating most technical signals. | |
| 21 | + | ||
| 22 | + | ||
gistfile1.txt renamed to technical-analysis.py
| @@ -1,25 +1,3 @@ | |||
| 1 | - | For entering positions, I'd focus on a combination of these technical indicators: | |
| 2 | - | ||
| 3 | - | **For Entry Signals:** | |
| 4 | - | ||
| 5 | - | - **Moving Average Crossovers** - When a shorter-term MA (like 20-day) crosses above a longer-term MA (like 50-day), it can signal upward momentum | |
| 6 | - | - **RSI (Relative Strength Index)** - Looking for oversold conditions (RSI below 30) that might indicate a bounce opportunity | |
| 7 | - | - **MACD** - When the MACD line crosses above the signal line, especially after being in negative territory | |
| 8 | - | - **Volume confirmation** - Ensuring any breakout or signal is accompanied by higher-than-average volume | |
| 9 | - | - **Support and Resistance levels** - Entering near strong support levels or after a clean breakout above resistance | |
| 10 | - | ||
| 11 | - | **For Exit Signals:** | |
| 12 | - | ||
| 13 | - | - **Trailing stops** based on ATR (Average True Range) to let winners run while protecting against major reversals | |
| 14 | - | - **RSI overbought conditions** (above 70) combined with bearish divergence | |
| 15 | - | - **Moving average violations** - When price closes below a key moving average that previously provided support | |
| 16 | - | - **MACD bearish crossover** - When MACD crosses below the signal line | |
| 17 | - | - **Volume patterns** - Selling into high volume spikes, especially if accompanied by reversal candlestick patterns | |
| 18 | - | - **Predetermined risk/reward ratios** - Taking profits at 2:1 or 3:1 reward-to-risk levels | |
| 19 | - | ||
| 20 | - | The key is using multiple indicators together rather than relying on any single one, and always having a clear exit plan before entering any position. Volume confirmation is crucial for validating most technical signals. | |
| 21 | - | ||
| 22 | - | ```python | |
| 23 | 1 | import pandas as pd | |
| 24 | 2 | import numpy as np | |
| 25 | 3 | from datetime import datetime, timedelta | |
| @@ -340,5 +318,4 @@ if __name__ == "__main__": | |||
| 340 | 318 | print("2. Adjust indicator parameters based on your trading style") | |
| 341 | 319 | print("3. Modify signal thresholds based on backtesting results") | |
| 342 | 320 | print("4. Always combine with risk management and position sizing") | |
| 343 | - | print("5. Consider market conditions and fundamental analysis") | |
| 344 | - | ``` | |
| 321 | + | print("5. Consider market conditions and fundamental analysis") | |
acedanger revised this gist 2 months ago. Go to revision
1 file changed, 344 insertions
gistfile1.txt(file created)
| @@ -0,0 +1,344 @@ | |||
| 1 | + | For entering positions, I'd focus on a combination of these technical indicators: | |
| 2 | + | ||
| 3 | + | **For Entry Signals:** | |
| 4 | + | ||
| 5 | + | - **Moving Average Crossovers** - When a shorter-term MA (like 20-day) crosses above a longer-term MA (like 50-day), it can signal upward momentum | |
| 6 | + | - **RSI (Relative Strength Index)** - Looking for oversold conditions (RSI below 30) that might indicate a bounce opportunity | |
| 7 | + | - **MACD** - When the MACD line crosses above the signal line, especially after being in negative territory | |
| 8 | + | - **Volume confirmation** - Ensuring any breakout or signal is accompanied by higher-than-average volume | |
| 9 | + | - **Support and Resistance levels** - Entering near strong support levels or after a clean breakout above resistance | |
| 10 | + | ||
| 11 | + | **For Exit Signals:** | |
| 12 | + | ||
| 13 | + | - **Trailing stops** based on ATR (Average True Range) to let winners run while protecting against major reversals | |
| 14 | + | - **RSI overbought conditions** (above 70) combined with bearish divergence | |
| 15 | + | - **Moving average violations** - When price closes below a key moving average that previously provided support | |
| 16 | + | - **MACD bearish crossover** - When MACD crosses below the signal line | |
| 17 | + | - **Volume patterns** - Selling into high volume spikes, especially if accompanied by reversal candlestick patterns | |
| 18 | + | - **Predetermined risk/reward ratios** - Taking profits at 2:1 or 3:1 reward-to-risk levels | |
| 19 | + | ||
| 20 | + | The key is using multiple indicators together rather than relying on any single one, and always having a clear exit plan before entering any position. Volume confirmation is crucial for validating most technical signals. | |
| 21 | + | ||
| 22 | + | ```python | |
| 23 | + | import pandas as pd | |
| 24 | + | import numpy as np | |
| 25 | + | from datetime import datetime, timedelta | |
| 26 | + | import warnings | |
| 27 | + | warnings.filterwarnings('ignore') | |
| 28 | + | ||
| 29 | + | class TechnicalAnalyzer: | |
| 30 | + | def __init__(self, data): | |
| 31 | + | """ | |
| 32 | + | Initialize with price data DataFrame | |
| 33 | + | Expected columns: ['date', 'open', 'high', 'low', 'close', 'volume'] | |
| 34 | + | """ | |
| 35 | + | self.data = data.copy() | |
| 36 | + | self.signals = pd.DataFrame() | |
| 37 | + | ||
| 38 | + | def calculate_sma(self, period): | |
| 39 | + | """Simple Moving Average""" | |
| 40 | + | return self.data['close'].rolling(window=period).mean() | |
| 41 | + | ||
| 42 | + | def calculate_ema(self, period): | |
| 43 | + | """Exponential Moving Average""" | |
| 44 | + | return self.data['close'].ewm(span=period).mean() | |
| 45 | + | ||
| 46 | + | def calculate_rsi(self, period=14): | |
| 47 | + | """Relative Strength Index""" | |
| 48 | + | delta = self.data['close'].diff() | |
| 49 | + | gain = (delta.where(delta > 0, 0)).rolling(window=period).mean() | |
| 50 | + | loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean() | |
| 51 | + | rs = gain / loss | |
| 52 | + | rsi = 100 - (100 / (1 + rs)) | |
| 53 | + | return rsi | |
| 54 | + | ||
| 55 | + | def calculate_macd(self, fast=12, slow=26, signal=9): | |
| 56 | + | """MACD Indicator""" | |
| 57 | + | ema_fast = self.calculate_ema(fast) | |
| 58 | + | ema_slow = self.calculate_ema(slow) | |
| 59 | + | macd_line = ema_fast - ema_slow | |
| 60 | + | signal_line = macd_line.ewm(span=signal).mean() | |
| 61 | + | histogram = macd_line - signal_line | |
| 62 | + | return macd_line, signal_line, histogram | |
| 63 | + | ||
| 64 | + | def calculate_bollinger_bands(self, period=20, std_dev=2): | |
| 65 | + | """Bollinger Bands""" | |
| 66 | + | sma = self.calculate_sma(period) | |
| 67 | + | std = self.data['close'].rolling(window=period).std() | |
| 68 | + | upper_band = sma + (std * std_dev) | |
| 69 | + | lower_band = sma - (std * std_dev) | |
| 70 | + | return upper_band, sma, lower_band | |
| 71 | + | ||
| 72 | + | def calculate_atr(self, period=14): | |
| 73 | + | """Average True Range""" | |
| 74 | + | high_low = self.data['high'] - self.data['low'] | |
| 75 | + | high_close = np.abs(self.data['high'] - self.data['close'].shift()) | |
| 76 | + | low_close = np.abs(self.data['low'] - self.data['close'].shift()) | |
| 77 | + | ranges = pd.concat([high_low, high_close, low_close], axis=1) | |
| 78 | + | true_range = np.max(ranges, axis=1) | |
| 79 | + | atr = true_range.rolling(window=period).mean() | |
| 80 | + | return atr | |
| 81 | + | ||
| 82 | + | def calculate_volume_indicators(self): | |
| 83 | + | """Volume-based indicators""" | |
| 84 | + | # Volume Moving Average | |
| 85 | + | vol_sma_20 = self.data['volume'].rolling(window=20).mean() | |
| 86 | + | vol_ratio = self.data['volume'] / vol_sma_20 | |
| 87 | + | ||
| 88 | + | # On Balance Volume (OBV) | |
| 89 | + | obv = (np.sign(self.data['close'].diff()) * self.data['volume']).fillna(0).cumsum() | |
| 90 | + | ||
| 91 | + | return vol_ratio, obv | |
| 92 | + | ||
| 93 | + | def generate_all_indicators(self): | |
| 94 | + | """Calculate all technical indicators""" | |
| 95 | + | # Moving Averages | |
| 96 | + | self.data['sma_20'] = self.calculate_sma(20) | |
| 97 | + | self.data['sma_50'] = self.calculate_sma(50) | |
| 98 | + | self.data['ema_12'] = self.calculate_ema(12) | |
| 99 | + | self.data['ema_26'] = self.calculate_ema(26) | |
| 100 | + | ||
| 101 | + | # RSI | |
| 102 | + | self.data['rsi'] = self.calculate_rsi() | |
| 103 | + | ||
| 104 | + | # MACD | |
| 105 | + | macd, signal, histogram = self.calculate_macd() | |
| 106 | + | self.data['macd'] = macd | |
| 107 | + | self.data['macd_signal'] = signal | |
| 108 | + | self.data['macd_histogram'] = histogram | |
| 109 | + | ||
| 110 | + | # Bollinger Bands | |
| 111 | + | bb_upper, bb_middle, bb_lower = self.calculate_bollinger_bands() | |
| 112 | + | self.data['bb_upper'] = bb_upper | |
| 113 | + | self.data['bb_middle'] = bb_middle | |
| 114 | + | self.data['bb_lower'] = bb_lower | |
| 115 | + | ||
| 116 | + | # ATR | |
| 117 | + | self.data['atr'] = self.calculate_atr() | |
| 118 | + | ||
| 119 | + | # Volume indicators | |
| 120 | + | vol_ratio, obv = self.calculate_volume_indicators() | |
| 121 | + | self.data['vol_ratio'] = vol_ratio | |
| 122 | + | self.data['obv'] = obv | |
| 123 | + | ||
| 124 | + | return self.data | |
| 125 | + | ||
| 126 | + | def identify_entry_signals(self): | |
| 127 | + | """Identify potential entry points""" | |
| 128 | + | signals = [] | |
| 129 | + | ||
| 130 | + | for i in range(1, len(self.data)): | |
| 131 | + | entry_score = 0 | |
| 132 | + | reasons = [] | |
| 133 | + | ||
| 134 | + | current = self.data.iloc[i] | |
| 135 | + | previous = self.data.iloc[i-1] | |
| 136 | + | ||
| 137 | + | # Moving Average Crossover (Golden Cross) | |
| 138 | + | if (current['sma_20'] > current['sma_50'] and | |
| 139 | + | previous['sma_20'] <= previous['sma_50']): | |
| 140 | + | entry_score += 2 | |
| 141 | + | reasons.append("SMA Golden Cross") | |
| 142 | + | ||
| 143 | + | # Price above both MAs | |
| 144 | + | if current['close'] > current['sma_20'] > current['sma_50']: | |
| 145 | + | entry_score += 1 | |
| 146 | + | reasons.append("Price above MAs") | |
| 147 | + | ||
| 148 | + | # RSI oversold recovery | |
| 149 | + | if previous['rsi'] < 30 and current['rsi'] > 30: | |
| 150 | + | entry_score += 2 | |
| 151 | + | reasons.append("RSI oversold recovery") | |
| 152 | + | ||
| 153 | + | # MACD bullish crossover | |
| 154 | + | if (current['macd'] > current['macd_signal'] and | |
| 155 | + | previous['macd'] <= previous['macd_signal']): | |
| 156 | + | entry_score += 2 | |
| 157 | + | reasons.append("MACD bullish crossover") | |
| 158 | + | ||
| 159 | + | # Bollinger Band bounce | |
| 160 | + | if previous['close'] <= previous['bb_lower'] and current['close'] > previous['bb_lower']: | |
| 161 | + | entry_score += 1 | |
| 162 | + | reasons.append("BB lower band bounce") | |
| 163 | + | ||
| 164 | + | # Volume confirmation | |
| 165 | + | if current['vol_ratio'] > 1.5: # 50% above average | |
| 166 | + | entry_score += 1 | |
| 167 | + | reasons.append("High volume") | |
| 168 | + | ||
| 169 | + | # Strong overall conditions | |
| 170 | + | if (current['rsi'] > 40 and current['rsi'] < 70 and | |
| 171 | + | current['macd'] > 0): | |
| 172 | + | entry_score += 1 | |
| 173 | + | reasons.append("Favorable momentum") | |
| 174 | + | ||
| 175 | + | if entry_score >= 3: # Minimum threshold for entry | |
| 176 | + | signals.append({ | |
| 177 | + | 'date': current['date'], | |
| 178 | + | 'type': 'ENTRY', | |
| 179 | + | 'price': current['close'], | |
| 180 | + | 'score': entry_score, | |
| 181 | + | 'reasons': reasons | |
| 182 | + | }) | |
| 183 | + | ||
| 184 | + | return signals | |
| 185 | + | ||
| 186 | + | def identify_exit_signals(self): | |
| 187 | + | """Identify potential exit points""" | |
| 188 | + | signals = [] | |
| 189 | + | ||
| 190 | + | for i in range(1, len(self.data)): | |
| 191 | + | exit_score = 0 | |
| 192 | + | reasons = [] | |
| 193 | + | ||
| 194 | + | current = self.data.iloc[i] | |
| 195 | + | previous = self.data.iloc[i-1] | |
| 196 | + | ||
| 197 | + | # Moving Average bearish cross | |
| 198 | + | if (current['sma_20'] < current['sma_50'] and | |
| 199 | + | previous['sma_20'] >= previous['sma_50']): | |
| 200 | + | exit_score += 2 | |
| 201 | + | reasons.append("SMA Death Cross") | |
| 202 | + | ||
| 203 | + | # Price below key MA | |
| 204 | + | if current['close'] < current['sma_20']: | |
| 205 | + | exit_score += 1 | |
| 206 | + | reasons.append("Price below SMA20") | |
| 207 | + | ||
| 208 | + | # RSI overbought | |
| 209 | + | if current['rsi'] > 70: | |
| 210 | + | exit_score += 1 | |
| 211 | + | reasons.append("RSI overbought") | |
| 212 | + | ||
| 213 | + | # RSI bearish divergence (simplified) | |
| 214 | + | if previous['rsi'] > 70 and current['rsi'] < 70: | |
| 215 | + | exit_score += 2 | |
| 216 | + | reasons.append("RSI overbought exit") | |
| 217 | + | ||
| 218 | + | # MACD bearish crossover | |
| 219 | + | if (current['macd'] < current['macd_signal'] and | |
| 220 | + | previous['macd'] >= previous['macd_signal']): | |
| 221 | + | exit_score += 2 | |
| 222 | + | reasons.append("MACD bearish crossover") | |
| 223 | + | ||
| 224 | + | # Bollinger Band upper touch | |
| 225 | + | if current['close'] >= current['bb_upper']: | |
| 226 | + | exit_score += 1 | |
| 227 | + | reasons.append("BB upper band resistance") | |
| 228 | + | ||
| 229 | + | # Volume spike (could indicate distribution) | |
| 230 | + | if current['vol_ratio'] > 3.0: | |
| 231 | + | exit_score += 1 | |
| 232 | + | reasons.append("Extreme volume spike") | |
| 233 | + | ||
| 234 | + | if exit_score >= 3: # Minimum threshold for exit | |
| 235 | + | signals.append({ | |
| 236 | + | 'date': current['date'], | |
| 237 | + | 'type': 'EXIT', | |
| 238 | + | 'price': current['close'], | |
| 239 | + | 'score': exit_score, | |
| 240 | + | 'reasons': reasons | |
| 241 | + | }) | |
| 242 | + | ||
| 243 | + | return signals | |
| 244 | + | ||
| 245 | + | def analyze_stock(self): | |
| 246 | + | """Complete analysis workflow""" | |
| 247 | + | # Generate all indicators | |
| 248 | + | self.generate_all_indicators() | |
| 249 | + | ||
| 250 | + | # Get entry and exit signals | |
| 251 | + | entry_signals = self.identify_entry_signals() | |
| 252 | + | exit_signals = self.identify_exit_signals() | |
| 253 | + | ||
| 254 | + | # Combine all signals | |
| 255 | + | all_signals = entry_signals + exit_signals | |
| 256 | + | all_signals = sorted(all_signals, key=lambda x: x['date']) | |
| 257 | + | ||
| 258 | + | return all_signals, self.data | |
| 259 | + | ||
| 260 | + | # Example usage and demo data generation | |
| 261 | + | def generate_sample_data(days=252): | |
| 262 | + | """Generate sample stock data for demonstration""" | |
| 263 | + | np.random.seed(42) # For reproducible results | |
| 264 | + | ||
| 265 | + | start_date = datetime.now() - timedelta(days=days) | |
| 266 | + | dates = [start_date + timedelta(days=i) for i in range(days)] | |
| 267 | + | ||
| 268 | + | # Generate realistic price movement | |
| 269 | + | returns = np.random.normal(0.001, 0.02, days) # Daily returns | |
| 270 | + | price = 100 # Starting price | |
| 271 | + | prices = [price] | |
| 272 | + | ||
| 273 | + | for ret in returns[1:]: | |
| 274 | + | price *= (1 + ret) | |
| 275 | + | prices.append(price) | |
| 276 | + | ||
| 277 | + | # Generate OHLC data | |
| 278 | + | data = [] | |
| 279 | + | for i, (date, close) in enumerate(zip(dates, prices)): | |
| 280 | + | high = close * (1 + abs(np.random.normal(0, 0.015))) | |
| 281 | + | low = close * (1 - abs(np.random.normal(0, 0.015))) | |
| 282 | + | open_price = low + (high - low) * np.random.random() | |
| 283 | + | volume = int(np.random.normal(1000000, 300000)) | |
| 284 | + | ||
| 285 | + | data.append({ | |
| 286 | + | 'date': date, | |
| 287 | + | 'open': open_price, | |
| 288 | + | 'high': high, | |
| 289 | + | 'low': low, | |
| 290 | + | 'close': close, | |
| 291 | + | 'volume': max(volume, 100000) # Ensure positive volume | |
| 292 | + | }) | |
| 293 | + | ||
| 294 | + | return pd.DataFrame(data) | |
| 295 | + | ||
| 296 | + | # Demo execution | |
| 297 | + | if __name__ == "__main__": | |
| 298 | + | # Generate sample data | |
| 299 | + | print("Generating sample stock data...") | |
| 300 | + | sample_data = generate_sample_data(180) # 6 months of data | |
| 301 | + | ||
| 302 | + | # Initialize analyzer | |
| 303 | + | analyzer = TechnicalAnalyzer(sample_data) | |
| 304 | + | ||
| 305 | + | # Run complete analysis | |
| 306 | + | print("Analyzing technical indicators...") | |
| 307 | + | signals, enhanced_data = analyzer.analyze_stock() | |
| 308 | + | ||
| 309 | + | # Display results | |
| 310 | + | print(f"\n=== TECHNICAL ANALYSIS RESULTS ===") | |
| 311 | + | print(f"Analysis period: {len(sample_data)} days") | |
| 312 | + | print(f"Total signals found: {len(signals)}") | |
| 313 | + | ||
| 314 | + | # Show recent indicators | |
| 315 | + | print(f"\n=== LATEST INDICATOR VALUES ===") | |
| 316 | + | latest = enhanced_data.iloc[-1] | |
| 317 | + | print(f"Price: ${latest['close']:.2f}") | |
| 318 | + | print(f"RSI: {latest['rsi']:.2f}") | |
| 319 | + | print(f"MACD: {latest['macd']:.4f}") | |
| 320 | + | print(f"Volume Ratio: {latest['vol_ratio']:.2f}x") | |
| 321 | + | print(f"20-day SMA: ${latest['sma_20']:.2f}") | |
| 322 | + | print(f"50-day SMA: ${latest['sma_50']:.2f}") | |
| 323 | + | ||
| 324 | + | # Show recent signals | |
| 325 | + | print(f"\n=== RECENT SIGNALS ===") | |
| 326 | + | recent_signals = [s for s in signals if s['date'] >= (datetime.now() - timedelta(days=30))] | |
| 327 | + | ||
| 328 | + | if recent_signals: | |
| 329 | + | for signal in recent_signals[-5:]: # Last 5 signals | |
| 330 | + | print(f"\n{signal['type']} Signal:") | |
| 331 | + | print(f" Date: {signal['date'].strftime('%Y-%m-%d')}") | |
| 332 | + | print(f" Price: ${signal['price']:.2f}") | |
| 333 | + | print(f" Score: {signal['score']}") | |
| 334 | + | print(f" Reasons: {', '.join(signal['reasons'])}") | |
| 335 | + | else: | |
| 336 | + | print("No recent signals found.") | |
| 337 | + | ||
| 338 | + | print(f"\n=== USAGE NOTES ===") | |
| 339 | + | print("1. Replace sample data with real market data from your preferred source") | |
| 340 | + | print("2. Adjust indicator parameters based on your trading style") | |
| 341 | + | print("3. Modify signal thresholds based on backtesting results") | |
| 342 | + | print("4. Always combine with risk management and position sizing") | |
| 343 | + | print("5. Consider market conditions and fundamental analysis") | |
| 344 | + | ``` | |