|
|
|
from datetime import datetime |
|
import streamlit as st |
|
import plotly.graph_objects as go |
|
import pandas as pd |
|
from utils.helper import * |
|
|
|
|
|
def main_algo_trader(): |
|
|
|
st.set_page_config(layout="wide") |
|
st.write("# Welcome to Algorithmic Trading! A Quick Implementation👋") |
|
|
|
with st.sidebar: |
|
with st.expander("Expand/Collapse"): |
|
st.markdown( |
|
r""" |
|
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: |
|
- use `yfinance` library to download stock data live (for the sake of speed, please start with time frame "1mo"); |
|
- 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); |
|
""" |
|
) |
|
|
|
|
|
tickers = st.text_input( |
|
"Enter tickers (comma-separated):", |
|
"MSFT, AAPL, NVDA, GOOG, AMZN, META, LLY, AVGO, TSLA, JPM, V, WMT, UNH, MA, PG, HD, JNJ, ORCL, MRK, COST, ABBV, BAC, CRM, AMD, NFLX, ACN, ADBE, DIS, TMO, WFC, MCD, CSCO, ABT, QCOM, INTC, INTU, IBM, AMAT, CMCSA, AXP, PFE, NOW, AMGN, MU, SHOP, DASH, MRVL, TDG, ISRG", |
|
) |
|
start_date = st.sidebar.date_input("Start date", pd.to_datetime("2001-01-01")) |
|
end_date = st.sidebar.date_input("End date", pd.to_datetime(datetime.now().strftime("%Y-%m-%d"))) |
|
time_frame = st.sidebar.selectbox( |
|
"Select Time Frame:", |
|
[ |
|
"1mo", |
|
"3mo", |
|
], |
|
) |
|
top_n = st.sidebar.number_input("Top n stocks", min_value=1, value=3) |
|
|
|
|
|
tickers_list = [ticker.strip() for ticker in tickers.split(",")] |
|
|
|
|
|
if st.button("Run Analysis"): |
|
with st.spinner("Downloading data and calculating returns..."): |
|
stock_data = download_stock_data( |
|
tickers_list, |
|
start_date.strftime("%Y-%m-%d"), |
|
end_date.strftime("%Y-%m-%d"), |
|
w=time_frame, |
|
) |
|
returns_data = create_portfolio_and_calculate_returns(stock_data, top_n) |
|
benchmark_sharpe_ratio = ( |
|
returns_data["benchmark"].mean() / returns_data["benchmark"].std() |
|
) |
|
portfolio_sharpe_ratio = ( |
|
returns_data["portfolio_returns"].mean() |
|
/ returns_data["portfolio_returns"].std() |
|
) |
|
|
|
|
|
df = returns_data[ |
|
["rolling_benchmark", "rolling_portfolio_returns", "portfolio_history"] |
|
] |
|
df.index = pd.to_datetime(df.index, unit="ms") |
|
|
|
|
|
fig = go.Figure() |
|
fig.add_trace( |
|
go.Scatter( |
|
x=df.index, |
|
y=df["rolling_benchmark"], |
|
mode="lines", |
|
name="Rolling Benchmark", |
|
) |
|
) |
|
fig.add_trace( |
|
go.Scatter( |
|
x=df.index, |
|
y=df["rolling_portfolio_returns"], |
|
mode="lines", |
|
name="Rolling Portfolio Returns", |
|
) |
|
) |
|
|
|
for date, stocks in df["portfolio_history"].items(): |
|
fig.add_shape( |
|
type="line", |
|
x0=date, |
|
y0=0, |
|
x1=date, |
|
y1=0, |
|
line=dict(color="RoyalBlue", width=1), |
|
) |
|
fig.add_annotation( |
|
x=date, |
|
y=0.5, |
|
text=str(stocks), |
|
showarrow=False, |
|
yshift=10, |
|
textangle=-90, |
|
font=dict(size=15), |
|
) |
|
|
|
|
|
benchmark_mean = returns_data["benchmark"].mean() |
|
benchmark_std = returns_data["benchmark"].std() |
|
portfolio_mean = returns_data["portfolio_returns"].mean() |
|
portfolio_std = returns_data["portfolio_returns"].std() |
|
|
|
|
|
if time_frame == '1mo': |
|
some_n_based_on_time_frame = 12 |
|
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame) |
|
in_50_years = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame*50) |
|
else: |
|
some_n_based_on_time_frame = 4 |
|
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame) |
|
in_50_years = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame*50) |
|
title_text = ( |
|
f"Performance:<br>" |
|
f"Benchmark Sharpe Ratio = {benchmark_sharpe_ratio:.3f}, " |
|
f"Portfolio Sharpe Ratio = {portfolio_sharpe_ratio:.3f}, " |
|
f"based on time frame: {time_frame}<br>" |
|
f"Benchmark => Mean: {benchmark_mean:.4f}, Std: {benchmark_std:.4f}; " |
|
f"Portfolio => Mean: {portfolio_mean:.4f}, Std: {portfolio_std:.4f}<br>" |
|
f"---<br>" |
|
f"This may or may not be a small number, let's check: <br>" |
|
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame})={in_a_year}, <br>" |
|
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame}*50)={in_50_years}." |
|
) |
|
curr_max_num = max( |
|
df.rolling_benchmark.max(), df.rolling_portfolio_returns.max() |
|
) |
|
fig.update_layout( |
|
title=title_text, |
|
xaxis_title="Date", |
|
yaxis_title="Value", |
|
yaxis=dict(range=[0, curr_max_num * 1.1]), |
|
legend=dict( |
|
orientation="h", x=0.5, y=-0.4, xanchor="center", yanchor="bottom" |
|
), |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |