-
Notifications
You must be signed in to change notification settings - Fork 1
/
dash_strat_moving_avg.py
153 lines (126 loc) · 5.17 KB
/
dash_strat_moving_avg.py
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
""" This is a Dash App for a Moving Average Crossover Strategy. """
""" The stock prices are loaded from a CSV file. """
""" Adapted from: https://plotly.com/python/dropdowns/ """
""" It uses Bootstrap Components for greater control of the slider layout. """
""" https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/ """
# Run this Dash app as follows:
# In a terminal run:
# python3 /Users/jerzy/Develop/Python/dash_strat_moving_avg.py
# Then in the browser open the url:
# http://127.0.0.1:8050/
# Ignore FutureWarning
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# import datetime
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
from utils import get_symbol, calc_sharpe
from strategies import strat_movavg
# Define marks for sliders
marklb = {i: {"label": str(i), "style": {"fontSize": "24px"}} for i in range(10, 120, 10)}
marklag = {i: {"label": str(i), "style": {"fontSize": "24px"}} for i in range(1, 7, 1)}
# marklag = {i: {"label": str(i), "style": {"transform": "scale(2.0)"}} for i in range(1, 7, 1)}
# Define parameters
symbol = "SPY"
range = "daily"
# range = "minute"
# Load OHLC stock prices from CSV file
filename = "/Users/jerzy/Develop/data/" + symbol + "_" + range + ".csv"
ohlc = pd.read_csv(filename)
# Download OHLC stock prices from Polygon for the symbol
# startd = datetime.date(2000, 1, 1)
# endd = datetime.date.today()
# ohlc = get_symbol(symbol=symbol, startd=startd, endd=endd, range=range)
# Calculate log stock prices
# ohlc[["Open", "High", "Low", "Close"]] = np.log(ohlc[["Open", "High", "Low", "Close"]])
# Select time slice of data
# startd = pd.to_datetime("2019").date()
# endd = pd.to_datetime("2022").date()
# ohlcsub = ohlc.loc[startd:endd]
# ohlcsub = ohlc.loc["2019":"2022"]
# Drop non-market data
# if (range == "minute"):
# ohlc = ohlc.drop(ohlc.between_time("0:00", "9:00").index)
# ohlc = ohlc.drop(ohlc.between_time("17:00", "23:59").index)
# Extract time index
# datev = ohlc.index
datev = ohlc.Date
# Calculate the log percentage returns
closep = ohlc.Close
retsp = np.log(closep).diff()
# Calculate the Sharpe ratio and cumulative returns
retsum = retsp.cumsum()
sharpev = round(calc_sharpe(retsp), 3)
## Create the Dash web app
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container([
html.H1("Dash App for Moving Average Strategy"),
html.Br(),
dbc.Row([
dbc.Col([
# Create the look-back slider
html.H3("Select the look-back interval:", style={"color": "blue"}),
dcc.Slider(id="ma_slider", min=10, max=120, step=1, value=50, marks=marklb,
tooltip={"placement": "top", "always_visible": True})
]),
dbc.Col([
# Create the lag slider
html.H3("Select the lag amount:", style={"color": "blue"}),
dcc.Slider(id="lag_slider", min=1, max=7, step=1, value=6, marks=marklag,
tooltip={"placement": "top", "always_visible": True})
]),
],
style={"width": "70%", "font-size": "24", "color": "red"}),
html.Br(),
print("Reading from sliders..."),
# Create the plot
dcc.Graph(id="time_series_chart")
], fluid=True)
# Define the Dash app server function
@app.callback(
Output("time_series_chart", "figure"),
[Input("ma_slider", "value"),
Input("lag_slider", "value")]
)
def display_time_series(lookback, lagv):
## Calculate the strategy returns
retstrat = strat_movavg(closep, retsp, lookback, lagv)
# Calculate the strategy Sharpe ratio
sharpestrat = calc_sharpe(retstrat)
textv = "Strategy Sharpe = " + str(round(sharpestrat, 3)) + "<br>" + \
symbol + " Sharpe = " + str(sharpev) + "<br>" + \
"lookback = " + str(lookback) + "<br>" + \
"lagv = " + str(lagv)
# Calculate the cumulative strategy returns
# retstrat = retstrat.cumsum()
## Plot the strategy returns
plotfig = go.Figure()
# Modify plot aesthetics
# https://plotly.com/python/reference/layout/
plotfig.update_layout(title_text="Moving Average Strategy for: " + symbol,
title_font_size=34, title_font_color="black",
yaxis_title="Cumulative Returns", font_color="black", font_size=18,
xaxis_rangeslider_visible=False, width=1400, height=850)
plotfig.add_annotation(text=textv, font=dict(family="bold", size=18), align="left",
showarrow=False, xref='paper', yref='paper', x=0.5, y=0.9)
# Add trace for symbol
plotfig.add_trace(go.Scatter(x=datev, y=retsum,
name=symbol, line=dict(color="blue")))
# Add trace for strategy
plotfig.add_trace(go.Scatter(x=datev, y=retstrat.cumsum(),
name="Strategy", line=dict(color="red")))
# Customize legend
plotfig.update_layout(legend=dict(x=0.2, y=0.9, traceorder="normal", itemsizing="constant",
font=dict(family="sans-serif", size=18, color="blue")))
return plotfig
if __name__ == "__main__":
# On Mac:
app.run_server(debug=True, port=8050)
# On mini server:
# app.run_server(debug=True, host="0.0.0.0")