mgbam commited on
Commit
f0c68c9
·
verified ·
1 Parent(s): cefc4af

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +38 -65
app.py CHANGED
@@ -3,8 +3,6 @@ import yfinance as yf
3
  import pandas as pd
4
  import numpy as np
5
  import matplotlib.pyplot as plt
6
- from ta.trend import MACD
7
- from ta.momentum import RSIIndicator
8
  from datetime import timedelta
9
 
10
  st.title("Extended MACD-RSI Combo Strategy for SPY")
@@ -41,23 +39,22 @@ data_load_state = st.text("Loading SPY data...")
41
  data = load_data("SPY", period="1y")
42
  data_load_state.text("Loading SPY data...done!")
43
 
44
- # Calculate technical indicators: MACD and RSI
45
- macd_indicator = MACD(close=data['Close'])
46
- macd_val = macd_indicator.macd()
47
- macd_signal_val = macd_indicator.macd_signal()
48
 
49
- # If the returned MACD is a DataFrame, extract the first column.
50
- if isinstance(macd_val, pd.DataFrame):
51
- macd_val = macd_val.iloc[:, 0]
52
- if isinstance(macd_signal_val, pd.DataFrame):
53
- macd_signal_val = macd_signal_val.iloc[:, 0]
54
 
55
- # Assign the MACD values to the DataFrame.
56
- data['MACD'] = macd_val
57
- data['MACD_signal'] = macd_signal_val
58
-
59
- rsi_indicator = RSIIndicator(close=data['Close'], window=14)
60
- data['RSI'] = rsi_indicator.rsi()
 
 
61
 
62
  # Initialize signal flags for plotting
63
  data['Buy'] = False
@@ -67,14 +64,8 @@ data['Sell'] = False
67
  initial_capital = 100000
68
  cash = initial_capital
69
  equity_curve = []
70
-
71
- # To enforce the buy cooldown, track the last buy date.
72
  last_buy_date = None
73
-
74
- # List to track open positions; each position is a dictionary with details.
75
- open_positions = [] # keys: entry_date, entry_price, shares, allocated, highest, trailing_stop
76
-
77
- # Lists to store completed trades for analysis
78
  completed_trades = []
79
 
80
  # Backtesting simulation loop
@@ -83,59 +74,44 @@ for i in range(1, len(data)):
83
  price = data['Close'].iloc[i]
84
  rsi_today = data['RSI'].iloc[i]
85
 
86
- # --- Check for a buy signal ---
87
- # Signal: MACD crossover (yesterday below signal, today above signal) and RSI below 50.
88
  macd_today = data['MACD'].iloc[i]
89
  signal_today = data['MACD_signal'].iloc[i]
90
- macd_yesterday = data['MACD'].iloc[i - 1]
91
- signal_yesterday = data['MACD_signal'].iloc[i - 1]
92
 
93
  buy_condition = (macd_yesterday < signal_yesterday) and (macd_today > signal_today) and (rsi_today < 50)
94
 
95
- # Enforce cooldown: if a buy occurred recently, skip.
96
- if last_buy_date is not None and (today - last_buy_date).days < min_days_between_buys:
97
- buy_condition = False
98
-
99
- if buy_condition:
100
  allocation = cash * buy_fraction
101
  if allocation > 0:
102
  shares_bought = allocation / price
103
  cash -= allocation
104
  last_buy_date = today
105
- # Initialize the open position with its own trailing stop.
106
- position = {
107
  "entry_date": today,
108
  "entry_price": price,
109
  "allocated": allocation,
110
  "shares": shares_bought,
111
- "highest": price, # track highest price achieved for this position
112
  "trailing_stop": price * (1 - trailing_stop_pct)
113
- }
114
- open_positions.append(position)
115
  data.at[today, 'Buy'] = True
116
- st.write(f"Buy: {today.date()} | Price: {price:.2f} | Shares: {shares_bought:.2f}")
117
 
118
- # --- Update open positions for trailing stops and partial sell targets ---
119
  positions_to_remove = []
120
  for idx, pos in enumerate(open_positions):
121
- # Update the highest price if the current price is higher.
122
  if price > pos["highest"]:
123
  pos["highest"] = price
124
- # Update trailing stop: trailing stop is highest price * (1 - trailing_stop_pct)
125
  pos["trailing_stop"] = pos["highest"] * (1 - trailing_stop_pct)
126
 
127
- # Check for partial sell condition:
128
- target_price = pos["entry_price"] * target_multiplier
129
- if price >= target_price and rsi_today > 50:
130
- # Sell a fraction of this position.
131
  shares_to_sell = pos["shares"] * sell_fraction
132
- sell_value = shares_to_sell * price
133
- cash += sell_value
134
- pos["allocated"] -= shares_to_sell * pos["entry_price"]
135
  pos["shares"] -= shares_to_sell
 
136
  data.at[today, 'Sell'] = True
137
- st.write(f"Partial Sell: {today.date()} | Price: {price:.2f} | Shares Sold: {shares_to_sell:.2f}")
138
- # If the position is nearly closed, mark it for complete removal.
139
  if pos["shares"] < 0.001:
140
  completed_trades.append({
141
  "entry_date": pos["entry_date"],
@@ -145,14 +121,11 @@ for i in range(1, len(data)):
145
  "allocated": pos["allocated"]
146
  })
147
  positions_to_remove.append(idx)
148
- # Continue to next position without checking trailing stop.
149
  continue
150
 
151
- # Check trailing stop: if current price falls below the trailing stop, sell the entire position.
152
  if price < pos["trailing_stop"]:
153
- sell_value = pos["shares"] * price
154
- cash += sell_value
155
- st.write(f"Trailing Stop Hit: {today.date()} | Price: {price:.2f} | Shares Sold: {pos['shares']:.2f}")
156
  completed_trades.append({
157
  "entry_date": pos["entry_date"],
158
  "exit_date": today,
@@ -162,21 +135,20 @@ for i in range(1, len(data)):
162
  })
163
  positions_to_remove.append(idx)
164
 
165
- # Remove positions that have been fully closed (reverse sort indices to remove safely)
166
- for idx in sorted(positions_to_remove, reverse=True):
167
  del open_positions[idx]
168
 
169
- # Calculate the current value of all open positions.
170
- position_value = sum([pos["shares"] * price for pos in open_positions])
171
- total_equity = cash + position_value
172
- equity_curve.append(total_equity)
173
 
174
- # Build performance DataFrame for visualization.
175
  performance = pd.DataFrame({
176
  'Date': data.index[1:len(equity_curve)+1],
177
  'Equity': equity_curve
178
  }).set_index('Date')
179
 
 
180
  st.subheader("Equity Curve")
181
  fig, ax = plt.subplots(figsize=(10, 4))
182
  ax.plot(performance.index, performance['Equity'], label="Total Equity")
@@ -195,13 +167,14 @@ ax2.set_ylabel("Price ($)")
195
  ax2.legend()
196
  st.pyplot(fig2)
197
 
198
- st.subheader("Strategy Performance Metrics")
199
  final_equity = equity_curve[-1]
200
  return_pct = ((final_equity - initial_capital) / initial_capital) * 100
 
201
  st.write(f"**Initial Capital:** ${initial_capital:,.2f}")
202
  st.write(f"**Final Equity:** ${final_equity:,.2f}")
203
  st.write(f"**Return:** {return_pct:.2f}%")
204
 
205
  st.markdown("""
206
  *This extended demo is for educational purposes only and does not constitute financial advice. Always test your strategies extensively before trading with real money.*
207
- """)
 
3
  import pandas as pd
4
  import numpy as np
5
  import matplotlib.pyplot as plt
 
 
6
  from datetime import timedelta
7
 
8
  st.title("Extended MACD-RSI Combo Strategy for SPY")
 
39
  data = load_data("SPY", period="1y")
40
  data_load_state.text("Loading SPY data...done!")
41
 
42
+ # --- Manual Calculation of Technical Indicators ---
 
 
 
43
 
44
+ # Calculate MACD manually
45
+ data['EMA12'] = data['Close'].ewm(span=12, adjust=False).mean()
46
+ data['EMA26'] = data['Close'].ewm(span=26, adjust=False).mean()
47
+ data['MACD'] = data['EMA12'] - data['EMA26']
48
+ data['MACD_signal'] = data['MACD'].ewm(span=9, adjust=False).mean()
49
 
50
+ # Calculate RSI manually
51
+ delta = data['Close'].diff()
52
+ gain = delta.where(delta > 0, 0)
53
+ loss = -delta.where(delta < 0, 0)
54
+ avg_gain = gain.rolling(window=14).mean()
55
+ avg_loss = loss.rolling(window=14).mean()
56
+ rs = avg_gain / avg_loss
57
+ data['RSI'] = 100 - (100 / (1 + rs))
58
 
