QuantumLearner commited on
Commit
5740aef
·
verified ·
1 Parent(s): 82f9802

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1363 -161
app.py CHANGED
@@ -1,177 +1,1379 @@
 
1
  import yfinance as yf
2
- import talib
3
  import pandas as pd
4
- import streamlit as st
5
- import plotly.graph_objects as go
 
 
 
 
 
 
6
 
7
- # Set page configuration
8
  st.set_page_config(layout="wide")
9
 
10
- # Define pattern functions
11
- pattern_funcs = [
12
- ("Two Crows", talib.CDL2CROWS),
13
- ("Three Black Crows", talib.CDL3BLACKCROWS),
14
- ("Three Inside Up/Down", talib.CDL3INSIDE),
15
- ("Three-Line Strike", talib.CDL3LINESTRIKE),
16
- ("Three Outside Up/Down", talib.CDL3OUTSIDE),
17
- ("Three Stars In The South", talib.CDL3STARSINSOUTH),
18
- ("Three Advancing White Soldiers", talib.CDL3WHITESOLDIERS),
19
- ("Abandoned Baby", talib.CDLABANDONEDBABY),
20
- ("Advance Block", talib.CDLADVANCEBLOCK),
21
- ("Belt-hold", talib.CDLBELTHOLD),
22
- ("Breakaway", talib.CDLBREAKAWAY),
23
- ("Closing Marubozu", talib.CDLCLOSINGMARUBOZU),
24
- ("Concealing Baby Swallow", talib.CDLCONCEALBABYSWALL),
25
- ("Counterattack", talib.CDLCOUNTERATTACK),
26
- ("Dark Cloud Cover", talib.CDLDARKCLOUDCOVER),
27
- ("Doji", talib.CDLDOJI),
28
- ("Doji Star", talib.CDLDOJISTAR),
29
- ("Dragonfly Doji", talib.CDLDRAGONFLYDOJI),
30
- ("Engulfing Pattern", talib.CDLENGULFING),
31
- ("Evening Doji Star", talib.CDLEVENINGDOJISTAR),
32
- ("Evening Star", talib.CDLEVENINGSTAR),
33
- ("Up/Down-gap side-by-side white lines", talib.CDLGAPSIDESIDEWHITE),
34
- ("Gravestone Doji", talib.CDLGRAVESTONEDOJI),
35
- ("Hammer", talib.CDLHAMMER),
36
- ("Hanging Man", talib.CDLHANGINGMAN),
37
- ("Harami Pattern", talib.CDLHARAMI),
38
- ("Harami Cross Pattern", talib.CDLHARAMICROSS),
39
- ("High-Wave Candle", talib.CDLHIGHWAVE),
40
- ("Hikkake Pattern", talib.CDLHIKKAKE),
41
- ("Modified Hikkake Pattern", talib.CDLHIKKAKEMOD),
42
- ("Homing Pigeon", talib.CDLHOMINGPIGEON),
43
- ("Identical Three Crows", talib.CDLIDENTICAL3CROWS),
44
- ("In-Neck Pattern", talib.CDLINNECK),
45
- ("Inverted Hammer", talib.CDLINVERTEDHAMMER),
46
- ("Kicking", talib.CDLKICKING),
47
- ("Kicking - bull/bear determined by the longer marubozu", talib.CDLKICKINGBYLENGTH),
48
- ("Ladder Bottom", talib.CDLLADDERBOTTOM),
49
- ("Long Legged Doji", talib.CDLLONGLEGGEDDOJI),
50
- ("Long Line Candle", talib.CDLLONGLINE),
51
- ("Marubozu", talib.CDLMARUBOZU),
52
- ("Matching Low", talib.CDLMATCHINGLOW),
53
- ("Mat Hold", talib.CDLMATHOLD),
54
- ("Morning Doji Star", talib.CDLMORNINGDOJISTAR),
55
- ("Morning Star", talib.CDLMORNINGSTAR),
56
- ("On-Neck Pattern", talib.CDLONNECK),
57
- ("Piercing Pattern", talib.CDLPIERCING),
58
- ("Rickshaw Man", talib.CDLRICKSHAWMAN),
59
- ("Rising/Falling Three Methods", talib.CDLRISEFALL3METHODS),
60
- ("Separating Lines", talib.CDLSEPARATINGLINES),
61
- ("Shooting Star", talib.CDLSHOOTINGSTAR),
62
- ("Short Line Candle", talib.CDLSHORTLINE),
63
- ("Spinning Top", talib.CDLSPINNINGTOP),
64
- ("Stalled Pattern", talib.CDLSTALLEDPATTERN),
65
- ("Stick Sandwich", talib.CDLSTICKSANDWICH),
66
- ("Takuri (Dragonfly Doji with very long lower shadow)", talib.CDLTAKURI),
67
- ("Tasuki Gap", talib.CDLTASUKIGAP),
68
- ("Thrusting Pattern", talib.CDLTHRUSTING),
69
- ("Tristar Pattern", talib.CDLTRISTAR),
70
- ("Unique 3 River", talib.CDLUNIQUE3RIVER),
71
- ("Upside Gap Two Crows", talib.CDLUPSIDEGAP2CROWS),
72
- ("Upside/Downside Gap Three Methods", talib.CDLXSIDEGAP3METHODS)
73
- ]
74
-
75
- # Streamlit app setup
76
- st.title('Automatic Candlestick Pattern Detection')
77
-
78
- st.write("""
79
- This tool automatically detects 60+ candlestick patterns in stock price data.
80
- * You can input the stock ticker (e.g. 'AAPL') or crypto pair (e.g. 'BTC-USD'), start date, and end date in the sidebar menu to analyze.
81
- * The tool will fetch the historical data and highlight detected patterns on the charts.
82
- * The charts display the stock price data along with vertical lines indicating where patterns were found.
83
- * If no patterns are detected, the chart for that pattern will not be included.
84
  """)
85
 
86
 
87
- st.sidebar.header('Input Parameters')
88
- symbol = st.sidebar.text_input('Enter Asset Symbol', 'AAPL')
89
- start_date = st.sidebar.date_input('Start Date', pd.to_datetime('2023-06-01'))
90
- end_date = st.sidebar.date_input('End Date', pd.to_datetime('2025-01-01'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- # Dynamic input for moving averages
93
- ma_short_period = st.sidebar.number_input('Short-term Moving Average Period', min_value=1, value=20)
94
- ma_long_period = st.sidebar.number_input('Long-term Moving Average Period', min_value=1, value=50)
 
 
 
 
 
 
 
 
95
 
96
- if st.sidebar.button('Run Analysis'):
97
- run_analysis = True
98
- else:
99
- run_analysis = False
 
 
 
 
 
 
 
 
 
 
100
 
101
- st.sidebar.header('Candlestick Patterns')
102
- selected_patterns = {name: st.sidebar.checkbox(name, value=True) for name, func in pattern_funcs}
 
 
 
 
 
103
 
104
- if run_analysis:
105
- data = yf.download(symbol, start=start_date, end=end_date)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
- if not data.empty:
108
- # Calculate moving averages based on user input
109
- data[f'MA{ma_short_period}'] = talib.SMA(data['Close'], timeperiod=ma_short_period)
110
- data[f'MA{ma_long_period}'] = talib.SMA(data['Close'], timeperiod=ma_long_period)
111
-
112
- for pattern_name, pattern_func in pattern_funcs:
113
- if selected_patterns[pattern_name]:
114
- data[pattern_name] = pattern_func(data['Open'], data['High'], data['Low'], data['Close'])
115
- pattern_dates = data[data[pattern_name] != 0].index
116
-
117
- if len(pattern_dates) == 0:
118
- continue
119
-
120
- fig = go.Figure()
121
-
122
- fig.add_trace(go.Candlestick(
123
- x=data.index,
124
- open=data['Open'],
125
- high=data['High'],
126
- low=data['Low'],
127
- close=data['Close'],
128
- name='Candlesticks',
129
- yaxis='y2'
130
- ))
131
-
132
- fig.add_trace(go.Scatter(
133
- x=data.index,
134
- y=data[f'MA{ma_short_period}'],
135
- mode='lines',
136
- line=dict(color='blue', width=1.5),
137
- name=f'{ma_short_period}-day MA',
138
- yaxis='y2'
139
- ))
140
-
141
- fig.add_trace(go.Scatter(
142
- x=data.index,
143
- y=data[f'MA{ma_long_period}'],
144
- mode='lines',
145
- line=dict(color='orange', width=1.5),
146
- name=f'{ma_long_period}-day MA',
147
- yaxis='y2'
148
- ))
149
-
150
- fig.add_trace(go.Bar(
151
- x=data.index,
152
- y=data['Volume'],
153
- name='Volume',
154
- yaxis='y',
155
- marker=dict(color='grey'),
156
- opacity=0.5
157
- ))
158
-
159
- for date in pattern_dates:
160
- fig.add_vline(x=date, line=dict(color='red', width=2, dash='dash'), name=pattern_name)
161
-
162
- fig.update_layout(
163
- title=f"{symbol} Stock Price and {pattern_name} Pattern Detection",
164
- xaxis_title="Date",
165
- yaxis=dict(title='Volume'),
166
- yaxis2=dict(title='Price', overlaying='y', side='right'),
167
- legend_title="Legend",
168
- xaxis_rangeslider_visible=False,
169
- template='plotly_white'
170
- )
171
-
172
- st.plotly_chart(fig)
173
- else:
174
- st.write("No data found for the given ticker and date range.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  hide_streamlit_style = """
177
  <style>
 
1
+ import streamlit as st
2
  import yfinance as yf
 
3
  import pandas as pd
4
+ import plotly.graph_objs as go
5
+ import numpy as np
6
+ import talib
7
+ from pandas import Series
8
+ from numpy import average as npAverage, nan as npNaN, log as npLog, power as npPower, sqrt as npSqrt, zeros_like as npZeroslike
9
+ from pandas_ta.utils import get_offset, verify_series
10
+ from datetime import datetime
11
+ from matplotlib.dates import date2num
12
 
 
13
  st.set_page_config(layout="wide")
14
 
15
+
16
+ st.title("Comprehensive Moving Averages Analysis Tool")
17
+
18
+ st.markdown("""
19
+ This app provides a detailed analysis of various moving averages. You can select from a wide range of techniques to better understand stock price movement trends.
20
+
21
+ ### Moving Averages Categories:
22
+
23
+ - **Fundamental Techniques**: Includes **SMA**, **EMA**, **WMA**, **DEMA**, **TEMA**, **VAMA**, **KAMA**, **TMA**, and **HMA**.
24
+
25
+ - **Adaptive and Dynamic Approaches**: Covers methods like **FRAMA**, **ZLEMA**, **VIDYA**, **ALMA**, **MAMA**, **Adaptive Period MA**, **Rainbow MA**, **Wilders MA**, and **SMMA**.
26
+
27
+ - **Advanced Weighting Techniques**: Features **GMMA**, **LSMA**, **Welch’s MMA**, **Sin-weighted MA**, **Median MA**, **Geometric MA**, **eVWMA**, **REMA**, and **Parabolic WMA**.
28
+
29
+ - **Specialized Methods**: Includes **JMA**, **EPMA**, **CMA**, **Harmonic MA**, **McGinley Dynamic**, **Anchored MA**, and **Filtered MA**.
30
+
31
+
32
+ For technical details on these methods, refer to [this article](https://entreprenerdly.com/top-36-moving-averages-methods-for-stock-prices-in-python/).
33
+ """)
34
+
35
+ st.sidebar.markdown("""
36
+ ### How to Use:
37
+ - **Ticker Symbol**: Enter the stock symbol (e.g., `AAPL`).
38
+ - **Date Range**: Select the start and end dates.
39
+ - **Fetch Data**: Click 'Fetch Data' to load the stock data.
40
+ - **Moving Averages**: Choose and customize your moving averages.
41
+ - **Run Analysis**: Click 'Run' to apply and visualize.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  """)
43
 
44
 
45
+ # Function to fetch data
46
+ @st.cache_data
47
+ def get_data(ticker, start_date, end_date):
48
+ data = yf.download(ticker, start=start_date, end=end_date)
49
+ return data
50
+
51
+ # Function to create the base plot with the stock price
52
+ def create_price_plot(data, ticker_symbol):
53
+ fig = go.Figure()
54
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name=f'{ticker_symbol} Stock Price'))
55
+ return fig
56
+
57
+ # Jurik Moving Average (JMA) Function
58
+ def jma(close, length=None, phase=None, offset=None, **kwargs):
59
+ _length = int(length) if length and length > 0 else 7
60
+ phase = float(phase) if phase and phase != 0 else 0
61
+ close = verify_series(close, _length)
62
+ offset = get_offset(offset)
63
+ if close is None: return
64
+
65
+ jma = npZeroslike(close)
66
+ volty = npZeroslike(close)
67
+ v_sum = npZeroslike(close)
68
+
69
+ kv = det0 = det1 = ma2 = 0.0
70
+ jma[0] = ma1 = uBand = lBand = close[0]
71
+
72
+ sum_length = 10
73
+ length = 0.5 * (_length - 1)
74
+ pr = 0.5 if phase < -100 else 2.5 if phase > 100 else 1.5 + phase * 0.01
75
+ length1 = max((npLog(npSqrt(length)) / npLog(2.0)) + 2.0, 0)
76
+ pow1 = max(length1 - 2.0, 0.5)
77
+ length2 = length1 * npSqrt(length)
78
+ bet = length2 / (length2 + 1)
79
+ beta = 0.45 * (_length - 1) / (0.45 * (_length - 1) + 2.0)
80
+
81
+ m = close.shape[0]
82
+ for i in range(1, m):
83
+ price = close[i]
84
+
85
+ del1 = price - uBand
86
+ del2 = price - lBand
87
+ volty[i] = max(abs(del1), abs(del2)) if abs(del1) != abs(del2) else 0
88
+
89
+ v_sum[i] = v_sum[i - 1] + (volty[i] - volty[max(i - sum_length, 0)]) / sum_length
90
+ avg_volty = npAverage(v_sum[max(i - 65, 0):i + 1])
91
+ d_volty = 0 if avg_volty == 0 else volty[i] / avg_volty
92
+ r_volty = max(1.0, min(npPower(length1, 1 / pow1), d_volty))
93
+
94
+ pow2 = npPower(r_volty, pow1)
95
+ kv = npPower(bet, npSqrt(pow2))
96
+ uBand = price if (del1 > 0) else price - (kv * del1)
97
+ lBand = price if (del2 < 0) else price - (kv * del2)
98
+
99
+ power = npPower(r_volty, pow1)
100
+ alpha = npPower(beta, power)
101
+
102
+ ma1 = ((1 - alpha) * price) + (alpha * ma1)
103
+
104
+ det0 = ((price - ma1) * (1 - beta)) + (beta * det0)
105
+ ma2 = ma1 + pr * det0
106
+
107
+ det1 = ((ma2 - jma[i - 1]) * (1 - alpha) * (1 - alpha)) + (alpha * alpha * det1)
108
+ jma[i] = jma[i - 1] + det1
109
+
110
+ jma[0:_length - 1] = npNaN
111
+ jma = Series(jma, index=close.index)
112
+
113
+ if offset != 0:
114
+ jma = jma.shift(offset)
115
+
116
+ if "fillna" in kwargs:
117
+ jma.fillna(kwargs["fillna"], inplace=True)
118
+ if "fill_method" in kwargs:
119
+ jma.fillna(method=kwargs["fill_method"], inplace=True)
120
+
121
+ jma.name = f"JMA_{_length}_{phase}"
122
+ jma.category = "overlap"
123
+
124
+ return jma
125
+
126
+ # Function to calculate Harmonic Moving Average (HMA)
127
+ def calculate_harmonic_moving_average(prices, period):
128
+ harmonic_ma = []
129
+ for i in range(len(prices)):
130
+ if i < period - 1:
131
+ harmonic_ma.append(np.nan)
132
+ else:
133
+ window = prices[i - period + 1: i + 1]
134
+ harmonic_mean = period / np.sum(1.0 / window)
135
+ harmonic_ma.append(harmonic_mean)
136
+ return harmonic_ma
137
+
138
+ # End Point Moving Average (EPMA) Function
139
+ def calculate_EPMA(prices, period):
140
+ epma_values = []
141
+
142
+ for i in range(period - 1, len(prices)):
143
+ x = np.arange(period)
144
+ y = prices[i-period+1:i+1]
145
+
146
+ slope, intercept = np.polyfit(x, y, 1)
147
+ epma = slope * (period - 1) + intercept
148
+
149
+ epma_values.append(epma)
150
+
151
+ return [None]*(period-1) + epma_values # Pad with None for alignment
152
+
153
+ # Chande Moving Average (CMA) Function
154
+ def calculate_CMA(prices):
155
+ cumsum = np.cumsum(prices)
156
+ cma = cumsum / (np.arange(len(prices)) + 1)
157
+ return cma
158
+
159
+ # Other Moving Average Methods
160
+ # Function to calculate Parabolic Weighted Moving Average (PWMA)
161
+ def parabolic_weighted_moving_average(prices, n=14):
162
+ weights = np.array([(n-i)**2 for i in range(n)])
163
+ return np.convolve(prices, weights/weights.sum(), mode='valid')
164
+
165
+ # Function to calculate Regularized Exponential Moving Average (REMA)
166
+ def REMA(prices, alpha=0.1, lambda_=0.1):
167
+ rema = [prices[0]]
168
+ penalty = 0
169
+
170
+ for t in range(1, len(prices)):
171
+ second_derivative = prices[t] - 2 * prices[t-1] + prices[t-2] if t-2 >= 0 else 0
172
+ penalty = lambda_ * second_derivative
173
+ current_rema = alpha * prices[t] + (1 - alpha) * rema[-1] - penalty
174
+ rema.append(current_rema)
175
+
176
+ return rema
177
+
178
+ # Function to calculate Weighted Moving Average (WMA)
179
+ def weighted_moving_average(data, periods):
180
+ weights = np.arange(1, periods + 1)
181
+ wma = data.rolling(periods).apply(lambda x: np.dot(x, weights) / weights.sum(), raw=True)
182
+ return wma
183
+
184
+ # Function to calculate Hull Moving Average (HMA)
185
+ def hull_moving_average(data, periods):
186
+ wma_half_period = weighted_moving_average(data, int(periods / 2))
187
+ wma_full_period = weighted_moving_average(data, periods)
188
+ hma = weighted_moving_average(2 * wma_half_period - wma_full_period, int(np.sqrt(periods)))
189
+ return hma
190
+
191
+ # Function to calculate Harmonic Moving Average (HMA) to avoid conflict with Hull
192
+ def harmonic_moving_average(data, period):
193
+ def harmonic_mean(prices):
194
+ return period / np.sum(1.0 / prices)
195
+
196
+ hma_values = []
197
+ for i in range(period - 1, len(data)):
198
+ hma_values.append(harmonic_mean(data[i - period + 1:i + 1]))
199
+
200
+ return [np.nan] * (period - 1) + hma_values
201
+
202
+ # Function to calculate Fractal Adaptive Moving Average (FRAMA)
203
+ def calculate_FRAMA(data, batch=10):
204
+ InputPrice = data['Close'].values
205
+ Length = len(InputPrice)
206
+ Filt = np.array(InputPrice)
207
+
208
+ for i in range(2 * batch, Length):
209
+ v1 = InputPrice[i-2*batch:i - batch]
210
+ v2 = InputPrice[i - batch:i]
211
+
212
+ H1 = np.max(v1)
213
+ L1 = np.min(v1)
214
+ N1 = (H1 - L1) / batch
215
+
216
+ H2 = np.max(v2)
217
+ L2 = np.min(v2)
218
+ N2 = (H2 - L2) / batch
219
+
220
+ H = np.max([H1, H2])
221
+ L = np.min([L1, L2])
222
+ N3 = (H - L) / (2 * batch)
223
+
224
+ Dimen = 0
225
+ if N1 > 0 and N2 > 0 and N3 > 0:
226
+ Dimen = (np.log(N1 + N2) - np.log(N3)) / np.log(2)
227
+
228
+ alpha = np.exp(-4.6 * Dimen - 1)
229
+ alpha = np.clip(alpha, 0.1, 1)
230
+
231
+ Filt[i] = alpha * InputPrice[i] + (1 - alpha) * Filt[i-1]
232
+
233
+ data['FRAMA'] = Filt
234
+ return data
235
+
236
+ # Function to calculate Exponential Moving Average (EMA)
237
+ def calculate_EMA(prices, period):
238
+ alpha = 2 / (period + 1)
239
+ EMA = [prices[0]]
240
+ for price in prices[1:]:
241
+ EMA.append((price - EMA[-1]) * alpha + EMA[-1])
242
+ return EMA
243
+
244
+ # Function to calculate Zero Lag Exponential Moving Average (ZLEMA)
245
+ def calculate_ZLEMA(prices, period):
246
+ lag = period // 2
247
+ adjusted_prices = [2 * prices[i] - (prices[i - lag] if i >= lag else prices[0]) for i in range(len(prices))]
248
+ ZLEMA = calculate_EMA(adjusted_prices, period)
249
+ return ZLEMA
250
+
251
+ # Function to calculate Chande Momentum Oscillator (CMO)
252
+ def calculate_CMO(prices, period):
253
+ deltas = np.diff(prices)
254
+ sum_gains = np.cumsum(np.where(deltas >= 0, deltas, 0))
255
+ sum_losses = np.abs(np.cumsum(np.where(deltas < 0, deltas, 0)))
256
+
257
+ cmo = 100 * (sum_gains - sum_losses) / (sum_gains + sum_losses)
258
+ return np.insert(cmo, 0, 0) # Add a zero at the beginning for alignment
259
+
260
+ # Function to calculate Variable Index Dynamic Average (VIDYA)
261
+ def calculate_VIDYA(prices, period):
262
+ cmo_values = calculate_CMO(prices, period)
263
+ vidya = [prices[0]]
264
+
265
+ for i in range(1, len(prices)):
266
+ alpha = abs(cmo_values[i]) / 100 # Normalize CMO to [0, 1]
267
+ vidya.append((1 - alpha) * vidya[-1] + alpha * prices[i])
268
+
269
+ return vidya
270
+
271
+ # Function to calculate Arnaud Legoux Moving Average (ALMA)
272
+ def calculate_ALMA(prices, period, offset=0.85, sigma=6):
273
+ m = np.floor(offset * (period - 1))
274
+ s = period / sigma
275
+ alma = []
276
+
277
+ for i in range(period - 1, len(prices)):
278
+ weights = [np.exp(- (j - m)**2 / (2 * s * s)) for j in range(period)]
279
+ sum_weights = sum(weights)
280
+ normalized_weights = [w/sum_weights for w in weights]
281
+
282
+ window = prices[i-period+1:i+1]
283
+ alma_value = sum([normalized_weights[j] * window[j] for j in range(period)])
284
+ alma.append(alma_value)
285
+
286
+ return [None]*(period-1) + alma # Pad the beginning with None for alignment
287
+
288
+ # Function to calculate Adaptive Period Moving Average (APMA)
289
+ def adaptive_period_moving_average(prices, min_period=5, max_period=30):
290
+ atr = np.zeros_like(prices)
291
+ adjusted_periods = np.zeros_like(prices)
292
+ moving_averages = np.full_like(prices, np.nan) # Initialize with NaN values
293
+
294
+ for i in range(1, len(prices)):
295
+ atr[i] = atr[i-1] + (abs(prices[i] - prices[i-1]) - atr[i-1]) / 14
296
+
297
+ min_volatility = atr[1:i+1].min()
298
+ max_volatility = atr[1:i+1].max()
299
+
300
+ if max_volatility == min_volatility:
301
+ adjusted_period = min_period
302
+ else:
303
+ adjusted_period = int(((max_period - min_period) / (max_volatility - min_volatility)) * (atr[i] - min_volatility) + min_period)
304
+
305
+ adjusted_periods[i] = adjusted_period
306
+
307
+ if i >= adjusted_period:
308
+ moving_averages[i] = np.mean(prices[i-adjusted_period+1:i+1])
309
+
310
+ return moving_averages
311
+
312
+ # Function to calculate Rainbow Moving Average (Rainbow EMA)
313
+ def calculate_rainbow_ema(data, lookback_periods):
314
+ for lookback in lookback_periods:
315
+ data[f'EMA{lookback}'] = data['Close'].ewm(span=lookback).mean()
316
+ return data
317
+
318
+ # Function to calculate Wilders Moving Average
319
+ def wilders_moving_average(prices, period):
320
+ wilder = [prices[0]]
321
+ for price in prices[1:]:
322
+ wilder_value = ((wilder[-1] * (period - 1)) + price) / period
323
+ wilder.append(wilder_value)
324
+ return wilder
325
+
326
+ # Function to calculate Smoothed Moving Average (SMMA)
327
+ def calculate_SMMA(prices, n):
328
+ SMMA = [np.nan] * (n-1) # Fill the initial n-1 values with NaN
329
+ SMMA.append(sum(prices[:n]) / n)
330
+ for i in range(n, len(prices)):
331
+ smma_value = (SMMA[-1] * (n - 1) + prices[i]) / n
332
+ SMMA.append(smma_value)
333
+ return SMMA
334
+
335
+ # Function to calculate Least Squares Moving Average (LSMA)
336
+ def calculate_LSMA(prices, period):
337
+ n = period
338
+ x = np.array(range(1, n+1))
339
+
340
+ LSMA = []
341
+ for i in range(len(prices) - period + 1):
342
+ y = prices[i:i+period]
343
+ m = (n*np.sum(x*y) - np.sum(x)*np.sum(y)) / (n*np.sum(x**2) - np.sum(x)**2)
344
+ c = (np.sum(y) - m*np.sum(x)) / n
345
+ LSMA.append(m * n + c) # The projected value at the end of the period
346
+
347
+ # Padding the beginning with NaNs for alignment
348
+ LSMA = [np.nan] * (period-1) + LSMA
349
+ return LSMA
350
+
351
+ # Function to calculate Welch's Moving Average (Modified Moving Average, MMA)
352
+ def calculate_MMA(prices, period):
353
+ MMA = [sum(prices[:period]) / period] # Start with the SMA for the first value
354
+ for t in range(period, len(prices)):
355
+ MMA.append((prices[t] + (period - 1) * MMA[-1]) / period)
356
+ return [None]*(period-1) + MMA # Pad the beginning with None for alignment
357
+
358
+ # Function to calculate Sin-weighted Moving Average (SinWMA)
359
+ def calculate_SinWMA(prices, period):
360
+ weights = [np.sin(np.pi * i / (period + 1)) for i in range(1, period+1)]
361
+ sum_weights = sum(weights)
362
+ normalized_weights = [w/sum_weights for w in weights]
363
+
364
+ SinWMA = []
365
+ for t in range(period - 1, len(prices)):
366
+ window = prices[t-period+1:t+1]
367
+ SinWMA.append(sum([normalized_weights[i] * window[i] for i in range(period)]))
368
+
369
+ return [None]*(period-1) + SinWMA # Pad the beginning with None for alignment
370
+
371
+ # Function to calculate Median Moving Average (MedMA)
372
+ def calculate_MedMA(prices, window):
373
+ medians = []
374
+ for i in range(len(prices)):
375
+ if i < window - 1:
376
+ medians.append(np.nan)
377
+ else:
378
+ median = np.median(prices[i - window + 1: i + 1])
379
+ medians.append(median)
380
+ return medians
381
 
382
+ # Function to calculate Geometric Moving Average (GMA)
383
+ def calculate_GMA(prices, window):
384
+ gm_avg = []
385
+ for i in range(len(prices)):
386
+ if i < window - 1:
387
+ gm_avg.append(np.nan)
388
+ else:
389
+ product = np.prod(prices[i - window + 1: i + 1])
390
+ gma_value = product ** (1/window)
391
+ gm_avg.append(gma_value)
392
+ return gm_avg
393
 
394
+ # Function to calculate Elastic Volume Weighted Moving Average (eVWMA)
395
+ def calculate_eVWMA(prices, volumes, window):
396
+ evwma_values = []
397
+ for i in range(len(prices)):
398
+ if i < window:
399
+ evwma_values.append(np.nan)
400
+ else:
401
+ price_subset = prices[i-window+1:i+1]
402
+ volume_subset = volumes[i-window+1:i+1]
403
+ numerator = sum([p * v for p, v in zip(price_subset, volume_subset)])
404
+ denominator = sum(volume_subset)
405
+ evwma = numerator / denominator
406
+ evwma_values.append(evwma)
407
+ return evwma_values
408
 
409
+ # Function to calculate McGinley Dynamic (MD)
410
+ def calculate_mcginley_dynamic(prices, n):
411
+ MD = [prices[0]]
412
+ for i in range(1, len(prices)):
413
+ md_value = MD[-1] + (prices[i] - MD[-1]) / (n * (prices[i] / MD[-1])**4)
414
+ MD.append(md_value)
415
+ return MD
416
 
417
+ # Function to calculate Anchored Moving Average (AMA)
418
+ from datetime import datetime
419
+
420
+ def calculate_AMA(prices, anchor_date, data):
421
+ # Ensure the anchor_date is a pandas Timestamp
422
+ anchor_date = pd.to_datetime(anchor_date)
423
+
424
+ try:
425
+ anchor_idx = data.index.get_loc(anchor_date)
426
+ except KeyError:
427
+ # If the exact date is not found, find the nearest available date
428
+ anchor_date = data.index[data.index.get_loc(anchor_date, method='nearest')]
429
+ anchor_idx = data.index.get_loc(anchor_date)
430
+
431
+ AMA = []
432
+
433
+ for i in range(len(prices)):
434
+ if i < anchor_idx:
435
+ AMA.append(None)
436
+ else:
437
+ AMA.append(sum(prices[anchor_idx:i+1]) / (i - anchor_idx + 1))
438
+
439
+ return AMA
440
+
441
+ # Function to calculate Filtered Moving Average (FMA)
442
+ def filtered_moving_average(prices, n=14):
443
+ # Define filter weights (for simplicity, we'll use equal weights similar to SMA)
444
+ w = np.ones(n) / n
445
+ return np.convolve(prices, w, mode='valid')
446
+
447
+ # Sidebar for user inputs
448
+ st.sidebar.header("Select Parameters")
449
+
450
+ # Ticker input with tooltip
451
+ ticker_symbol = st.sidebar.text_input(
452
+ "Ticker Symbol",
453
+ value=st.session_state.get('ticker_symbol', 'AAPL'),
454
+ help="Enter the stock ticker symbol (e.g., AAPL for Apple)."
455
+ )
456
+
457
+ # Date range inputs with tooltip
458
+ start_date = st.sidebar.date_input(
459
+ "Start Date",
460
+ value=st.session_state.get('start_date', pd.to_datetime("2020-01-01")),
461
+ help="Select the start date for fetching the stock data."
462
+ )
463
+ end_date = st.sidebar.date_input(
464
+ "End Date",
465
+ value=st.session_state.get('end_date', pd.to_datetime("2024-01-01")),
466
+ help="Select the end date for fetching the stock data."
467
+ )
468
+
469
+ # Fetch Data button
470
+ if st.sidebar.button('Fetch Data'):
471
+ # Only fetch if the ticker or date range has changed
472
+ if (
473
+ ticker_symbol != st.session_state.get('ticker_symbol') or
474
+ start_date != st.session_state.get('start_date') or
475
+ end_date != st.session_state.get('end_date')
476
+ ):
477
+ data = get_data(ticker_symbol, start_date, end_date)
478
+ st.session_state['data'] = data
479
+ st.session_state['ticker_symbol'] = ticker_symbol
480
+ st.session_state['start_date'] = start_date
481
+ st.session_state['end_date'] = end_date
482
+ st.session_state['price_plot'] = create_price_plot(data, ticker_symbol)
483
+ st.session_state['current_fig'] = st.session_state['price_plot']
484
+
485
+ # Check if data is fetched and stored in session state
486
+ if 'data' in st.session_state:
487
+ data = st.session_state['data']
488
+
489
+ # Moving average method selection
490
+ st.sidebar.header("Moving Average Methods")
491
+
492
+ # SMA with tooltip
493
+ use_sma = st.sidebar.checkbox(
494
+ 'Simple Moving Average (SMA)',
495
+ value=st.session_state.get('use_sma', False),
496
+ help="Select to apply Simple Moving Average (SMA) to the stock price."
497
+ )
498
+ sma_period = st.sidebar.number_input(
499
+ 'SMA Period',
500
+ min_value=1,
501
+ value=st.session_state.get('sma_period', 50),
502
+ step=1,
503
+ disabled=not use_sma,
504
+ help="Specify the period (in days) for the SMA."
505
+ )
506
+
507
+ # EMA with tooltip
508
+ use_ema = st.sidebar.checkbox(
509
+ 'Exponential Moving Average (EMA)',
510
+ value=st.session_state.get('use_ema', False),
511
+ help="Select to apply Exponential Moving Average (EMA) to the stock price."
512
+ )
513
+ ema_period = st.sidebar.number_input(
514
+ 'EMA Period',
515
+ min_value=1,
516
+ value=st.session_state.get('ema_period', 50),
517
+ step=1,
518
+ disabled=not use_ema,
519
+ help="Specify the period (in days) for the EMA."
520
+ )
521
+
522
+ # WMA with tooltip
523
+ use_wma = st.sidebar.checkbox(
524
+ 'Weighted Moving Average (WMA)',
525
+ value=st.session_state.get('use_wma', False),
526
+ help="Select to apply Weighted Moving Average (WMA) to the stock price."
527
+ )
528
+ wma_period = st.sidebar.number_input(
529
+ 'WMA Period',
530
+ min_value=1,
531
+ value=st.session_state.get('wma_period', 50),
532
+ step=1,
533
+ disabled=not use_wma,
534
+ help="Specify the period (in days) for the WMA."
535
+ )
536
+
537
+ # DEMA with tooltip
538
+ use_dema = st.sidebar.checkbox(
539
+ 'Double Exponential Moving Average (DEMA)',
540
+ value=st.session_state.get('use_dema', False),
541
+ help="Select to apply Double Exponential Moving Average (DEMA) to the stock price."
542
+ )
543
+ dema_period = st.sidebar.number_input(
544
+ 'DEMA Period',
545
+ min_value=1,
546
+ value=st.session_state.get('dema_period', 50),
547
+ step=1,
548
+ disabled=not use_dema,
549
+ help="Specify the period (in days) for the DEMA."
550
+ )
551
+
552
+ # TEMA with tooltip
553
+ use_tema = st.sidebar.checkbox(
554
+ 'Triple Exponential Moving Average (TEMA)',
555
+ value=st.session_state.get('use_tema', False),
556
+ help="Select to apply Triple Exponential Moving Average (TEMA) to the stock price."
557
+ )
558
+ tema_period = st.sidebar.number_input(
559
+ 'TEMA Period',
560
+ min_value=1,
561
+ value=st.session_state.get('tema_period', 50),
562
+ step=1,
563
+ disabled=not use_tema,
564
+ help="Specify the period (in days) for the TEMA."
565
+ )
566
+
567
+ # VAMA with tooltip
568
+ use_vama = st.sidebar.checkbox(
569
+ 'Volume-Adjusted Moving Average (VAMA)',
570
+ value=st.session_state.get('use_vama', False),
571
+ help="Select to apply Volume-Adjusted Moving Average (VAMA) to the stock price."
572
+ )
573
+ vama_period = st.sidebar.number_input(
574
+ 'VAMA Period',
575
+ min_value=1,
576
+ value=st.session_state.get('vama_period', 50),
577
+ step=1,
578
+ disabled=not use_vama,
579
+ help="Specify the period (in days) for the VAMA."
580
+ )
581
+
582
+ # KAMA with tooltip
583
+ use_kama = st.sidebar.checkbox(
584
+ 'Kaufman Adaptive Moving Average (KAMA)',
585
+ value=st.session_state.get('use_kama', False),
586
+ help="Select to apply Kaufman Adaptive Moving Average (KAMA) to the stock price."
587
+ )
588
+ kama_period = st.sidebar.number_input(
589
+ 'KAMA Period',
590
+ min_value=1,
591
+ value=st.session_state.get('kama_period', 10),
592
+ step=1,
593
+ disabled=not use_kama,
594
+ help="Specify the efficiency ratio period (in days) for the KAMA."
595
+ )
596
+ fastest_period = st.sidebar.number_input(
597
+ 'Fastest SC Period',
598
+ min_value=1,
599
+ value=st.session_state.get('fastest_period', 2),
600
+ step=1,
601
+ disabled=not use_kama,
602
+ help="Specify the fastest smoothing constant period."
603
+ )
604
+ slowest_period = st.sidebar.number_input(
605
+ 'Slowest SC Period',
606
+ min_value=1,
607
+ value=st.session_state.get('slowest_period', 30),
608
+ step=1,
609
+ disabled=not use_kama,
610
+ help="Specify the slowest smoothing constant period."
611
+ )
612
+
613
+ # TMA with tooltip
614
+ use_tma = st.sidebar.checkbox(
615
+ 'Triangular Moving Average (TMA)',
616
+ value=st.session_state.get('use_tma', False),
617
+ help="Select to apply Triangular Moving Average (TMA) to the stock price."
618
+ )
619
+ tma_period = st.sidebar.number_input(
620
+ 'TMA Period',
621
+ min_value=1,
622
+ value=st.session_state.get('tma_period', 20),
623
+ step=1,
624
+ disabled=not use_tma,
625
+ help="Specify the period (in days) for the TMA."
626
+ )
627
+
628
+ # Hull MA with tooltip
629
+ use_hull_ma = st.sidebar.checkbox(
630
+ 'Hull Moving Average (HMA)',
631
+ value=st.session_state.get('use_hull_ma', False),
632
+ help="Select to apply Hull Moving Average (HMA) to the stock price."
633
+ )
634
+ hull_ma_period = st.sidebar.number_input(
635
+ 'HMA Period',
636
+ min_value=1,
637
+ value=st.session_state.get('hull_ma_period', 120),
638
+ step=1,
639
+ disabled=not use_hull_ma,
640
+ help="Specify the period (in days) for the Hull Moving Average."
641
+ )
642
+
643
+ # Harmonic MA with tooltip
644
+ use_harmonic_ma = st.sidebar.checkbox(
645
+ 'Harmonic Moving Average (HMA)',
646
+ value=st.session_state.get('use_harmonic_ma', False),
647
+ help="Select to apply Harmonic Moving Average (HMA) to the stock price."
648
+ )
649
+ harmonic_ma_period = st.sidebar.number_input(
650
+ 'HMA Period',
651
+ min_value=1,
652
+ value=st.session_state.get('harmonic_ma_period', 120),
653
+ step=1,
654
+ disabled=not use_harmonic_ma,
655
+ help="Specify the period (in days) for the Harmonic Moving Average."
656
+ )
657
+
658
+ # FRAMA with tooltip
659
+ use_frama = st.sidebar.checkbox(
660
+ 'Fractal Adaptive Moving Average (FRAMA)',
661
+ value=st.session_state.get('use_frama', False),
662
+ help="Select to apply Fractal Adaptive Moving Average (FRAMA) to the stock price."
663
+ )
664
+ frama_batch = st.sidebar.number_input(
665
+ 'FRAMA Batch Size',
666
+ min_value=1,
667
+ value=st.session_state.get('frama_batch', 10),
668
+ step=1,
669
+ disabled=not use_frama,
670
+ help="Specify the batch size for FRAMA calculation."
671
+ )
672
+
673
+ # ZLEMA with tooltip
674
+ use_zlema = st.sidebar.checkbox(
675
+ 'Zero Lag Exponential Moving Average (ZLEMA)',
676
+ value=st.session_state.get('use_zlema', False),
677
+ help="Select to apply Zero Lag Exponential Moving Average (ZLEMA) to the stock price."
678
+ )
679
+ zlema_period = st.sidebar.number_input(
680
+ 'ZLEMA Period',
681
+ min_value=1,
682
+ value=st.session_state.get('zlema_period', 28),
683
+ step=1,
684
+ disabled=not use_zlema,
685
+ help="Specify the period (in days) for the ZLEMA."
686
+ )
687
+
688
+ # VIDYA with tooltip
689
+ use_vidya = st.sidebar.checkbox(
690
+ 'Variable Index Dynamic Average (VIDYA)',
691
+ value=st.session_state.get('use_vidya', False),
692
+ help="Select to apply Variable Index Dynamic Average (VIDYA) to the stock price."
693
+ )
694
+ vidya_period = st.sidebar.number_input(
695
+ 'VIDYA Period',
696
+ min_value=1,
697
+ value=st.session_state.get('vidya_period', 14),
698
+ step=1,
699
+ disabled=not use_vidya,
700
+ help="Specify the period (in days) for the VIDYA."
701
+ )
702
+
703
+ # ALMA with tooltip
704
+ use_alma = st.sidebar.checkbox(
705
+ 'Arnaud Legoux Moving Average (ALMA)',
706
+ value=st.session_state.get('use_alma', False),
707
+ help="Select to apply Arnaud Legoux Moving Average (ALMA) to the stock price."
708
+ )
709
+ alma_period = st.sidebar.number_input(
710
+ 'ALMA Period',
711
+ min_value=1,
712
+ value=st.session_state.get('alma_period', 36),
713
+ step=1,
714
+ disabled=not use_alma,
715
+ help="Specify the period (in days) for the ALMA."
716
+ )
717
+ alma_offset = st.sidebar.number_input(
718
+ 'ALMA Offset',
719
+ min_value=0.0,
720
+ max_value=1.0,
721
+ value=st.session_state.get('alma_offset', 0.85),
722
+ step=0.01,
723
+ disabled=not use_alma,
724
+ help="Specify the offset for the ALMA (0 to 1)."
725
+ )
726
+ alma_sigma = st.sidebar.number_input(
727
+ 'ALMA Sigma',
728
+ min_value=1,
729
+ value=st.session_state.get('alma_sigma', 6),
730
+ step=1,
731
+ disabled=not use_alma,
732
+ help="Specify the sigma for the ALMA."
733
+ )
734
+
735
+ # MAMA and FAMA with tooltip
736
+ use_mama_fama = st.sidebar.checkbox(
737
+ 'MESA Adaptive Moving Average (MAMA) & FAMA',
738
+ value=st.session_state.get('use_mama_fama', False),
739
+ help="Select to apply MESA Adaptive Moving Average (MAMA) and Following Adaptive Moving Average (FAMA) to the stock price."
740
+ )
741
+ mama_fast_limit = st.sidebar.number_input(
742
+ 'MAMA Fast Limit',
743
+ min_value=0.0,
744
+ max_value=1.0,
745
+ value=st.session_state.get('mama_fast_limit', 0.5),
746
+ step=0.01,
747
+ disabled=not use_mama_fama,
748
+ help="Specify the fast limit for MAMA (0 to 1)."
749
+ )
750
+ mama_slow_limit = st.sidebar.number_input(
751
+ 'MAMA Slow Limit',
752
+ min_value=0.0,
753
+ max_value=1.0,
754
+ value=st.session_state.get('mama_slow_limit', 0.05),
755
+ step=0.01,
756
+ disabled=not use_mama_fama,
757
+ help="Specify the slow limit for MAMA (0 to 1)."
758
+ )
759
+
760
+ # APMA with tooltip
761
+ use_apma = st.sidebar.checkbox(
762
+ 'Adaptive Period Moving Average (APMA)',
763
+ value=st.session_state.get('use_apma', False),
764
+ help="Select to apply Adaptive Period Moving Average (APMA) to the stock price."
765
+ )
766
+ apma_min_period = st.sidebar.number_input(
767
+ 'APMA Min Period',
768
+ min_value=1,
769
+ value=st.session_state.get('apma_min_period', 5),
770
+ step=1,
771
+ disabled=not use_apma,
772
+ help="Specify the minimum period for the APMA."
773
+ )
774
+ apma_max_period = st.sidebar.number_input(
775
+ 'APMA Max Period',
776
+ min_value=1,
777
+ value=st.session_state.get('apma_max_period', 30),
778
+ step=1,
779
+ disabled=not use_apma,
780
+ help="Specify the maximum period for the APMA."
781
+ )
782
+
783
+ # Rainbow EMA with tooltip
784
+ use_rainbow_ema = st.sidebar.checkbox(
785
+ 'Rainbow Moving Average (EMA)',
786
+ value=st.session_state.get('use_rainbow_ema', False),
787
+ help="Select to apply Rainbow Moving Average (EMA) with multiple lookback periods to the stock price."
788
+ )
789
+ rainbow_lookback_periods = st.sidebar.multiselect(
790
+ 'Rainbow Lookback Periods',
791
+ options=[2, 4, 8, 16, 32, 64, 128, 192, 320, 512],
792
+ default=st.session_state.get('rainbow_lookback_periods', [2, 4, 8, 16, 32, 64, 128]),
793
+ disabled=not use_rainbow_ema,
794
+ help="Select multiple lookback periods for the Rainbow EMA."
795
+ )
796
+
797
+ # Wilders MA with tooltip
798
+ use_wilders_ma = st.sidebar.checkbox(
799
+ 'Wilders Moving Average (Wilder\'s MA)',
800
+ value=st.session_state.get('use_wilders_ma', False),
801
+ help="Select to apply Wilder's Moving Average to the stock price."
802
+ )
803
+ wilders_ma_period = st.sidebar.number_input(
804
+ 'Wilders MA Period',
805
+ min_value=1,
806
+ value=st.session_state.get('wilders_ma_period', 14),
807
+ step=1,
808
+ disabled=not use_wilders_ma,
809
+ help="Specify the period (in days) for Wilder's Moving Average."
810
+ )
811
+
812
+ # SMMA with tooltip
813
+ use_smma = st.sidebar.checkbox(
814
+ 'Smoothed Moving Average (SMMA)',
815
+ value=st.session_state.get('use_smma', False),
816
+ help="Select to apply Smoothed Moving Average (SMMA) to the stock price."
817
+ )
818
+ smma_period = st.sidebar.number_input(
819
+ 'SMMA Period',
820
+ min_value=1,
821
+ value=st.session_state.get('smma_period', 28),
822
+ step=1,
823
+ disabled=not use_smma,
824
+ help="Specify the period (in days) for the SMMA."
825
+ )
826
+
827
+ # GMMA with tooltip
828
+ use_gmma = st.sidebar.checkbox(
829
+ 'Guppy Multiple Moving Average (GMMA)',
830
+ value=st.session_state.get('use_gmma', False),
831
+ help="Select to apply Guppy Multiple Moving Average (GMMA) to the stock price."
832
+ )
833
+ gmma_short_periods = st.sidebar.multiselect(
834
+ 'GMMA Short Periods',
835
+ options=[3, 5, 8, 10, 12, 15],
836
+ default=st.session_state.get('gmma_short_periods', [3, 5, 8, 10, 12, 15]),
837
+ disabled=not use_gmma,
838
+ help="Select the short-term periods for GMMA."
839
+ )
840
+ gmma_long_periods = st.sidebar.multiselect(
841
+ 'GMMA Long Periods',
842
+ options=[30, 35, 40, 45, 50, 60],
843
+ default=st.session_state.get('gmma_long_periods', [30, 35, 40, 45, 50, 60]),
844
+ disabled=not use_gmma,
845
+ help="Select the long-term periods for GMMA."
846
+ )
847
+
848
+ # LSMA with tooltip
849
+ use_lsma = st.sidebar.checkbox(
850
+ 'Least Squares Moving Average (LSMA)',
851
+ value=st.session_state.get('use_lsma', False),
852
+ help="Select to apply Least Squares Moving Average (LSMA) to the stock price."
853
+ )
854
+ lsma_period = st.sidebar.number_input(
855
+ 'LSMA Period',
856
+ min_value=1,
857
+ value=st.session_state.get('lsma_period', 28),
858
+ step=1,
859
+ disabled=not use_lsma,
860
+ help="Specify the period (in days) for the LSMA."
861
+ )
862
+
863
+ # MMA (Welch's MMA) with tooltip
864
+ use_mma = st.sidebar.checkbox(
865
+ 'Welch\'s Moving Average (MMA)',
866
+ value=st.session_state.get('use_mma', False),
867
+ help="Select to apply Welch\'s Moving Average (Modified Moving Average) to the stock price."
868
+ )
869
+ mma_period = st.sidebar.number_input(
870
+ 'MMA Period',
871
+ min_value=1,
872
+ value=st.session_state.get('mma_period', 14),
873
+ step=1,
874
+ disabled=not use_mma,
875
+ help="Specify the period (in days) for the MMA."
876
+ )
877
+
878
+ # SinWMA with tooltip
879
+ use_sinwma = st.sidebar.checkbox(
880
+ 'Sin-weighted Moving Average (SinWMA)',
881
+ value=st.session_state.get('use_sinwma', False),
882
+ help="Select to apply Sin-weighted Moving Average (SinWMA) to the stock price."
883
+ )
884
+ sinwma_period = st.sidebar.number_input(
885
+ 'SinWMA Period',
886
+ min_value=1,
887
+ value=st.session_state.get('sinwma_period', 21),
888
+ step=1,
889
+ disabled=not use_sinwma,
890
+ help="Specify the period (in days) for the SinWMA."
891
+ )
892
+
893
+ # MedMA with tooltip
894
+ use_medma = st.sidebar.checkbox(
895
+ 'Median Moving Average (MedMA)',
896
+ value=st.session_state.get('use_medma', False),
897
+ help="Select to apply Median Moving Average (MedMA) to the stock price."
898
+ )
899
+ medma_period = st.sidebar.number_input(
900
+ 'MedMA Period',
901
+ min_value=1,
902
+ value=st.session_state.get('medma_period', 20),
903
+ step=1,
904
+ disabled=not use_medma,
905
+ help="Specify the period (in days) for the MedMA."
906
+ )
907
+
908
+ # GMA with tooltip
909
+ use_gma = st.sidebar.checkbox(
910
+ 'Geometric Moving Average (GMA)',
911
+ value=st.session_state.get('use_gma', False),
912
+ help="Select to apply Geometric Moving Average (GMA) to the stock price."
913
+ )
914
+ gma_period = st.sidebar.number_input(
915
+ 'GMA Period',
916
+ min_value=1,
917
+ value=st.session_state.get('gma_period', 20),
918
+ step=1,
919
+ disabled=not use_gma,
920
+ help="Specify the period (in days) for the GMA."
921
+ )
922
+
923
+ # eVWMA with tooltip
924
+ use_evwma = st.sidebar.checkbox(
925
+ 'Elastic Volume Weighted Moving Average (eVWMA)',
926
+ value=st.session_state.get('use_evwma', False),
927
+ help="Select to apply Elastic Volume Weighted Moving Average (eVWMA) to the stock price."
928
+ )
929
+ evwma_period = st.sidebar.number_input(
930
+ 'eVWMA Period',
931
+ min_value=1,
932
+ value=st.session_state.get('evwma_period', 20),
933
+ step=1,
934
+ disabled=not use_evwma,
935
+ help="Specify the period (in days) for the eVWMA."
936
+ )
937
+
938
+ # REMA with tooltip
939
+ use_rema = st.sidebar.checkbox(
940
+ 'Regularized Exponential Moving Average (REMA)',
941
+ value=st.session_state.get('use_rema', False),
942
+ help="Select to apply Regularized Exponential Moving Average (REMA) to the stock price."
943
+ )
944
+ rema_alpha = st.sidebar.number_input(
945
+ 'REMA Alpha',
946
+ min_value=0.0,
947
+ max_value=1.0,
948
+ value=st.session_state.get('rema_alpha', 0.1),
949
+ step=0.01,
950
+ disabled=not use_rema,
951
+ help="Specify the alpha value for the REMA (0 to 1)."
952
+ )
953
+ rema_lambda = st.sidebar.number_input(
954
+ 'REMA Lambda',
955
+ min_value=0.0,
956
+ max_value=1.0,
957
+ value=st.session_state.get('rema_lambda', 0.1),
958
+ step=0.01,
959
+ disabled=not use_rema,
960
+ help="Specify the lambda value for the REMA (0 to 1)."
961
+ )
962
+
963
+ # PWMA with tooltip
964
+ use_pwma = st.sidebar.checkbox(
965
+ 'Parabolic Weighted Moving Average (PWMA)',
966
+ value=st.session_state.get('use_pwma', False),
967
+ help="Select to apply Parabolic Weighted Moving Average (PWMA) to the stock price."
968
+ )
969
+ pwma_period = st.sidebar.number_input(
970
+ 'PWMA Period',
971
+ min_value=1,
972
+ value=st.session_state.get('pwma_period', 14),
973
+ step=1,
974
+ disabled=not use_pwma,
975
+ help="Specify the period (in days) for the PWMA."
976
+ )
977
+
978
+ # JMA with tooltip
979
+ use_jma = st.sidebar.checkbox(
980
+ 'Jurik Moving Average (JMA)',
981
+ value=st.session_state.get('use_jma', False),
982
+ help="Select to apply Jurik Moving Average (JMA) to the stock price."
983
+ )
984
+ jma_period = st.sidebar.number_input(
985
+ 'JMA Period',
986
+ min_value=1,
987
+ value=st.session_state.get('jma_period', 28),
988
+ step=1,
989
+ disabled=not use_jma,
990
+ help="Specify the period (in days) for the JMA."
991
+ )
992
+ jma_phase = st.sidebar.number_input(
993
+ 'JMA Phase',
994
+ min_value=-100.0,
995
+ max_value=100.0,
996
+ value=st.session_state.get('jma_phase', 0.0),
997
+ step=0.1,
998
+ disabled=not use_jma,
999
+ help="Specify the phase for the JMA (-100 to 100)."
1000
+ )
1001
+
1002
+ # EPMA with tooltip
1003
+ use_epma = st.sidebar.checkbox(
1004
+ 'End Point Moving Average (EPMA)',
1005
+ value=st.session_state.get('use_epma', False),
1006
+ help="Select to apply End Point Moving Average (EPMA) to the stock price."
1007
+ )
1008
+ epma_period = st.sidebar.number_input(
1009
+ 'EPMA Period',
1010
+ min_value=1,
1011
+ value=st.session_state.get('epma_period', 28),
1012
+ step=1,
1013
+ disabled=not use_epma,
1014
+ help="Specify the period (in days) for the EPMA."
1015
+ )
1016
+
1017
+ # CMA with tooltip
1018
+ use_cma = st.sidebar.checkbox(
1019
+ 'Chande Moving Average (CMA)',
1020
+ value=st.session_state.get('use_cma', False),
1021
+ help="Select to apply Chande Moving Average (CMA) to the stock price."
1022
+ )
1023
+ cma_period = len(data['Close'])
1024
+
1025
+ # McGinley Dynamic with tooltip
1026
+ use_mcginley_dynamic = st.sidebar.checkbox(
1027
+ 'McGinley Dynamic',
1028
+ value=st.session_state.get('use_mcginley_dynamic', False),
1029
+ help="Select to apply McGinley Dynamic to the stock price."
1030
+ )
1031
+ mcginley_dynamic_period = st.sidebar.number_input(
1032
+ 'McGinley Dynamic Period',
1033
+ min_value=1,
1034
+ value=st.session_state.get('mcginley_dynamic_period', 14),
1035
+ step=1,
1036
+ disabled=not use_mcginley_dynamic,
1037
+ help="Specify the period (in days) for the McGinley Dynamic."
1038
+ )
1039
+
1040
+ # Anchored Moving Average (AMA) with tooltip
1041
+ use_ama = st.sidebar.checkbox(
1042
+ 'Anchored Moving Average (AMA)',
1043
+ value=st.session_state.get('use_ama', False),
1044
+ help="Select to apply Anchored Moving Average (AMA) to the stock price."
1045
+ )
1046
+ ama_anchor_date = st.sidebar.date_input(
1047
+ 'AMA Anchor Date',
1048
+ value=pd.to_datetime(st.session_state.get('ama_anchor_date', '2021-01-01')),
1049
+ disabled=not use_ama,
1050
+ help="Select the anchor date for the AMA."
1051
+ )
1052
+
1053
+ # Filtered Moving Average (FMA) with tooltip
1054
+ use_fma = st.sidebar.checkbox(
1055
+ 'Filtered Moving Average (FMA)',
1056
+ value=st.session_state.get('use_fma', False),
1057
+ help="Select to apply Filtered Moving Average (FMA) to the stock price."
1058
+ )
1059
+ fma_period = st.sidebar.number_input(
1060
+ 'FMA Period',
1061
+ min_value=1,
1062
+ value=st.session_state.get('fma_period', 14),
1063
+ step=1,
1064
+ disabled=not use_fma,
1065
+ help="Specify the period (in days) for the FMA."
1066
+ )
1067
+
1068
+ # Grid toggle with tooltip
1069
+ show_grid = st.sidebar.checkbox(
1070
+ "Show Grid",
1071
+ value=True,
1072
+ help="Toggle to show or hide the grid on the plot."
1073
+ )
1074
 
1075
+ # Run button to apply moving averages
1076
+ if st.sidebar.button('Run'):
1077
+ # Save the moving average settings to session state
1078
+ st.session_state['use_sma'] = use_sma
1079
+ st.session_state['sma_period'] = sma_period
1080
+ st.session_state['use_ema'] = use_ema
1081
+ st.session_state['ema_period'] = ema_period
1082
+ st.session_state['use_wma'] = use_wma
1083
+ st.session_state['wma_period'] = wma_period
1084
+ st.session_state['use_dema'] = use_dema
1085
+ st.session_state['dema_period'] = dema_period
1086
+ st.session_state['use_tema'] = use_tema
1087
+ st.session_state['tema_period'] = tema_period
1088
+ st.session_state['use_vama'] = use_vama
1089
+ st.session_state['vama_period'] = vama_period
1090
+ st.session_state['use_kama'] = use_kama
1091
+ st.session_state['kama_period'] = kama_period
1092
+ st.session_state['fastest_period'] = fastest_period
1093
+ st.session_state['slowest_period'] = slowest_period
1094
+ st.session_state['use_tma'] = use_tma
1095
+ st.session_state['tma_period'] = tma_period
1096
+ st.session_state['use_hull_ma'] = use_hull_ma
1097
+ st.session_state['hull_ma_period'] = hull_ma_period
1098
+ st.session_state['use_harmonic_ma'] = use_harmonic_ma
1099
+ st.session_state['harmonic_ma_period'] = harmonic_ma_period
1100
+ st.session_state['use_frama'] = use_frama
1101
+ st.session_state['frama_batch'] = frama_batch
1102
+ st.session_state['use_zlema'] = use_zlema
1103
+ st.session_state['zlema_period'] = zlema_period
1104
+ st.session_state['use_vidya'] = use_vidya
1105
+ st.session_state['vidya_period'] = vidya_period
1106
+ st.session_state['use_alma'] = use_alma
1107
+ st.session_state['alma_period'] = alma_period
1108
+ st.session_state['alma_offset'] = alma_offset
1109
+ st.session_state['alma_sigma'] = alma_sigma
1110
+ st.session_state['use_mama_fama'] = use_mama_fama
1111
+ st.session_state['mama_fast_limit'] = mama_fast_limit
1112
+ st.session_state['mama_slow_limit'] = mama_slow_limit
1113
+ st.session_state['use_apma'] = use_apma
1114
+ st.session_state['apma_min_period'] = apma_min_period
1115
+ st.session_state['apma_max_period'] = apma_max_period
1116
+ st.session_state['use_rainbow_ema'] = use_rainbow_ema
1117
+ st.session_state['rainbow_lookback_periods'] = rainbow_lookback_periods
1118
+ st.session_state['use_wilders_ma'] = use_wilders_ma
1119
+ st.session_state['wilders_ma_period'] = wilders_ma_period
1120
+ st.session_state['use_smma'] = use_smma
1121
+ st.session_state['smma_period'] = smma_period
1122
+ st.session_state['use_gmma'] = use_gmma
1123
+ st.session_state['gmma_short_periods'] = gmma_short_periods
1124
+ st.session_state['gmma_long_periods'] = gmma_long_periods
1125
+ st.session_state['use_lsma'] = use_lsma
1126
+ st.session_state['lsma_period'] = lsma_period
1127
+ st.session_state['use_mma'] = use_mma
1128
+ st.session_state['mma_period'] = mma_period
1129
+ st.session_state['use_sinwma'] = use_sinwma
1130
+ st.session_state['sinwma_period'] = sinwma_period
1131
+ st.session_state['use_medma'] = use_medma
1132
+ st.session_state['medma_period'] = medma_period
1133
+ st.session_state['use_gma'] = use_gma
1134
+ st.session_state['gma_period'] = gma_period
1135
+ st.session_state['use_evwma'] = use_evwma
1136
+ st.session_state['evwma_period'] = evwma_period
1137
+ st.session_state['use_rema'] = use_rema
1138
+ st.session_state['rema_alpha'] = rema_alpha
1139
+ st.session_state['rema_lambda'] = rema_lambda
1140
+ st.session_state['use_pwma'] = use_pwma
1141
+ st.session_state['pwma_period'] = pwma_period
1142
+ st.session_state['use_jma'] = use_jma
1143
+ st.session_state['jma_period'] = jma_period
1144
+ st.session_state['jma_phase'] = jma_phase
1145
+ st.session_state['use_epma'] = use_epma
1146
+ st.session_state['epma_period'] = epma_period
1147
+ st.session_state['use_cma'] = use_cma
1148
+ st.session_state['use_mcginley_dynamic'] = use_mcginley_dynamic
1149
+ st.session_state['mcginley_dynamic_period'] = mcginley_dynamic_period
1150
+ st.session_state['use_ama'] = use_ama
1151
+ st.session_state['ama_anchor_date'] = ama_anchor_date
1152
+ st.session_state['use_fma'] = use_fma
1153
+ st.session_state['fma_period'] = fma_period
1154
+ # (Save all previous moving average settings here)
1155
+
1156
+ # Start with the base price plot
1157
+ fig = go.Figure(data=st.session_state['price_plot'].data)
1158
+
1159
+ # Add JMA if selected
1160
+ if use_jma:
1161
+ st.session_state['JMA'] = jma(data['Close'], length=jma_period, phase=jma_phase)
1162
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['JMA'], mode='lines', name=f'JMA (n={jma_period}, phase={jma_phase})', line=dict(dash='dash', color='green')))
1163
+
1164
+ # Add EPMA if selected
1165
+ if use_epma:
1166
+ st.session_state['EPMA'] = calculate_EPMA(data['Close'].tolist(), epma_period)
1167
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['EPMA'], mode='lines', name=f'EPMA (n={epma_period})', line=dict(dash='dash', color='blue')))
1168
+
1169
+ # Add CMA if selected
1170
+ if use_cma:
1171
+ st.session_state['CMA'] = calculate_CMA(data['Close'])
1172
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['CMA'], mode='lines', name=f'CMA', line=dict(dash='dash', color='blue')))
1173
+
1174
+ # Add McGinley Dynamic if selected
1175
+ if use_mcginley_dynamic:
1176
+ st.session_state['McGinley_Dynamic'] = calculate_mcginley_dynamic(data['Close'].tolist(), mcginley_dynamic_period)
1177
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['McGinley_Dynamic'], mode='lines', name=f'McGinley Dynamic (n={mcginley_dynamic_period})', line=dict(dash='dash', color='orange')))
1178
+
1179
+ # Add AMA if selected
1180
+ if use_ama:
1181
+ st.session_state['AMA'] = calculate_AMA(data['Close'].tolist(), ama_anchor_date, data)
1182
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['AMA'], mode='lines', name=f'Anchored MA (Anchor Date={ama_anchor_date})', line=dict(dash='dash', color='red')))
1183
+ fig.add_shape(type="line", x0=ama_anchor_date, y0=data['Close'].min(), x1=ama_anchor_date, y1=data['Close'].max(), line=dict(color="blue", width=2, dash="dash"))
1184
+
1185
+ # Add FMA if selected
1186
+ if use_fma:
1187
+ st.session_state['FMA'] = filtered_moving_average(data['Close'].values, fma_period)
1188
+ fig.add_trace(go.Scatter(x=data.index, y=np.concatenate([np.array([np.nan]*(fma_period-1)), st.session_state['FMA']]), mode='lines', name=f'Filtered MA (n={fma_period})', line=dict(dash='dash', color='green')))
1189
+
1190
+ # Add SMA if selected
1191
+ if use_sma:
1192
+ st.session_state['SMA'] = data['Close'].rolling(window=sma_period).mean()
1193
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['SMA'], mode='lines', name=f'{sma_period}-Day SMA', line=dict(dash='dash')))
1194
+
1195
+ # Add EMA if selected
1196
+ if use_ema:
1197
+ st.session_state['EMA'] = data['Close'].ewm(span=ema_period, adjust=False).mean()
1198
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['EMA'], mode='lines', name=f'{ema_period}-Day EMA', line=dict(dash='dash', color='green')))
1199
+
1200
+ # Add WMA if selected
1201
+ if use_wma:
1202
+ weights = np.arange(1, wma_period + 1)
1203
+ st.session_state['WMA'] = data['Close'].rolling(window=wma_period).apply(lambda prices: np.dot(prices, weights)/weights.sum(), raw=True)
1204
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['WMA'], mode='lines', name=f'{wma_period}-Day WMA', line=dict(dash='dash', color='orange')))
1205
+
1206
+ # Add DEMA if selected
1207
+ if use_dema:
1208
+ data['EMA'] = data['Close'].ewm(span=dema_period, adjust=False).mean()
1209
+ data['EMA2'] = data['EMA'].ewm(span=dema_period, adjust=False).mean()
1210
+ st.session_state['DEMA'] = 2 * data['EMA'] - data['EMA2']
1211
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['DEMA'], mode='lines', name=f'{dema_period}-Day DEMA', line=dict(dash='dash', color='red')))
1212
+
1213
+ # Add TEMA if selected
1214
+ if use_tema:
1215
+ data['EMA'] = data['Close'].ewm(span=tema_period, adjust=False).mean()
1216
+ data['EMA2'] = data['EMA'].ewm(span=tema_period, adjust=False).mean()
1217
+ data['EMA3'] = data['EMA2'].ewm(span=tema_period, adjust=False).mean()
1218
+ st.session_state['TEMA'] = 3 * data['EMA'] - 3 * data['EMA2'] + data['EMA3']
1219
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['TEMA'], mode='lines', name=f'{tema_period}-Day TEMA', line=dict(dash='dash', color='purple')))
1220
+
1221
+ # Add VAMA if selected
1222
+ if use_vama:
1223
+ data['Volume_Price'] = data['Close'] * data['Volume']
1224
+ st.session_state['VAMA'] = data['Volume_Price'].rolling(window=vama_period).sum() / data['Volume'].rolling(window=vama_period).sum()
1225
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['VAMA'], mode='lines', name=f'{vama_period}-Day VAMA', line=dict(dash='dash', color='orange')))
1226
+
1227
+ # Add KAMA if selected
1228
+ if use_kama:
1229
+ fastest_SC = 2 / (fastest_period + 1)
1230
+ slowest_SC = 2 / (slowest_period + 1)
1231
+ data['Change'] = abs(data['Close'] - data['Close'].shift(kama_period))
1232
+ data['Volatility'] = data['Close'].diff().abs().rolling(window=kama_period).sum()
1233
+ data['ER'] = data['Change'] / data['Volatility']
1234
+ data['SC'] = (data['ER'] * (fastest_SC - slowest_SC) + slowest_SC)**2
1235
+ data['KAMA'] = data['Close'].copy()
1236
+ for i in range(kama_period, len(data)):
1237
+ data['KAMA'].iloc[i] = data['KAMA'].iloc[i-1] + data['SC'].iloc[i] * (data['Close'].iloc[i] - data['KAMA'].iloc[i-1])
1238
+ st.session_state['KAMA'] = data['KAMA']
1239
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['KAMA'], mode='lines', name=f'KAMA (n={kama_period})', line=dict(dash='dash', color='green')))
1240
+
1241
+ # Add TMA if selected
1242
+ if use_tma:
1243
+ half_n = (tma_period + 1) // 2
1244
+ data['Half_SMA'] = data['Close'].rolling(window=half_n).mean()
1245
+ st.session_state['TMA'] = data['Half_SMA'].rolling(window=half_n).mean()
1246
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['TMA'], mode='lines', name=f'TMA (n={tma_period})', line=dict(dash='dash', color='red')))
1247
+
1248
+ # Add Hull MA if selected
1249
+ if use_hull_ma:
1250
+ st.session_state['Hull_MA'] = hull_moving_average(data['Close'], hull_ma_period)
1251
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['Hull_MA'], mode='lines', name=f'Hull MA (n={hull_ma_period})', line=dict(dash='dash', color='green')))
1252
+
1253
+ # Add Harmonic MA if selected
1254
+ if use_harmonic_ma:
1255
+ st.session_state['Harmonic_MA'] = calculate_harmonic_moving_average(data['Close'].values, harmonic_ma_period)
1256
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['Harmonic_MA'], mode='lines', name=f'Harmonic MA (n={harmonic_ma_period})', line=dict(dash='dash', color='purple')))
1257
+
1258
+ # Add FRAMA if selected
1259
+ if use_frama:
1260
+ st.session_state['FRAMA'] = calculate_FRAMA(data, batch=frama_batch)['FRAMA']
1261
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['FRAMA'], mode='lines', name=f'FRAMA (batch={frama_batch})', line=dict(dash='dash', color='green')))
1262
+
1263
+ # Add ZLEMA if selected
1264
+ if use_zlema:
1265
+ st.session_state['ZLEMA'] = calculate_ZLEMA(data['Close'].tolist(), zlema_period)
1266
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['ZLEMA'], mode='lines', name=f'ZLEMA (n={zlema_period})', line=dict(dash='dash', color='red')))
1267
+
1268
+ # Add VIDYA if selected
1269
+ if use_vidya:
1270
+ st.session_state['VIDYA'] = calculate_VIDYA(data['Close'].tolist(), vidya_period)
1271
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['VIDYA'], mode='lines', name=f'VIDYA (n={vidya_period})', line=dict(dash='dash', color='blue')))
1272
+
1273
+ # Add ALMA if selected
1274
+ if use_alma:
1275
+ st.session_state['ALMA'] = calculate_ALMA(data['Close'].tolist(), alma_period, offset=alma_offset, sigma=alma_sigma)
1276
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['ALMA'], mode='lines', name=f'ALMA (n={alma_period})', line=dict(dash='dash', color='purple')))
1277
+
1278
+ # Add MAMA and FAMA if selected
1279
+ if use_mama_fama:
1280
+ data['MAMA'], data['FAMA'] = talib.MAMA(data['Close'].values, fastlimit=mama_fast_limit, slowlimit=mama_slow_limit)
1281
+ st.session_state['MAMA'] = data['MAMA']
1282
+ st.session_state['FAMA'] = data['FAMA']
1283
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['MAMA'], mode='lines', name=f'MAMA', line=dict(dash='dash', color='blue')))
1284
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['FAMA'], mode='lines', name=f'FAMA', line=dict(dash='dash', color='red')))
1285
+
1286
+ # Add APMA if selected
1287
+ if use_apma:
1288
+ st.session_state['APMA'] = adaptive_period_moving_average(data['Close'].values, min_period=apma_min_period, max_period=apma_max_period)
1289
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['APMA'], mode='lines', name=f'APMA (min={apma_min_period}, max={apma_max_period})', line=dict(dash='dash', color='red')))
1290
+
1291
+ # Add Rainbow EMA if selected
1292
+ if use_rainbow_ema:
1293
+ data = calculate_rainbow_ema(data, rainbow_lookback_periods)
1294
+ colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet', 'black','gray','brown']
1295
+ for i, lookback in enumerate(rainbow_lookback_periods):
1296
+ fig.add_trace(go.Scatter(x=data.index, y=data[f'EMA{lookback}'], mode='lines', name=f'EMA {lookback}', line=dict(dash='solid', color=colors[i % len(colors)])))
1297
+
1298
+ # Add Wilders MA if selected
1299
+ if use_wilders_ma:
1300
+ st.session_state['Wilders_MA'] = wilders_moving_average(data['Close'].tolist(), wilders_ma_period)
1301
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['Wilders_MA'], mode='lines', name=f'Wilders MA (n={wilders_ma_period})', line=dict(dash='dash', color='red')))
1302
+
1303
+ # Add SMMA if selected
1304
+ if use_smma:
1305
+ st.session_state['SMMA'] = calculate_SMMA(data['Close'].tolist(), smma_period)
1306
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['SMMA'], mode='lines', name=f'SMMA (n={smma_period})', line=dict(dash='dash', color='red')))
1307
+
1308
+ # Add GMMA if selected
1309
+ if use_gmma:
1310
+ close_prices = data['Close'].tolist()
1311
+ for period in gmma_short_periods:
1312
+ data[f'EMA_{period}'] = calculate_EMA(close_prices, period)
1313
+ fig.add_trace(go.Scatter(x=data.index, y=data[f'EMA_{period}'], mode='lines', name=f'GMMA Short EMA {period}', line=dict(dash='solid')))
1314
+ for period in gmma_long_periods:
1315
+ data[f'EMA_{period}'] = calculate_EMA(close_prices, period)
1316
+ fig.add_trace(go.Scatter(x=data.index, y=data[f'EMA_{period}'], mode='lines', name=f'GMMA Long EMA {period}', line=dict(dash='dash')))
1317
+
1318
+ # Add LSMA if selected
1319
+ if use_lsma:
1320
+ st.session_state['LSMA'] = calculate_LSMA(data['Close'].tolist(), lsma_period)
1321
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['LSMA'], mode='lines', name=f'LSMA (n={lsma_period})', line=dict(dash='dash', color='blue')))
1322
+
1323
+ # Add MMA (Welch's MMA) if selected
1324
+ if use_mma:
1325
+ st.session_state['MMA'] = calculate_MMA(data['Close'].tolist(), mma_period)
1326
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['MMA'], mode='lines', name=f'MMA (n={mma_period})', line=dict(dash='dash', color='blue')))
1327
+
1328
+ # Add SinWMA if selected
1329
+ if use_sinwma:
1330
+ st.session_state['SinWMA'] = calculate_SinWMA(data['Close'].tolist(), sinwma_period)
1331
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['SinWMA'], mode='lines', name=f'SinWMA (n={sinwma_period})', line=dict(dash='dash', color='green')))
1332
+
1333
+ # Add MedMA if selected
1334
+ if use_medma:
1335
+ st.session_state['MedMA'] = calculate_MedMA(data['Close'].tolist(), medma_period)
1336
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['MedMA'], mode='lines', name=f'MedMA (n={medma_period})', line=dict(dash='dash', color='blue')))
1337
+
1338
+ # Add GMA if selected
1339
+ if use_gma:
1340
+ st.session_state['GMA'] = calculate_GMA(data['Close'].tolist(), gma_period)
1341
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['GMA'], mode='lines', name=f'GMA (n={gma_period})', line=dict(dash='dash', color='green')))
1342
+
1343
+ # Add eVWMA if selected
1344
+ if use_evwma:
1345
+ st.session_state['eVWMA'] = calculate_eVWMA(data['Close'], data['Volume'], evwma_period)
1346
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['eVWMA'], mode='lines', name=f'eVWMA (n={evwma_period})', line=dict(dash='dash', color='blue')))
1347
+
1348
+ # Add REMA if selected
1349
+ if use_rema:
1350
+ st.session_state['REMA'] = REMA(data['Close'], alpha=rema_alpha, lambda_=rema_lambda)
1351
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['REMA'], mode='lines', name=f'REMA (alpha={rema_alpha}, lambda={rema_lambda})', line=dict(dash='dash', color='red')))
1352
+
1353
+ # Add PWMA if selected
1354
+ if use_pwma:
1355
+ pwma_values = parabolic_weighted_moving_average(data['Close'].values, pwma_period)
1356
+ st.session_state['PWMA'] = np.concatenate([np.array([np.nan]*(pwma_period-1)), pwma_values])
1357
+ fig.add_trace(go.Scatter(x=data.index, y=st.session_state['PWMA'], mode='lines', name=f'PWMA (n={pwma_period})', line=dict(dash='dash', color='red')))
1358
+
1359
+
1360
+ # Update layout with grid toggle
1361
+ fig.update_layout(
1362
+ title=f'{ticker_symbol} Stock Price with Moving Averages',
1363
+ xaxis_title='Date',
1364
+ yaxis_title='Stock Price',
1365
+ legend_title='Indicators',
1366
+ template='plotly_white',
1367
+ xaxis=dict(showgrid=show_grid),
1368
+ yaxis=dict(showgrid=show_grid)
1369
+ )
1370
+
1371
+ # Store the updated figure in session state
1372
+ st.session_state['current_fig'] = fig
1373
+
1374
+ # Display the current figure, which remains unchanged until "Run" is clicked
1375
+ if 'current_fig' in st.session_state:
1376
+ st.plotly_chart(st.session_state['current_fig'], use_container_width=True)
1377
 
1378
  hide_streamlit_style = """
1379
  <style>