File size: 8,801 Bytes
6892254
 
 
 
4e7b07f
6892254
1d08af5
 
 
6892254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cd6bc6e
1d08af5
 
8062c88
4e7b07f
 
8062c88
4e7b07f
1d08af5
6892254
8062c88
 
 
 
 
1d08af5
8062c88
 
 
 
6892254
0edd5a6
 
 
 
 
 
8062c88
 
 
4e7b07f
d0d1f85
3d8dc02
 
 
 
 
6892254
3d8dc02
4e7b07f
 
 
6892254
1d08af5
3d8dc02
1d08af5
3d8dc02
1d08af5
 
3d8dc02
1d08af5
 
3d8dc02
4e7b07f
 
3d8dc02
4e7b07f
 
 
 
 
 
 
 
 
 
3d8dc02
4e7b07f
 
 
 
 
 
 
 
 
3d8dc02
4e7b07f
 
 
 
 
 
 
 
 
3d8dc02
4e7b07f
 
 
 
 
 
 
 
1d08af5
3d8dc02
1d08af5
4e7b07f
 
3d8dc02
4e7b07f
8062c88
4e7b07f
 
 
 
 
3d8dc02
 
4e7b07f
1d08af5
3d8dc02
6892254
3d8dc02
1d08af5
0edd5a6
1d08af5
 
 
 
 
 
3d8dc02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import yfinance as yf
import talib
import pandas as pd
import streamlit as st
import plotly.graph_objects as go

# Set page configuration
st.set_page_config(layout="wide")

# Define pattern functions
pattern_funcs = [
    ("Two Crows", talib.CDL2CROWS),
    ("Three Black Crows", talib.CDL3BLACKCROWS),
    ("Three Inside Up/Down", talib.CDL3INSIDE),
    ("Three-Line Strike", talib.CDL3LINESTRIKE),
    ("Three Outside Up/Down", talib.CDL3OUTSIDE),
    ("Three Stars In The South", talib.CDL3STARSINSOUTH),
    ("Three Advancing White Soldiers", talib.CDL3WHITESOLDIERS),
    ("Abandoned Baby", talib.CDLABANDONEDBABY),
    ("Advance Block", talib.CDLADVANCEBLOCK),
    ("Belt-hold", talib.CDLBELTHOLD),
    ("Breakaway", talib.CDLBREAKAWAY),
    ("Closing Marubozu", talib.CDLCLOSINGMARUBOZU),
    ("Concealing Baby Swallow", talib.CDLCONCEALBABYSWALL),
    ("Counterattack", talib.CDLCOUNTERATTACK),
    ("Dark Cloud Cover", talib.CDLDARKCLOUDCOVER),
    ("Doji", talib.CDLDOJI),
    ("Doji Star", talib.CDLDOJISTAR),
    ("Dragonfly Doji", talib.CDLDRAGONFLYDOJI),
    ("Engulfing Pattern", talib.CDLENGULFING),
    ("Evening Doji Star", talib.CDLEVENINGDOJISTAR),
    ("Evening Star", talib.CDLEVENINGSTAR),
    ("Up/Down-gap side-by-side white lines", talib.CDLGAPSIDESIDEWHITE),
    ("Gravestone Doji", talib.CDLGRAVESTONEDOJI),
    ("Hammer", talib.CDLHAMMER),
    ("Hanging Man", talib.CDLHANGINGMAN),
    ("Harami Pattern", talib.CDLHARAMI),
    ("Harami Cross Pattern", talib.CDLHARAMICROSS),
    ("High-Wave Candle", talib.CDLHIGHWAVE),
    ("Hikkake Pattern", talib.CDLHIKKAKE),
    ("Modified Hikkake Pattern", talib.CDLHIKKAKEMOD),
    ("Homing Pigeon", talib.CDLHOMINGPIGEON),
    ("Identical Three Crows", talib.CDLIDENTICAL3CROWS),
    ("In-Neck Pattern", talib.CDLINNECK),
    ("Inverted Hammer", talib.CDLINVERTEDHAMMER),
    ("Kicking", talib.CDLKICKING),
    ("Kicking - bull/bear determined by the longer marubozu", talib.CDLKICKINGBYLENGTH),
    ("Ladder Bottom", talib.CDLLADDERBOTTOM),
    ("Long Legged Doji", talib.CDLLONGLEGGEDDOJI),
    ("Long Line Candle", talib.CDLLONGLINE),
    ("Marubozu", talib.CDLMARUBOZU),
    ("Matching Low", talib.CDLMATCHINGLOW),
    ("Mat Hold", talib.CDLMATHOLD),
    ("Morning Doji Star", talib.CDLMORNINGDOJISTAR),
    ("Morning Star", talib.CDLMORNINGSTAR),
    ("On-Neck Pattern", talib.CDLONNECK),
    ("Piercing Pattern", talib.CDLPIERCING),
    ("Rickshaw Man", talib.CDLRICKSHAWMAN),
    ("Rising/Falling Three Methods", talib.CDLRISEFALL3METHODS),
    ("Separating Lines", talib.CDLSEPARATINGLINES),
    ("Shooting Star", talib.CDLSHOOTINGSTAR),
    ("Short Line Candle", talib.CDLSHORTLINE),
    ("Spinning Top", talib.CDLSPINNINGTOP),
    ("Stalled Pattern", talib.CDLSTALLEDPATTERN),
    ("Stick Sandwich", talib.CDLSTICKSANDWICH),
    ("Takuri (Dragonfly Doji with very long lower shadow)", talib.CDLTAKURI),
    ("Tasuki Gap", talib.CDLTASUKIGAP),
    ("Thrusting Pattern", talib.CDLTHRUSTING),
    ("Tristar Pattern", talib.CDLTRISTAR),
    ("Unique 3 River", talib.CDLUNIQUE3RIVER),
    ("Upside Gap Two Crows", talib.CDLUPSIDEGAP2CROWS),
    ("Upside/Downside Gap Three Methods", talib.CDLXSIDEGAP3METHODS)
]