59
  # Initialize signal flags for plotting
60
  data['Buy'] = False
 
64
  initial_capital = 100000
65
  cash = initial_capital
66
  equity_curve = []
 
 
67
  last_buy_date = None
68
+ open_positions = []
 
 
 
 
69
  completed_trades = []
70
 
71
  # Backtesting simulation loop
 
74
  price = data['Close'].iloc[i]
75
  rsi_today = data['RSI'].iloc[i]
76
 
77
+ # Check for buy signal
 
78
  macd_today = data['MACD'].iloc[i]
79
  signal_today = data['MACD_signal'].iloc[i]
80
+ macd_yesterday = data['MACD'].iloc[i-1]
81
+ signal_yesterday = data['MACD_signal'].iloc[i-1]
82
 
83
  buy_condition = (macd_yesterday < signal_yesterday) and (macd_today > signal_today) and (rsi_today < 50)
84
 
85
+ if buy_condition and (last_buy_date is None or (today - last_buy_date).days >= min_days_between_buys):
 
 
 
 
86
  allocation = cash * buy_fraction
87
  if allocation > 0:
88
  shares_bought = allocation / price
89
  cash -= allocation
90
  last_buy_date = today
91
+ open_positions.append({
 
92
  "entry_date": today,
93
  "entry_price": price,
94
  "allocated": allocation,
95
  "shares": shares_bought,
96
+ "highest": price,
97
  "trailing_stop": price * (1 - trailing_stop_pct)
98
+ })
 
99
  data.at[today, 'Buy'] = True
 
100
 
101
+ # Update positions and check sell conditions
102
  positions_to_remove = []
103
  for idx, pos in enumerate(open_positions):
 
104
  if price > pos["highest"]:
105
  pos["highest"] = price
 
106
  pos["trailing_stop"] = pos["highest"] * (1 - trailing_stop_pct)
107
 
108
+ # Partial sell condition
109
+ if price >= pos["entry_price"] * target_multiplier and rsi_today > 50:
 
 
110
  shares_to_sell = pos["shares"] * sell_fraction
111
+ cash += shares_to_sell * price
 
 
112
  pos["shares"] -= shares_to_sell
113
+ pos["allocated"] -= shares_to_sell * pos["entry_price"]
114
  data.at[today, 'Sell'] = True
 
 
115
  if pos["shares"] < 0.001:
116
  completed_trades.append({
117
  "entry_date": pos["entry_date"],
 
121
  "allocated": pos["allocated"]
122
  })
123
  positions_to_remove.append(idx)
 
124
  continue
125
 
126
+ # Trailing stop exit
127
  if price < pos["trailing_stop"]:
128
+ cash += pos["shares"] * price
 
 
129
  completed_trades.append({
130
  "entry_date": pos["entry_date"],
131
  "exit_date": today,
 
135
  })
136
  positions_to_remove.append(idx)
137
 
138
+ for idx in reversed(positions_to_remove):
 
139
  del open_positions[idx]
140
 
141
+ # Update equity curve
142
+ position_value = sum(pos["shares"] * price for pos in open_positions)
143
+ equity_curve.append(cash + position_value)
 
144
 
145
+ # Build performance DataFrame
146
  performance = pd.DataFrame({
147
  'Date': data.index[1:len(equity_curve)+1],
148
  'Equity': equity_curve
149
  }).set_index('Date')
150
 
151
+ # Plot results
152
  st.subheader("Equity Curve")
153
  fig, ax = plt.subplots(figsize=(10, 4))
154
  ax.plot(performance.index, performance['Equity'], label="Total Equity")
 
167
  ax2.legend()
168
  st.pyplot(fig2)
169
 
170
+ # Display performance metrics
171
  final_equity = equity_curve[-1]
172
  return_pct = ((final_equity - initial_capital) / initial_capital) * 100
173
+ st.subheader("Strategy Performance Metrics")
174
  st.write(f"**Initial Capital:** ${initial_capital:,.2f}")
175
  st.write(f"**Final Equity:** ${final_equity:,.2f}")
176
  st.write(f"**Return:** {return_pct:.2f}%")
177
 
178
  st.markdown("""
179
  *This extended demo is for educational purposes only and does not constitute financial advice. Always test your strategies extensively before trading with real money.*
180
+ """)