apps updated
Browse files- app.py +47 -6
- pages/1_english_.py +1 -1
- pages/2_chinese_.py +1 -1
- utils/helper.py +1 -1
- utils/ui_helper.py +47 -32
app.py
CHANGED
@@ -16,11 +16,7 @@ import streamlit as st
|
|
16 |
|
17 |
|
18 |
def run():
|
19 |
-
st.set_page_config(
|
20 |
-
page_title="Momentum-Strategy",
|
21 |
-
page_icon="💹",
|
22 |
-
layout="wide"
|
23 |
-
)
|
24 |
|
25 |
st.write("# Welcome to the Momentum Strategy Simulator! 💹")
|
26 |
|
@@ -56,6 +52,51 @@ def run():
|
|
56 |
"""
|
57 |
)
|
58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
if __name__ == "__main__":
|
61 |
-
run()
|
|
|
16 |
|
17 |
|
18 |
def run():
|
19 |
+
st.set_page_config(page_title="Momentum-Strategy", page_icon="💹", layout="wide")
|
|
|
|
|
|
|
|
|
20 |
|
21 |
st.write("# Welcome to the Momentum Strategy Simulator! 💹")
|
22 |
|
|
|
52 |
"""
|
53 |
)
|
54 |
|
55 |
+
with st.expander("Reference (Expand/Collapse)"):
|
56 |
+
st.markdown(
|
57 |
+
f"""
|
58 |
+
# Factor Models and Mutual Fund Evaluation
|
59 |
+
|
60 |
+
## Monthly Momentum Factor (MOM)
|
61 |
+
|
62 |
+
The Monthly Momentum Factor (MOM) can be calculated by subtracting the equal-weighted average of the lowest performing firms from the equal-weighted average of the highest performing firms, lagged one month (Carhart, 1997). A stock exhibits momentum if its prior 12-month average of returns is positive. Similar to the three-factor model, the momentum factor is defined by a self-financing portfolio of (long positive momentum) + (short negative momentum). Momentum strategies remain popular in financial markets, and financial analysts often incorporate the 52-week price high/low in their Buy/Sell recommendations.
|
63 |
+
|
64 |
+
## Four-Factor Model
|
65 |
+
|
66 |
+
The four-factor model is commonly used for active management and mutual fund evaluation. Three commonly used methods to adjust a mutual fund's returns for risk are:
|
67 |
+
|
68 |
+
### 1. Market Model:
|
69 |
+
|
70 |
+
$$
|
71 |
+
EXR_t = α^J + β_mkt * EXMKT_t + ε_t
|
72 |
+
$$
|
73 |
+
|
74 |
+
The intercept in this model is referred to as "Jensen's alpha".
|
75 |
+
|
76 |
+
### 2. Fama–French Three-Factor Model:
|
77 |
+
|
78 |
+
$$
|
79 |
+
EXR_t = α^FF + β_mkt * EXMKT_t + β_HML * HML_t + β_SMB * SMB_t + ε_t
|
80 |
+
$$
|
81 |
+
|
82 |
+
The intercept in this model is referred to as the "three-factor alpha".
|
83 |
+
|
84 |
+
### 3. Carhart Four-Factor Model:
|
85 |
+
|
86 |
+
$$
|
87 |
+
EXR_t = α^c + β_mkt * EXMKT_t + β_HML * HML_t + β_SMB * SMB_t + β_UMD * UMD_t + ε_t
|
88 |
+
$$
|
89 |
+
|
90 |
+
The intercept in this model is referred to as the "four-factor alpha".
|
91 |
+
|
92 |
+
`EXR_t` is the monthly return to the asset of concern in excess of the monthly t-bill rate. These models are used to adjust for risk by regressing the excess returns of the asset on an intercept (the alpha) and some factors on the right-hand side of the equation that attempt to control for market-wide risk factors. The right-hand side risk factors include the monthly return of the CRSP value-weighted index less the risk-free rate (`EXMKT_t`), monthly premium of the book-to-market factor (`HML_t`), monthly premium of the size factor (`SMB_t`), and the monthly premium on winners minus losers (`UMD_t`) from Fama-French (1993) and Carhart (1997).
|
93 |
+
|
94 |
+
A fund manager demonstrates forecasting ability when their fund has a positive and statistically significant alpha.
|
95 |
+
|
96 |
+
SMB is a zero-investment portfolio that is long on small capitalization (cap) stocks and short on big-cap stocks. Similarly, HML is a zero-investment portfolio that is long on high book-to-market (B/M) stocks and short on low B/M stocks, and UMD is a zero-cost portfolio that is long previous 12-month return winners and short previous 12-month loser stocks.
|
97 |
+
"""
|
98 |
+
)
|
99 |
+
|
100 |
|
101 |
if __name__ == "__main__":
|
102 |
+
run()
|
pages/1_english_.py
CHANGED
@@ -3,4 +3,4 @@ from utils.helper import *
|
|
3 |
from utils.ui_helper import *
|
4 |
|
5 |
# run ui
|
6 |
-
main_algo_trader()
|
|
|
3 |
from utils.ui_helper import *
|
4 |
|
5 |
# run ui
|
6 |
+
main_algo_trader()
|
pages/2_chinese_.py
CHANGED
@@ -3,4 +3,4 @@ from utils.helper import *
|
|
3 |
from utils.ui_helper import *
|
4 |
|
5 |
# run ui
|
6 |
-
main_algo_trader_chinese()
|
|
|
3 |
from utils.ui_helper import *
|
4 |
|
5 |
# run ui
|
6 |
+
main_algo_trader_chinese()
|
utils/helper.py
CHANGED
@@ -74,4 +74,4 @@ def create_portfolio_and_calculate_returns(
|
|
74 |
new_returns_data["portfolio_returns"] + 1
|
75 |
).cumprod()
|
76 |
|
77 |
-
return new_returns_data
|
|
|
74 |
new_returns_data["portfolio_returns"] + 1
|
75 |
).cumprod()
|
76 |
|
77 |
+
return new_returns_data
|
utils/ui_helper.py
CHANGED
@@ -4,7 +4,8 @@ import streamlit as st
|
|
4 |
import plotly.graph_objects as go
|
5 |
import pandas as pd
|
6 |
from utils.helper import *
|
7 |
-
|
|
|
8 |
# english
|
9 |
def main_algo_trader():
|
10 |
# Front-end Design
|
@@ -25,9 +26,11 @@ def main_algo_trader():
|
|
25 |
tickers = st.text_input(
|
26 |
"Enter tickers (comma-separated):",
|
27 |
"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",
|
28 |
-
)
|
29 |
start_date = st.sidebar.date_input("Start date", pd.to_datetime("2001-01-01"))
|
30 |
-
end_date = st.sidebar.date_input(
|
|
|
|
|
31 |
time_frame = st.sidebar.selectbox(
|
32 |
"Select Time Frame:",
|
33 |
[
|
@@ -36,7 +39,9 @@ def main_algo_trader():
|
|
36 |
],
|
37 |
)
|
38 |
top_n = st.sidebar.number_input("Top n stocks", min_value=1, value=3)
|
39 |
-
height_of_graph = st.sidebar.number_input(
|
|
|
|
|
40 |
|
41 |
# Process inputs
|
42 |
tickers_list = [ticker.strip() for ticker in tickers.split(",")]
|
@@ -69,8 +74,8 @@ def main_algo_trader():
|
|
69 |
@st.cache_data
|
70 |
def convert_df(df):
|
71 |
# IMPORTANT: Cache the conversion to prevent computation on every rerun
|
72 |
-
return df.to_csv().encode(
|
73 |
-
|
74 |
csv = convert_df(df)
|
75 |
|
76 |
# Create plot
|
@@ -118,14 +123,18 @@ def main_algo_trader():
|
|
118 |
portfolio_std = returns_data["portfolio_returns"].std()
|
119 |
|
120 |
# Update title text with additional information
|
121 |
-
if time_frame ==
|
122 |
some_n_based_on_time_frame = 12
|
123 |
-
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame)
|
124 |
-
in_50_years = 1000*(1+portfolio_mean)**(
|
|
|
|
|
125 |
else:
|
126 |
some_n_based_on_time_frame = 4
|
127 |
-
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame)
|
128 |
-
in_50_years = 1000*(1+portfolio_mean)**(
|
|
|
|
|
129 |
title_text = (
|
130 |
f"Performance:<br>"
|
131 |
f"Benchmark Sharpe Ratio = {benchmark_sharpe_ratio:.3f}, "
|
@@ -158,8 +167,8 @@ def main_algo_trader():
|
|
158 |
st.download_button(
|
159 |
label="Download data as CSV",
|
160 |
data=csv,
|
161 |
-
file_name=f
|
162 |
-
mime=
|
163 |
)
|
164 |
|
165 |
|
@@ -183,9 +192,11 @@ def main_algo_trader_chinese():
|
|
183 |
tickers = st.text_input(
|
184 |
"使用英文键入输入股票代码(以逗号分隔):",
|
185 |
"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",
|
186 |
-
)
|
187 |
start_date = st.sidebar.date_input("开始日期", pd.to_datetime("2001-01-01"))
|
188 |
-
end_date = st.sidebar.date_input(
|
|
|
|
|
189 |
time_frame = st.sidebar.selectbox(
|
190 |
"选择时间框架:",
|
191 |
[
|
@@ -227,8 +238,8 @@ def main_algo_trader_chinese():
|
|
227 |
@st.cache_data
|
228 |
def convert_df(df):
|
229 |
# IMPORTANT: Cache the conversion to prevent computation on every rerun
|
230 |
-
return df.to_csv().encode(
|
231 |
-
|
232 |
csv = convert_df(df)
|
233 |
|
234 |
# Create plot
|
@@ -276,23 +287,27 @@ def main_algo_trader_chinese():
|
|
276 |
portfolio_std = returns_data["portfolio_returns"].std()
|
277 |
|
278 |
# Update title text with additional information
|
279 |
-
if time_frame ==
|
280 |
some_n_based_on_time_frame = 12
|
281 |
-
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame)
|
282 |
-
in_50_years = 1000*(1+portfolio_mean)**(
|
|
|
|
|
283 |
else:
|
284 |
some_n_based_on_time_frame = 4
|
285 |
-
in_a_year = 1000*(1+portfolio_mean)**(some_n_based_on_time_frame)
|
286 |
-
in_50_years = 1000*(1+portfolio_mean)**(
|
|
|
|
|
287 |
title_text = (
|
288 |
-
f"
|
289 |
-
f"
|
290 |
-
f"
|
291 |
-
f"
|
292 |
-
f"
|
293 |
-
f"
|
294 |
f"---<br>"
|
295 |
-
f"
|
296 |
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame})={in_a_year}, <br>"
|
297 |
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame}*50)={in_50_years}."
|
298 |
)
|
@@ -316,6 +331,6 @@ def main_algo_trader_chinese():
|
|
316 |
st.download_button(
|
317 |
label="Download data as CSV",
|
318 |
data=csv,
|
319 |
-
file_name=f
|
320 |
-
mime=
|
321 |
-
)
|
|
|
4 |
import plotly.graph_objects as go
|
5 |
import pandas as pd
|
6 |
from utils.helper import *
|
7 |
+
|
8 |
+
|
9 |
# english
|
10 |
def main_algo_trader():
|
11 |
# Front-end Design
|
|
|
26 |
tickers = st.text_input(
|
27 |
"Enter tickers (comma-separated):",
|
28 |
"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",
|
29 |
+
)
|
30 |
start_date = st.sidebar.date_input("Start date", pd.to_datetime("2001-01-01"))
|
31 |
+
end_date = st.sidebar.date_input(
|
32 |
+
"End date", pd.to_datetime(datetime.now().strftime("%Y-%m-%d"))
|
33 |
+
)
|
34 |
time_frame = st.sidebar.selectbox(
|
35 |
"Select Time Frame:",
|
36 |
[
|
|
|
39 |
],
|
40 |
)
|
41 |
top_n = st.sidebar.number_input("Top n stocks", min_value=1, value=3)
|
42 |
+
height_of_graph = st.sidebar.number_input(
|
43 |
+
"Height of the plot", min_value=500, value=750
|
44 |
+
)
|
45 |
|
46 |
# Process inputs
|
47 |
tickers_list = [ticker.strip() for ticker in tickers.split(",")]
|
|
|
74 |
@st.cache_data
|
75 |
def convert_df(df):
|
76 |
# IMPORTANT: Cache the conversion to prevent computation on every rerun
|
77 |
+
return df.to_csv().encode("utf-8")
|
78 |
+
|
79 |
csv = convert_df(df)
|
80 |
|
81 |
# Create plot
|
|
|
123 |
portfolio_std = returns_data["portfolio_returns"].std()
|
124 |
|
125 |
# Update title text with additional information
|
126 |
+
if time_frame == "1mo":
|
127 |
some_n_based_on_time_frame = 12
|
128 |
+
in_a_year = 1000 * (1 + portfolio_mean) ** (some_n_based_on_time_frame)
|
129 |
+
in_50_years = 1000 * (1 + portfolio_mean) ** (
|
130 |
+
some_n_based_on_time_frame * 50
|
131 |
+
)
|
132 |
else:
|
133 |
some_n_based_on_time_frame = 4
|
134 |
+
in_a_year = 1000 * (1 + portfolio_mean) ** (some_n_based_on_time_frame)
|
135 |
+
in_50_years = 1000 * (1 + portfolio_mean) ** (
|
136 |
+
some_n_based_on_time_frame * 50
|
137 |
+
)
|
138 |
title_text = (
|
139 |
f"Performance:<br>"
|
140 |
f"Benchmark Sharpe Ratio = {benchmark_sharpe_ratio:.3f}, "
|
|
|
167 |
st.download_button(
|
168 |
label="Download data as CSV",
|
169 |
data=csv,
|
170 |
+
file_name=f"history_{end_date}.csv",
|
171 |
+
mime="text/csv",
|
172 |
)
|
173 |
|
174 |
|
|
|
192 |
tickers = st.text_input(
|
193 |
"使用英文键入输入股票代码(以逗号分隔):",
|
194 |
"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",
|
195 |
+
)
|
196 |
start_date = st.sidebar.date_input("开始日期", pd.to_datetime("2001-01-01"))
|
197 |
+
end_date = st.sidebar.date_input(
|
198 |
+
"结束日期", pd.to_datetime(datetime.now().strftime("%Y-%m-%d"))
|
199 |
+
)
|
200 |
time_frame = st.sidebar.selectbox(
|
201 |
"选择时间框架:",
|
202 |
[
|
|
|
238 |
@st.cache_data
|
239 |
def convert_df(df):
|
240 |
# IMPORTANT: Cache the conversion to prevent computation on every rerun
|
241 |
+
return df.to_csv().encode("utf-8")
|
242 |
+
|
243 |
csv = convert_df(df)
|
244 |
|
245 |
# Create plot
|
|
|
287 |
portfolio_std = returns_data["portfolio_returns"].std()
|
288 |
|
289 |
# Update title text with additional information
|
290 |
+
if time_frame == "1mo":
|
291 |
some_n_based_on_time_frame = 12
|
292 |
+
in_a_year = 1000 * (1 + portfolio_mean) ** (some_n_based_on_time_frame)
|
293 |
+
in_50_years = 1000 * (1 + portfolio_mean) ** (
|
294 |
+
some_n_based_on_time_frame * 50
|
295 |
+
)
|
296 |
else:
|
297 |
some_n_based_on_time_frame = 4
|
298 |
+
in_a_year = 1000 * (1 + portfolio_mean) ** (some_n_based_on_time_frame)
|
299 |
+
in_50_years = 1000 * (1 + portfolio_mean) ** (
|
300 |
+
some_n_based_on_time_frame * 50
|
301 |
+
)
|
302 |
title_text = (
|
303 |
+
f"业绩:<br>"
|
304 |
+
f"标杆回报风险比 = {benchmark_sharpe_ratio:.3f}, "
|
305 |
+
f"投资组合回报风险比 = {portfolio_sharpe_ratio:.3f}, "
|
306 |
+
f"交易窗口: {time_frame}<br>"
|
307 |
+
f"标杆 => Mean: {benchmark_mean:.4f}, Std: {benchmark_std:.4f}; "
|
308 |
+
f"投资组合 => Mean: {portfolio_mean:.4f}, Std: {portfolio_std:.4f}<br>"
|
309 |
f"---<br>"
|
310 |
+
f"这个数字如何理解,我们以1000块钱计算以下: <br>"
|
311 |
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame})={in_a_year}, <br>"
|
312 |
f"$1,000*(1+{portfolio_mean:.4f})^({some_n_based_on_time_frame}*50)={in_50_years}."
|
313 |
)
|
|
|
331 |
st.download_button(
|
332 |
label="Download data as CSV",
|
333 |
data=csv,
|
334 |
+
file_name=f"history_{end_date}.csv",
|
335 |
+
mime="text/csv",
|
336 |
+
)
|