# Streamlit app setup
st.title('Automatic Candlestick Pattern Detection')

st.write("""
This tool automatically detects 60+ candlestick patterns in stock or crypto price data. 
* 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.
* The tool will fetch the historical data and highlight detected patterns on the charts.
* The charts display the asset price data along with vertical lines indicating where patterns were found.
* If no patterns are detected, the chart for that pattern will not be included.
""")

# Sidebar for input parameters
with st.sidebar.expander("Input Parameters", expanded=True):
    symbol = st.text_input('Enter Asset Symbol', 'BTC-USD', help="Input the ticker symbol for the stock or crypto pair.")
    start_date = st.date_input('Start Date', pd.to_datetime('2023-06-01'), help="Select the start date for the analysis.")
    end_date = st.date_input('End Date', pd.to_datetime(pd.Timestamp.now().date() + pd.Timedelta(days=1)), help="Select the end date for the analysis.")

# Moving averages inside an expander, closed by default
with st.sidebar.expander("Moving Average Parameters", expanded=False):
    ma_short_period = st.number_input('Short-term Moving Average Period', min_value=1, value=20, help="Set the period for the short-term moving average.")
    ma_long_period = st.number_input('Long-term Moving Average Period', min_value=1, value=50, help="Set the period for the long-term moving average.")

# Place the Run Analysis button above the candlestick pattern checkboxes
if st.sidebar.button('Run Analysis'):
    run_analysis = True
else:
    run_analysis = False

# Candlestick patterns selection inside an expander, open by default
with st.sidebar.expander("Candlestick Patterns", expanded=True):
    selected_patterns = {name: st.checkbox(name, value=True) for name, func in pattern_funcs}

if run_analysis:
    # Fetch data with yfinance adjustments
    data = yf.download(symbol, start=start_date, end=end_date, auto_adjust=False)
    if isinstance(data.columns, pd.MultiIndex):
        data.columns = data.columns.get_level_values(0)

    if not data.empty:
        # Calculate moving averages based on user input with NaN handling
        data[f'MA{ma_short_period}'] = talib.SMA(data['Close'], timeperiod=ma_short_period)
        data[f'MA{ma_long_period}'] = talib.SMA(data['Close'], timeperiod=ma_long_period)
        
        for pattern_name, pattern_func in pattern_funcs:
            if selected_patterns[pattern_name]:
                # Calculate pattern with TALib and handle potential NaNs
                data[pattern_name] = pattern_func(data['Open'], data['High'], data['Low'], data['Close'])
                pattern_dates = data[data[pattern_name].notna() & (data[pattern_name] != 0)].index

                if len(pattern_dates) == 0:
                    st.write(f"No {pattern_name} patterns detected for {symbol} in the selected date range.")
                    continue

                # Create Plotly figure
                fig = go.Figure()
                
                # Candlestick chart
                fig.add_trace(go.Candlestick(
                    x=data.index,
                    open=data['Open'],
                    high=data['High'],
                    low=data['Low'],
                    close=data['Close'],
                    name='Candlesticks',
                    yaxis='y2'
                ))

                # Short-term moving average
                fig.add_trace(go.Scatter(
                    x=data.index,
                    y=data[f'MA{ma_short_period}'],
                    mode='lines',
                    line=dict(color='blue', width=1.5),
                    name=f'{ma_short_period}-day MA',
                    yaxis='y2'
                ))

                # Long-term moving average
                fig.add_trace(go.Scatter(
                    x=data.index,
                    y=data[f'MA{ma_long_period}'],
                    mode='lines',
                    line=dict(color='orange', width=1.5),
                    name=f'{ma_long_period}-day MA',
                    yaxis='y2'
                ))

                # Volume bars
                fig.add_trace(go.Bar(
                    x=data.index,
                    y=data['Volume'],
                    name='Volume',
                    yaxis='y',
                    marker=dict(color='grey'),
                    opacity=0.5
                ))

                # Add vertical lines for pattern detection
                for date in pattern_dates:
                    fig.add_vline(x=date, line=dict(color='red', width=2, dash='dash'), name=pattern_name)

                # Update layout
                fig.update_layout(
                    title=f"{symbol} Price and {pattern_name} Pattern Detection",
                    xaxis_title="Date",
                    yaxis=dict(title='Volume'),
                    yaxis2=dict(title='Price', overlaying='y', side='right'),
                    legend_title="Legend",
                    xaxis_rangeslider_visible=False,
                    template='plotly_white',
                    height=600
                )
                
                st.plotly_chart(fig, use_container_width=True)
    else:
        st.error(f"No data found for {symbol} in the given date range ({start_date} to {end_date}).")

# Hide the Streamlit style
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)