Download app.py from mgbam/Market: direct link, hf CLI and curl.
- Browser
- Download file 7.26 kB
-
https://huggingface.co/spaces/mgbam/Market/resolve/main/app.py
- Command line
-
hf download hf://spaces/mgbam/Market/app.py
-
curl -L -o app.py https://huggingface.co/spaces/mgbam/Market/resolve/main/app.py
7.26 kB
| import streamlit as st | |
| import yfinance as yf | |
| import pandas as pd | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| from datetime import timedelta | |
| # Streamlit App Title and Description | |
| st.title("Extended MACD-RSI Combo Strategy for SPY") | |
| st.markdown(""" | |
| This app demonstrates an extended MACD-RSI based trading strategy on SPY with the following features: | |
| - **Multiple Simultaneous Positions:** Each buy signal creates a new position. | |
| - **Dynamic Trailing Stop:** Each open position is updated with a trailing stop. | |
| - **Configurable Parameters:** Adjust strategy parameters via the sidebar. | |
| - **Buy Rule:** | |
| Buy a fraction of available cash when: | |
| - The MACD line crosses above its signal line. | |
| - RSI is below 50. | |
| - No buy has been executed in the last few days. | |
| - **Sell Rule:** | |
| For each position: | |
| - **Partial Sell:** Sell a fraction of the position when the price reaches a target multiple of the entry price and RSI is above 50. | |
| - **Trailing Stop Exit:** If the price falls below the position’s dynamic trailing stop, sell the entire position. | |
| """) | |
| # Sidebar for Strategy Parameters | |
| st.sidebar.header("Strategy Parameters") | |
| initial_capital = st.sidebar.number_input("Initial Capital ($)", min_value=1000, max_value=1000000, value=100000, step=1000) | |
| buy_fraction = st.sidebar.slider("Buy Fraction (of available cash)", 0.05, 0.50, 0.15, 0.05) | |
| sell_fraction = st.sidebar.slider("Partial Sell Fraction", 0.10, 0.90, 0.40, 0.05) | |
| target_multiplier = st.sidebar.slider("Target Multiplier", 1.01, 1.20, 1.08, 0.01) | |
| trailing_stop_pct = st.sidebar.slider("Trailing Stop (%)", 0.01, 0.20, 0.08, 0.01) | |
| min_days_between_buys = st.sidebar.number_input("Minimum Days Between Buys", min_value=1, max_value=10, value=2) | |
| # Load SPY Data | |
| def load_data(ticker, period="1y"): | |
| data = yf.download(ticker, period=period) | |
| data.dropna(inplace=True) | |
| return data | |
| data_load_state = st.text("Loading SPY data...") | |
| data = load_data("SPY", period="1y") | |
| data_load_state.text("Loading SPY data...done!") | |
| # --- Manual Calculation of Technical Indicators --- | |
| # Calculate MACD | |
| data['EMA12'] = data['Close'].ewm(span=12, adjust=False).mean() | |
| data['EMA26'] = data['Close'].ewm(span=26, adjust=False).mean() | |
| data['MACD'] = data['EMA12'] - data['EMA26'] | |
| data['MACD_signal'] = data['MACD'].ewm(span=9, adjust=False).mean() | |
| # Calculate RSI | |
| delta = data['Close'].diff() | |
| gain = delta.where(delta > 0, 0) | |
| loss = -delta.where(delta < 0, 0) | |
| avg_gain = gain.rolling(window=14).mean() | |
| avg_loss = loss.rolling(window=14).mean() | |
| rs = avg_gain / avg_loss | |
| data['RSI'] = 100 - (100 / (1 + rs)) | |
| # Initialize signal flags for plotting | |
| data['Buy'] = False | |
| data['Sell'] = False | |
| # Backtesting parameters | |
| cash = initial_capital | |
| equity_curve = [] | |
| last_buy_date = None | |
| open_positions = [] | |
| completed_trades = [] | |
| # Backtesting simulation loop | |
| for i in range(1, len(data)): | |
| today = data.index[i] | |
| price = float(data['Close'].iloc[i]) # Ensure price is a scalar value | |
| rsi_today = float(data['RSI'].iloc[i]) # Ensure RSI is a scalar value | |
| # Check for buy signal | |
| macd_today = float(data['MACD'].iloc[i]) | |
| signal_today = float(data['MACD_signal'].iloc[i]) | |
| macd_yesterday = float(data['MACD'].iloc[i-1]) | |
| signal_yesterday = float(data['MACD_signal'].iloc[i-1]) | |
| buy_condition = (macd_yesterday < signal_yesterday) and (macd_today > signal_today) and (rsi_today < 50) | |
| if buy_condition and (last_buy_date is None or (today - last_buy_date).days >= min_days_between_buys): | |
| allocation = cash * buy_fraction | |
| if allocation > 0: | |
| shares_bought = allocation / price | |
| cash -= allocation | |
| last_buy_date = today | |
| open_positions.append({ | |
| "entry_date": today, | |
| "entry_price": price, | |
| "allocated": allocation, | |
| "shares": shares_bought, | |
| "highest": price, | |
| "trailing_stop": price * (1 - trailing_stop_pct) | |
| }) | |
| data.at[today, 'Buy'] = True | |
| # Update positions and check sell conditions | |
| positions_to_remove = [] | |
| for idx, pos in enumerate(open_positions): | |
| current_highest = pos["highest"] | |
| if price > current_highest: | |
| pos["highest"] = price | |
| pos["trailing_stop"] = pos["highest"] * (1 - trailing_stop_pct) | |
| # Partial sell condition | |
| if price >= (pos["entry_price"] * target_multiplier) and rsi_today > 50: | |
| shares_to_sell = pos["shares"] * sell_fraction | |
| cash += shares_to_sell * price | |
| pos["shares"] -= shares_to_sell | |
| pos["allocated"] -= shares_to_sell * pos["entry_price"] | |
| data.at[today, 'Sell'] = True | |
| if pos["shares"] < 0.001: | |
| completed_trades.append({ | |
| "entry_date": pos["entry_date"], | |
| "exit_date": today, | |
| "entry_price": pos["entry_price"], | |
| "exit_price": price, | |
| "allocated": pos["allocated"] | |
| }) | |
| positions_to_remove.append(idx) | |
| continue | |
| # Trailing stop exit | |
| current_trailing_stop = pos["trailing_stop"] | |
| if price < current_trailing_stop: | |
| cash += pos["shares"] * price | |
| completed_trades.append({ | |
| "entry_date": pos["entry_date"], | |
| "exit_date": today, | |
| "entry_price": pos["entry_price"], | |
| "exit_price": price, | |
| "allocated": pos["allocated"] | |
| }) | |
| positions_to_remove.append(idx) | |
| for idx in reversed(positions_to_remove): | |
| del open_positions[idx] | |
| # Update equity curve | |
| position_value = sum(pos["shares"] * price for pos in open_positions) | |
| equity_curve.append(cash + position_value) | |
| # Build performance DataFrame | |
| performance = pd.DataFrame({ | |
| 'Date': data.index[1:len(equity_curve)+1], | |
| 'Equity': equity_curve | |
| }).set_index('Date') | |
| # Plot results | |
| st.subheader("Equity Curve") | |
| fig, ax = plt.subplots(figsize=(10, 4)) | |
| ax.plot(performance.index, performance['Equity'], label="Total Equity") | |
| ax.set_xlabel("Date") | |
| ax.set_ylabel("Equity ($)") | |
| ax.legend() | |
| st.pyplot(fig) | |
| st.subheader("SPY Price with Buy/Sell Signals") | |
| fig2, ax2 = plt.subplots(figsize=(10, 4)) | |
| ax2.plot(data.index, data['Close'], label="SPY Close Price", color='black') | |
| ax2.scatter(data.index[data['Buy']], data['Close'][data['Buy']], marker="^", color="green", label="Buy Signal", s=100) | |
| ax2.scatter(data.index[data['Sell']], data['Close'][data['Sell']], marker="v", color="red", label="Sell Signal", s=100) | |
| ax2.set_xlabel("Date") | |
| ax2.set_ylabel("Price ($)") | |
| ax2.legend() | |
| st.pyplot(fig2) | |
| # Display performance metrics | |
| final_equity = equity_curve[-1] | |
| return_pct = ((final_equity - initial_capital) / initial_capital) * 100 | |
| st.subheader("Strategy Performance Metrics") | |
| st.write(f"**Initial Capital:** ${initial_capital:,.2f}") | |
| st.write(f"**Final Equity:** ${final_equity:,.2f}") | |
| st.write(f"**Return:** {return_pct:.2f}%") | |
| st.markdown(""" | |
| *This extended demo is for educational purposes only and does not constitute financial advice. Always test your strategies extensively before trading with real money.* | |
| """) |