Last active 2 months ago

Entry and exit signals with a sample (generic) python script

Revision d81004bad1f8974d69486a735be25ee3ac6d2030

gistfile1.txt Raw
1For 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
20The 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
23import pandas as pd
24import numpy as np
25from datetime import datetime, timedelta
26import warnings
27warnings.filterwarnings('ignore')
28
29class 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
261def 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
297if __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```
345