Spaces:
Running
Running
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import plotly.graph_objects as go
|
3 |
+
import pygwalker as pyg
|
4 |
+
import streamlit as st
|
5 |
+
import streamlit.components.v1 as components
|
6 |
+
import yfinance as yf
|
7 |
+
from plotly.subplots import make_subplots
|
8 |
+
from scipy.stats import norm
|
9 |
+
|
10 |
+
from utils.helper import *
|
11 |
+
|
12 |
+
# Streamlit app layout
|
13 |
+
st.set_page_config(layout="wide")
|
14 |
+
st.title("📊 Technical Trading Strategy Simulation 💹")
|
15 |
+
|
16 |
+
|
17 |
+
# Sidebar inputs
|
18 |
+
ticker = st.sidebar.text_input("Enter Stock Ticker", "AAPL").upper()
|
19 |
+
start_date = st.sidebar.date_input("Start Date", pd.to_datetime("2020-01-01"))
|
20 |
+
end_date = st.sidebar.date_input("End Date", pd.to_datetime("today"))
|
21 |
+
|
22 |
+
# Add sidebar slider for selecting two integers
|
23 |
+
st.sidebar.success("Please select your own parameters.")
|
24 |
+
short_window = st.sidebar.slider(
|
25 |
+
"Select short window size", min_value=2, max_value=200, value=12
|
26 |
+
)
|
27 |
+
long_window = st.sidebar.slider(
|
28 |
+
"Select long window size", min_value=2, max_value=250, value=50
|
29 |
+
)
|
30 |
+
signal_window = st.sidebar.slider(
|
31 |
+
"Select signal window size", min_value=2, max_value=250, value=9
|
32 |
+
)
|
33 |
+
values = st.sidebar.slider(
|
34 |
+
"Select a range of values", min_value=-100, max_value=100, value=(-10, 10), step=1
|
35 |
+
)
|
36 |
+
option = st.sidebar.selectbox(
|
37 |
+
"How would you like rescale data?", ("Original", "Normalization", "Percentile")
|
38 |
+
)
|
39 |
+
|
40 |
+
# Add submit button in the sidebar
|
41 |
+
submit_button = st.sidebar.button("Submit")
|
42 |
+
|
43 |
+
# Update to execute changes only when the submit button is clicked
|
44 |
+
if submit_button:
|
45 |
+
with st.spinner("Wait for it..."):
|
46 |
+
# Message
|
47 |
+
if option == "Original":
|
48 |
+
st.success(
|
49 |
+
"We use the stock price (within the range selected) to create the MACD and Signal Line (which numerically vary based on price data)."
|
50 |
+
)
|
51 |
+
elif option == "Normalization":
|
52 |
+
st.success(
|
53 |
+
"We use the stock price (within the range selected) to create the MACD and Signal Line (which numerically vary based on price data). Next, we normalize the MACD/Signal Line so that fall in a consistent range, i.e. approximately from -2 to 2."
|
54 |
+
)
|
55 |
+
else:
|
56 |
+
st.success(
|
57 |
+
"We use the stock price (within the range selected) to create the MACD and Signal Line (which numerically vary based on price data). Next, we normalize the MACD/Signal Line so that fall in a consistent range, i.e. approximately from -2 to 2. Last, we use the normalized data to create probabilities from -100% to +100%. The probability means statistically what is believed to reverse the current direction."
|
58 |
+
)
|
59 |
+
|
60 |
+
# Download stock data
|
61 |
+
data = yf.download(ticker, start=start_date, end=end_date)
|
62 |
+
|
63 |
+
if not data.empty:
|
64 |
+
if option == "Normalization":
|
65 |
+
data = calculate_normalized_macd(
|
66 |
+
data, short_window, long_window, signal_window
|
67 |
+
)
|
68 |
+
some_warning_message = "normalized data"
|
69 |
+
elif option == "Percentile":
|
70 |
+
data = calculate_percentile_macd(
|
71 |
+
data, short_window, long_window, signal_window
|
72 |
+
)
|
73 |
+
some_warning_message = "percentile data (numbers in %)"
|
74 |
+
else:
|
75 |
+
data = calculate_macd(data, short_window, long_window, signal_window)
|
76 |
+
some_warning_message = "original data"
|
77 |
+
data = find_crossovers(data, values[0], values[1])
|
78 |
+
|
79 |
+
# Plotting
|
80 |
+
fig = create_fig(data, ticker)
|
81 |
+
st.plotly_chart(fig, use_container_width=True)
|
82 |
+
st.warning(f"In the above graph, we use {some_warning_message}.")
|
83 |
+
else:
|
84 |
+
st.write("No data available for the given ticker.")
|
85 |
+
|
86 |
+
# New section to get and display fundamentals data under an expander
|
87 |
+
with st.expander("View Fundamentals Data"):
|
88 |
+
a, b, c = get_fundamentals(ticker)
|
89 |
+
fundamentals_data = pd.concat([a, b, c], axis=0)
|
90 |
+
fundamentals_data_t = fundamentals_data.transpose()
|
91 |
+
fundamentals_data_t["date"] = fundamentals_data_t.index
|
92 |
+
fundamentals_data_t.reset_index(drop=True, inplace=True)
|
93 |
+
|
94 |
+
if not fundamentals_data.empty:
|
95 |
+
# Generate a table (crude way)
|
96 |
+
# st.table(fundamentals_data)
|
97 |
+
|
98 |
+
# Generate the HTML using Pygwalker (user-friendly and flexible)
|
99 |
+
pyg_html = pyg.walk(fundamentals_data_t, return_html=True)
|
100 |
+
|
101 |
+
# Embed the HTML into the Streamlit app
|
102 |
+
components.html(pyg_html, height=1000, scrolling=True)
|
103 |
+
else:
|
104 |
+
st.write("No fundamentals data available for the given ticker.")
|