Spaces:
Sleeping
Sleeping
File size: 11,931 Bytes
6c95f9b 5c82fb3 6c95f9b 1cf74f5 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b ce04c4b 6c95f9b 7692b47 6c95f9b 7692b47 6c95f9b a0232d0 7692b47 6c95f9b 7692b47 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | import streamlit as st
import yfinance as yf
import pandas as pd
import numpy as np
import plotly.graph_objs as go
from datetime import datetime, timedelta
from plotly.subplots import make_subplots
# Set Streamlit page configuration
st.set_page_config(page_title="Pivot Point-RSI Volatility Trading Strategy", layout="wide")
# App title
st.title("Pivot Point-RSI Volatility Trading Strategy")
# Description of the app
st.markdown('''
This tool executes a trading strategy that combines Pivot Points, RSI, and ATR to identify buy and sell signals.
It works by calculating support and resistance levels using Pivot Points, determining market momentum through RSI, and adjusting
stop-loss and target levels based on market volatility using ATR.
The strategy finds the best possible parameters through optimization. You can also manually input your own settings.
''')
# Sidebar: How to use the app
with st.sidebar.expander("How to Use", expanded=False):
st.write('''
1. Select the asset and date range for backtesting.
2. Click "Run Strategy" to generate results.
3. View the equity curve, buy/sell signals, and performance metrics.
4. Adjust the parameters post-run to fine-tune results.
''')
# Sidebar: Select Ticker and Date Range
with st.sidebar.expander("Asset Settings", expanded=True):
ticker = st.text_input("Asset Symbol", value="ASML.AS", help="Ticker Symbol or Cryptocurrency Pair (e.g., AAPL, BTC-USD)")
start_date = st.date_input("Start Date", value=datetime(2020, 1, 1), help="Select the start date for historical data.")
end_date = st.date_input("End Date", value=datetime.today() + timedelta(days=1), help="Select the end date for historical data.")
# Download data
@st.cache_data
def download_data(ticker, start, end):
data = yf.download(ticker, start=start, end=end, auto_adjust=False)
if isinstance(data.columns, pd.MultiIndex):
data.columns = data.columns.get_level_values(0)
if data.empty:
raise ValueError(f"No data retrieved for {ticker}")
if len(data) < 24: # Minimum needed for 24-period Pivot Point calculation
raise ValueError(f"Insufficient data points for {ticker}. Need at least 24 days.")
return data
# Function to execute and cache strategy results
@st.cache_data
def execute_strategy(data, strategy_params):
data_with_signals = trading_strategy(data.copy(), **strategy_params)
profit, equity_curve, accuracy_rate = backtest_strategy(data_with_signals)
return data_with_signals, profit, equity_curve, accuracy_rate, strategy_params
# Trading strategy function
def trading_strategy(data, rsi_period, rsi_oversold, rsi_overbought, atr_period, stop_loss_multiplier, target_multiplier, tolerance):
data['high24'] = data['High'].rolling(window=24).max()
data['low24'] = data['Low'].rolling(window=24).min()
data['close24'] = data['Close'].rolling(window=24).mean()
data['pivot'] = (data['high24'] + data['low24'] + data['close24']) / 3
data['support'] = (2 * data['pivot'].rolling(window=12).min()) - data['high24']
data['resistance'] = (2 * data['pivot'].rolling(window=12).max()) - data['low24']
delta = data['Close'].diff()
up, down = delta.copy(), delta.copy()
up[up < 0] = 0
down[down > 0] = 0
roll_up1 = up.ewm(span=rsi_period).mean()
roll_down1 = down.abs().ewm(span=rsi_period).mean()
RS1 = roll_up1 / roll_down1
data['rsi'] = 100.0 - (100.0 / (1.0 + RS1))
data['HL'] = data['High'] - data['Low']
data['absHC'] = abs(data['High'] - data['Close'].shift())
data['absLC'] = abs(data['Low'] - data['Close'].shift())
data['TR'] = data[['HL', 'absHC', 'absLC']].max(axis=1)
data['atr'] = data['TR'].rolling(window=atr_period).mean()
data['signal'] = np.where(
(data['Close'] >= data['support'] * (1 - tolerance)) & (data['Close'] <= data['support'] * (1 + tolerance)) & (data['rsi'] < rsi_oversold), 'Buy',
np.where((data['Close'] >= data['resistance'] * (1 - tolerance)) & (data['Close'] <= data['resistance'] * (1 + tolerance)) & (data['rsi'] > rsi_overbought), 'Sell', 'Hold')
)
data['stop_loss'] = data['Close'] - stop_loss_multiplier * data['atr']
data['target'] = data['Close'] + target_multiplier * data['atr']
return data
# Backtest function
def backtest_strategy(data):
initial_capital = 100000
position = 0
capital = initial_capital
equity_curve = []
correct_signals = 0
total_signals = 0
for index, row in data.iterrows():
if row['signal'] == 'Buy' and position == 0:
position = 1
entry_price = row['Close']
stop_loss = row['stop_loss']
target = row['target']
elif row['signal'] == 'Sell' and position == 0:
position = -1
entry_price = row['Close']
stop_loss = row['stop_loss']
target = row['target']
if position == 1:
if row['Low'] <= stop_loss:
capital += (stop_loss - entry_price) * 100
position = 0
elif row['High'] >= target:
capital += (target - entry_price) * 100
position = 0
elif row['signal'] == 'Sell':
capital += (row['Close'] - entry_price) * 100
position = 0
elif position == -1:
if row['High'] >= stop_loss:
capital += (entry_price - stop_loss) * 100
position = 0
elif row['Low'] <= target:
capital += (entry_price - target) * 100
position = 0
elif row['signal'] == 'Buy':
capital += (entry_price - row['Close']) * 100
position = 0
equity_curve.append(capital)
final_profit = equity_curve[-1] - initial_capital
return final_profit, equity_curve, 0
# Optimized parameters (fixed)
optimized_params = {
'rsi_period': 13,
'rsi_oversold': 30,
'rsi_overbought': 70,
'atr_period': 14,
'stop_loss_multiplier': 2.25,
'target_multiplier': 4.5,
'tolerance': 0.01
}
# Run button to initiate the strategy
run_button = st.sidebar.button("Run Strategy")
# Running the strategy
if run_button:
try:
# Download data
data = download_data(ticker, start_date, end_date)
# Execute the strategy with optimized parameters
data_with_signals, profit, equity_curve, _, best_params = execute_strategy(data, optimized_params)
# Cache results in session state
st.session_state['data_with_signals'] = data_with_signals
st.session_state['equity_curve'] = equity_curve
st.session_state['best_params'] = best_params
# Display optimized parameters
st.json(best_params)
except Exception as e:
st.error(f"An error occurred while running the analysis: {e}")
# If session state contains data, allow for post-run parameter adjustments
if 'data_with_signals' in st.session_state:
st.sidebar.markdown("### Adjust Parameters Post-Run")
adjusted_rsi_period = st.sidebar.slider("RSI Period", 5, 30, st.session_state['best_params']['rsi_period'], step=1, help="RSI period defines the sensitivity of the RSI. Lower values make the RSI more sensitive to price changes, generating more signals. Higher values smooth out price movements, reducing the number of signals.")
adjusted_stop_loss_multiplier = st.sidebar.slider("Stop Loss Multiplier", 1.0, 3.0, st.session_state['best_params']['stop_loss_multiplier'], step=0.1, help="Stop Loss Multiplier adjusts how far the stop-loss is set from the entry price. Lower values keep the stop-loss closer, reducing risk but increasing the chance of being stopped out. Higher values place the stop-loss further away, allowing more flexibility but with higher potential risk.")
adjusted_target_multiplier = st.sidebar.slider("Target Multiplier", 1.0, 5.0, st.session_state['best_params']['target_multiplier'], step=0.1, help="Target Multiplier adjusts how far the profit target is set from the entry price. Lower values take profits earlier but reduce potential gains. Higher values aim for larger profits but increase the chance of price reversal before hitting the target.")
adjusted_tolerance = st.sidebar.slider("Signal Tolerance", 0.0, 0.05, st.session_state['best_params']['tolerance'], step=0.01, help="Signal Tolerance adjusts how strictly the strategy follows support and resistance levels. Lower values make signals more precise but reduce their frequency. Higher values increase the frequency of signals by being more lenient, which can lead to false signals.")
# Recalculate the strategy with adjusted parameters
updated_params = {
'rsi_period': adjusted_rsi_period,
'rsi_oversold': 30,
'rsi_overbought': 70,
'atr_period': 14,
'stop_loss_multiplier': adjusted_stop_loss_multiplier,
'target_multiplier': adjusted_target_multiplier,
'tolerance': adjusted_tolerance
}
updated_data = trading_strategy(st.session_state['data_with_signals'].copy(), **updated_params)
# Plotting with adjustments for easier comparison of x-axis
fig = make_subplots(rows=3, cols=1, shared_xaxes=True,
subplot_titles=("Price and Bollinger Bands", "RSI", "Equity Curve"),
vertical_spacing=0.20)
# Price and signal plot
fig.add_trace(go.Scatter(x=updated_data.index, y=updated_data['Close'], mode='lines', name='Close Price'))
fig.add_trace(go.Scatter(x=updated_data.index, y=updated_data['support'], mode='lines', name='Support Level', line=dict(dash='dash')))
fig.add_trace(go.Scatter(x=updated_data.index, y=updated_data['resistance'], mode='lines', name='Resistance Level', line=dict(dash='dash')))
# Buy/Sell Signals with increased marker size
buy_signals = updated_data[updated_data['signal'] == 'Buy']
sell_signals = updated_data[updated_data['signal'] == 'Sell']
fig.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['Close'], mode='markers', name='Buy Signal',
marker=dict(color='green', symbol='triangle-up', size=12)))
fig.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['Close'], mode='markers', name='Sell Signal',
marker=dict(color='red', symbol='triangle-down', size=12)))
# RSI Plot with black line, red upper threshold, and green lower threshold
fig.add_trace(go.Scatter(x=updated_data.index, y=updated_data['rsi'], mode='lines', name='RSI', line=dict(color='white')), row=2, col=1)
fig.add_shape(type="line", x0=updated_data.index[0], x1=updated_data.index[-1], y0=30, y1=30,
line=dict(dash='dash', color='green'), row=2, col=1)
fig.add_shape(type="line", x0=updated_data.index[0], x1=updated_data.index[-1], y0=70, y1=70,
line=dict(dash='dash', color='red'), row=2, col=1)
# Equity Curve Plot
fig.add_trace(go.Scatter(x=updated_data.index, y=st.session_state['equity_curve'], mode='lines', name='Equity Curve'), row=3, col=1)
# Move the legend outside the plot and increase gap
fig.update_layout(
title=f'{ticker} Strategy with Adjusted Parameters',
xaxis_title='Date',
yaxis_title='Price',
legend=dict(orientation="h", yanchor="bottom", y=1.15, xanchor="center", x=0.5, traceorder='normal', valign='top', borderwidth=0),
height=1000,
margin=dict(t=30, b=30),
font=dict(size=12),
annotations=[dict(font=dict(color="white", size=14)) for _ in range(3)]
)
# Display the chart
st.plotly_chart(fig, use_container_width=True)
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True) |