mgbam commited on
Commit
cbdd805
·
verified ·
1 Parent(s): 9150298

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +14 -115
app.py CHANGED
@@ -1,84 +1,16 @@
1
- import streamlit as st
2
- import yfinance as yf
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")
9
- st.markdown("""
10
- This app demonstrates an extended MACD-RSI based trading strategy on SPY with the following features:
11
- - **Multiple Simultaneous Positions:** Each buy signal creates a new position.
12
- - **Dynamic Trailing Stop:** Each open position is updated with a trailing stop.
13
- - **Configurable Parameters:** Adjust strategy parameters via the sidebar.
14
- - **Buy Rule:**
15
- Buy a fraction of available cash when:
16
- - The MACD line crosses above its signal line.
17
- - RSI is below 50.
18
- - No buy has been executed in the last few days.
19
- - **Sell Rule:**
20
- For each position:
21
- - **Partial Sell:** Sell a fraction of the position when the price reaches a target multiple of the entry price and RSI is above 50.
22
- - **Trailing Stop Exit:** If the price falls below the position’s dynamic trailing stop, sell the entire position.
23
- """)
24
-
25
- st.sidebar.header("Strategy Parameters")
26
- buy_fraction = st.sidebar.slider("Buy Fraction (of available cash)", 0.05, 0.50, 0.15, 0.05)
27
- sell_fraction = st.sidebar.slider("Partial Sell Fraction", 0.10, 0.90, 0.40, 0.05)
28
- target_multiplier = st.sidebar.slider("Target Multiplier", 1.01, 1.20, 1.08, 0.01)
29
- trailing_stop_pct = st.sidebar.slider("Trailing Stop (%)", 0.01, 0.20, 0.08, 0.01)
30
- min_days_between_buys = st.sidebar.number_input("Minimum Days Between Buys", min_value=1, max_value=10, value=2)
31
-
32
- @st.cache_data
33
- def load_data(ticker, period="1y"):
34
- data = yf.download(ticker, period=period)
35
- data.dropna(inplace=True)
36
- return data
37
-
38
- data_load_state = st.text("Loading SPY data...")
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
61
- data['Sell'] = False
62
-
63
- # Backtesting parameters
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
72
  for i in range(1, len(data)):
73
  today = data.index[i]
74
- price = data['Close'].iloc[i] # Ensure price is a scalar value
75
- rsi_today = data['RSI'].iloc[i] # Ensure RSI is a scalar value
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
 
@@ -93,7 +25,7 @@ for i in range(1, len(data)):
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
@@ -101,12 +33,13 @@ for i in range(1, len(data)):
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
@@ -124,7 +57,8 @@ for i in range(1, len(data)):
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"],
@@ -142,39 +76,4 @@ for i in range(1, len(data)):
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")
155
- ax.set_xlabel("Date")
156
- ax.set_ylabel("Equity ($)")
157
- ax.legend()
158
- st.pyplot(fig)
159
-
160
- st.subheader("SPY Price with Buy/Sell Signals")
161
- fig2, ax2 = plt.subplots(figsize=(10, 4))
162
- ax2.plot(data.index, data['Close'], label="SPY Close Price", color='black')
163
- ax2.scatter(data.index[data['Buy']], data['Close'][data['Buy']], marker="^", color="green", label="Buy Signal", s=100)
164
- ax2.scatter(data.index[data['Sell']], data['Close'][data['Sell']], marker="v", color="red", label="Sell Signal", s=100)
165
- ax2.set_xlabel("Date")
166
- ax2.set_ylabel("Price ($)")
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
- """)
 
1
+ # ... (previous code remains the same)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  # Backtesting simulation loop
4
  for i in range(1, len(data)):
5
  today = data.index[i]
6
+ price = float(data['Close'].iloc[i]) # Cast to float
7
+ rsi_today = float(data['RSI'].iloc[i]) # Cast to float
8
 
9
  # Check for buy signal
10
+ macd_today = float(data['MACD'].iloc[i])
11
+ signal_today = float(data['MACD_signal'].iloc[i])
12
+ macd_yesterday = float(data['MACD'].iloc[i-1])
13
+ signal_yesterday = float(data['MACD_signal'].iloc[i-1])
14
 
15
  buy_condition = (macd_yesterday < signal_yesterday) and (macd_today > signal_today) and (rsi_today < 50)
16
 
 
25
  "entry_price": price,
26
  "allocated": allocation,
27
  "shares": shares_bought,
28
+ "highest": price, # Ensure scalar
29
  "trailing_stop": price * (1 - trailing_stop_pct)
30
  })
31
  data.at[today, 'Buy'] = True
 
33
  # Update positions and check sell conditions
34
  positions_to_remove = []
35
  for idx, pos in enumerate(open_positions):
36
+ current_highest = pos["highest"]
37
+ if price > current_highest:
38
  pos["highest"] = price
39
  pos["trailing_stop"] = pos["highest"] * (1 - trailing_stop_pct)
40
 
41
  # Partial sell condition
42
+ if price >= (pos["entry_price"] * target_multiplier) and rsi_today > 50:
43
  shares_to_sell = pos["shares"] * sell_fraction
44
  cash += shares_to_sell * price
45
  pos["shares"] -= shares_to_sell
 
57
  continue
58
 
59
  # Trailing stop exit
60
+ current_trailing_stop = pos["trailing_stop"]
61
+ if price < current_trailing_stop:
62
  cash += pos["shares"] * price
63
  completed_trades.append({
64
  "entry_date": pos["entry_date"],
 
76
  position_value = sum(pos["shares"] * price for pos in open_positions)
77
  equity_curve.append(cash + position_value)
78
 
79
+ # ... (remaining code remains the same)