Create ui_helper.py
Browse files- utils/ui_helper.py +141 -0
utils/ui_helper.py
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# app.py
|
2 |
+
import streamlit as st
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
import pandas as pd
|
5 |
+
from helpers.app_helper_algotrader import *
|
6 |
+
|
7 |
+
|
8 |
+
def main_algo_trader():
|
9 |
+
st.write("# Welcome to Algorithmic Trading! A Toy Implementation👋")
|
10 |
+
st.sidebar.success("Select a session from the menu of content.")
|
11 |
+
|
12 |
+
st.markdown(
|
13 |
+
r"""
|
14 |
+
The following app is a simple demonstration of the growth stock strategy. For simplicity, we assume our research team hand over a pool of stocks. Amongst the pool of stocks, we can do the following:
|
15 |
+
- use `yfinance` library to download stock data live (for the sake of speed, please start with time frame "1mo");
|
16 |
+
- every period (time frame is a tuning parameter), we balance our portfolio (equal weight) by holding the top n stocks (n can be top quintile/quartile of stocks);
|
17 |
+
"""
|
18 |
+
)
|
19 |
+
|
20 |
+
# Main inputs
|
21 |
+
tickers = st.text_input(
|
22 |
+
"Enter tickers (comma-separated):",
|
23 |
+
"AXP, AMGN, AAPL, BA, CAT, CSCO, CVX, GS, HD, HON, IBM, INTC, JNJ, KO, JPM, MCD, MMM, MRK, MSFT, NKE, PG, TRV, UNH, CRM, VZ, V, WBA, WMT, DIS, DOW, XOM, WFC, MA, COST, AVGO, ADBE, C, NFLX, PYPL, TSLA, NVDA",
|
24 |
+
)
|
25 |
+
start_date = st.date_input("Start date", pd.to_datetime("2001-01-01"))
|
26 |
+
end_date = st.date_input("End date", pd.to_datetime("2023-12-31"))
|
27 |
+
time_frame = st.selectbox(
|
28 |
+
"Select Time Frame:",
|
29 |
+
[
|
30 |
+
"1mo",
|
31 |
+
"3mo",
|
32 |
+
],
|
33 |
+
)
|
34 |
+
top_n = st.number_input("Top n stocks", min_value=1, value=3)
|
35 |
+
|
36 |
+
# Process inputs
|
37 |
+
tickers_list = [ticker.strip() for ticker in tickers.split(",")]
|
38 |
+
|
39 |
+
# Run analysis on button click
|
40 |
+
if st.button("Run Analysis"):
|
41 |
+
with st.spinner("Downloading data and calculating returns..."):
|
42 |
+
stock_data = download_stock_data(
|
43 |
+
tickers_list,
|
44 |
+
start_date.strftime("%Y-%m-%d"),
|
45 |
+
end_date.strftime("%Y-%m-%d"),
|
46 |
+
w=time_frame,
|
47 |
+
)
|
48 |
+
returns_data = create_portfolio_and_calculate_returns(stock_data, top_n)
|
49 |
+
benchmark_sharpe_ratio = (
|
50 |
+
returns_data["benchmark"].mean() / returns_data["benchmark"].std()
|
51 |
+
)
|
52 |
+
portfolio_sharpe_ratio = (
|
53 |
+
returns_data["portfolio_returns"].mean()
|
54 |
+
/ returns_data["portfolio_returns"].std()
|
55 |
+
)
|
56 |
+
|
57 |
+
# Data for plotting
|
58 |
+
df = returns_data[
|
59 |
+
["rolling_benchmark", "rolling_portfolio_returns", "portfolio_history"]
|
60 |
+
]
|
61 |
+
df.index = pd.to_datetime(df.index, unit="ms")
|
62 |
+
|
63 |
+
# Create plot
|
64 |
+
fig = go.Figure()
|
65 |
+
fig.add_trace(
|
66 |
+
go.Scatter(
|
67 |
+
x=df.index,
|
68 |
+
y=df["rolling_benchmark"],
|
69 |
+
mode="lines",
|
70 |
+
name="Rolling Benchmark",
|
71 |
+
)
|
72 |
+
)
|
73 |
+
fig.add_trace(
|
74 |
+
go.Scatter(
|
75 |
+
x=df.index,
|
76 |
+
y=df["rolling_portfolio_returns"],
|
77 |
+
mode="lines",
|
78 |
+
name="Rolling Portfolio Returns",
|
79 |
+
)
|
80 |
+
)
|
81 |
+
|
82 |
+
for date, stocks in df["portfolio_history"].items():
|
83 |
+
fig.add_shape(
|
84 |
+
type="line",
|
85 |
+
x0=date,
|
86 |
+
y0=0,
|
87 |
+
x1=date,
|
88 |
+
y1=0,
|
89 |
+
line=dict(color="RoyalBlue", width=1),
|
90 |
+
)
|
91 |
+
fig.add_annotation(
|
92 |
+
x=date,
|
93 |
+
y=0.5,
|
94 |
+
text=str(stocks),
|
95 |
+
showarrow=False,
|
96 |
+
yshift=10,
|
97 |
+
textangle=-90,
|
98 |
+
font=dict(size=15), # You can adjust the size as needed
|
99 |
+
)
|
100 |
+
|
101 |
+
# Calculate means and standard deviations
|
102 |
+
benchmark_mean = returns_data["benchmark"].mean()
|
103 |
+
benchmark_std = returns_data["benchmark"].std()
|
104 |
+
portfolio_mean = returns_data["portfolio_returns"].mean()
|
105 |
+
portfolio_std = returns_data["portfolio_returns"].std()
|
106 |
+
|
107 |
+
# Update title text with additional information
|
108 |
+
if time_frame == '1mo':
|
109 |
+
some_n_based_on_time_frame = 12
|
110 |
+
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame)
|
111 |
+
in_50_years = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame*50)
|
112 |
+
else:
|
113 |
+
some_n_based_on_time_frame = 4
|
114 |
+
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame)
|
115 |
+
in_50_years = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame*50)
|
116 |
+
title_text = (
|
117 |
+
f"Performance:<br>"
|
118 |
+
f"Benchmark Sharpe Ratio = {benchmark_sharpe_ratio:.3f}, "
|
119 |
+
f"Portfolio Sharpe Ratio = {portfolio_sharpe_ratio:.3f}, "
|
120 |
+
f"based on time frame: {time_frame}<br>"
|
121 |
+
f"Benchmark => Mean: {benchmark_mean:.4f}, Std: {benchmark_std:.4f}; "
|
122 |
+
f"Portfolio => Mean: {portfolio_mean:.4f}, Std: {portfolio_std:.4f}<br>"
|
123 |
+
f"---<br>"
|
124 |
+
f"This may or may not be a small number, let's check: <br>"
|
125 |
+
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame})={in_a_year}, <br>"
|
126 |
+
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame}*50)={in_50_years}."
|
127 |
+
)
|
128 |
+
curr_max_num = max(
|
129 |
+
df.rolling_benchmark.max(), df.rolling_portfolio_returns.max()
|
130 |
+
)
|
131 |
+
fig.update_layout(
|
132 |
+
title=title_text,
|
133 |
+
xaxis_title="Date",
|
134 |
+
yaxis_title="Value",
|
135 |
+
yaxis=dict(range=[0, curr_max_num * 1.1]),
|
136 |
+
legend=dict(
|
137 |
+
orientation="h", x=0.5, y=-0.4, xanchor="center", yanchor="bottom"
|
138 |
+
),
|
139 |
+
)
|
140 |
+
|
141 |
+
st.plotly_chart(fig, use_container_width=True)
|