def visualize_dvd_df(df_dvd): # Div/ATR histogram fig = px.histogram(df_dvd, x='div/ATR', color='ticker', title='div/ATR Histogram') show_plotly(fig) # Days vs Div/ATR scatter if 'Days-to-ex' in df_dvd.columns: fig = px.scatter(df_dvd, x='Days-to-ex', y='div/ATR', color='ticker', title=f'Days-to-ex vs div/ATR') #, hover_data = ['']) show_plotly(fig) if 'TR_ex' in df_dvd.columns: max_Div_delta = int(max([df_dvd['TR_ex'].max(), df_dvd['Div'].max()])) fig = px.scatter( df_dvd, x='TR_ex', y='Div', color='ticker', title=f'Div vs True Range (on ex-date)') #, hover_data = ['']) fig.add_trace(go.Scatter(x=[0, max_Div_delta], y=[0, max_Div_delta], name='break-even', marker_symbol='x-thin', marker_size=1, line={ 'dash': 'solid', 'color': 'GhostWhite', 'width': 0.5 }), row=1, col=1) show_plotly(fig) if 'capture_r_multiplier' in df_dvd.columns: df_p = df_dvd[df_dvd['capture_risk'] > 0] fig = px.histogram(df_p, x='capture_r_multiplier', color='ticker', title='Dividend Capture R-Multiplier Distribution') show_plotly(fig)
def compare_returns(df_returns, df_prices, chart_size=500): ''' plot multi stocks returns in ST using PX ''' l_tickers = df_returns.columns.tolist() l_col, r_col = st.columns(2) # returns fig = px.line(df_returns, y=df_returns.columns.tolist(), title=f'stock returns') show_plotly(fig, height=chart_size, st_asset=l_col) # return dist fig = px.histogram(df_returns, y=l_tickers, opacity=0.8, title=f'returns distribution') show_plotly(fig, height=chart_size, st_asset=r_col) # cumulative returns fig = px.line( (df_returns + 1).cumprod(), y=df_returns.columns.tolist(), title=f'growtht of $1 invested on {df_returns.index[0]}', labels={'value': f'cumulative returns'}, # color_discrete_sequence = ["#b58900"] ) show_plotly(fig, height=chart_size, st_asset=l_col) # compare volume for t in l_tickers: df_prices['Dollar_Traded', t] = df_prices['Volume', t] * df_prices['Adj Close', t] fig = px.line( df_prices['Dollar_Traded'], y=l_tickers, title=f'Dollar Traded', labels={'value': f'Dollar Traded'}, # color_discrete_sequence = ["#b58900"] ) show_plotly(fig, height=chart_size, st_asset=r_col)
def plot_returns(df_returns, df_prices, target_ticker, chart_size=500, show_ohlc=True): ''' plot target_ticker returns in ST using PX ''' # TODO: remove show_ohlc arg l_col, r_col = st.columns(2) # returns fig = px.line( df_returns, y=target_ticker, title=f'{target_ticker} returns', color_discrete_sequence=["#b58900"], ) show_plotly(fig, height=chart_size, st_asset=l_col) # simple price chart # if show_ohlc: df_plot = pd.DataFrame({ 'Open': df_prices['Open'][target_ticker], 'High': df_prices['High'][target_ticker], 'Low': df_prices['Low'][target_ticker], 'Close': df_prices['Close'][target_ticker], 'Volume': df_prices['Volume'][target_ticker], 'Date': df_prices.index }) # fig = plotly_ohlc_chart(df_plot, vol_col = 'Volume', date_col = 'Date', # show_legend = False, show_volume_profile = False) fig = plotly_ohlc_chart(df_plot, date_col='Date', show_legend=False, show_volume_profile=False, show_range_slider=False) # else: # fig = px.line(df_prices['Adj Close'], y = target_ticker, # color_discrete_sequence = ["#b58900"]) show_plotly(fig, height=chart_size, title=f'price of {target_ticker}', st_asset=l_col) # return dist fig = px.histogram( df_returns, y=target_ticker, color_discrete_sequence=["#b58900"], title=f'{target_ticker} returns distribution (with 2stdv lines)') two_stdv = df_returns[target_ticker].mean( ) + df_returns[target_ticker].std() * 2 plotly_hist_draw_hline(fig, l_value_format=[{ 'value': v } for v in [two_stdv, -two_stdv]]) show_plotly(fig, height=chart_size, st_asset=r_col) # if show_ohlc: # #cumulative returns # fig = px.line((df_returns+1).cumprod(), y = target_ticker, # title = f'growtht of $1 invested in {target_ticker} on {df_returns.index[0]}', # labels = {target_ticker: f'{target_ticker} cumulative returns'}, # color_discrete_sequence = ["#b58900"] # ) # show_plotly(fig, height = chart_size) # else: # volume at price # https://medium.com/swlh/how-to-analyze-volume-profiles-with-python-3166bb10ff24 idx = pd.IndexSlice df_vp = df_prices.loc[:, idx[['Adj Close', 'Volume'], target_ticker]] df_vp.columns = ['Adj Close', 'Volume'] fig = px.histogram(df_vp, y='Adj Close', x='Volume', title=f'Volume-at-price: {target_ticker}', orientation='h', nbins=100, color_discrete_sequence=["#b58900"]) show_plotly(fig, height=chart_size, st_asset=r_col)
def visualize_features(df_dict, chart_configs, atr_period, start_date, use_ema=True): ''' Args: df_dict: dictionary of {ticker: df, ...} ''' l_tickers = list(df_dict.keys()) is_single = len(l_tickers) == 1 # Add KER df_dict = { t: add_KER(df, atr_period)[df.index > pd.Timestamp(start_date)] for t, df in df_dict.items() } norm_atr = st.checkbox('normalize ATR', value=True) if is_single: # Special Handling for Single Ticker tickers = None df_dict[l_tickers[0]]['ATR'] = df_dict[l_tickers[0]]['ATR']/ df_dict[l_tickers[0]]['Close'] \ if norm_atr else df_dict[l_tickers[0]]['ATR'] atr_periods = st.text_input( 'more ATR periods to test (comma separated)') atr_periods = [int(i) for i in atr_periods.split(',') ] if atr_periods else None if atr_periods: for p in atr_periods: df = df_dict[l_tickers[0]] df_dict[l_tickers[0]] = add_ATR(df, period=p, normalize=norm_atr, use_ema=use_ema, channel_dict=None, col_name=f'ATR_{p}') # MA of ATR # str_ma_period = st.text_input('moving averages period (comma-separated for multiple periods)') # if str_ma_period: # t = df_p.columns[0] # for p in str_ma_period.split(','): # df_p[f'{p}bars_MA'] = df_p[t].rolling(int(p)).mean().shift() # ATR Time-Series df_p = df_dict[l_tickers[0]] df_p = df_p[[c for c in df_p.columns if 'ATR' in c]] fig = px.line( df_p, y=df_p.columns, labels={ 'x': 'Date', 'y': 'ATR' }, title=f'Historical Average True Range ({atr_period} bars)') show_plotly(fig) else: # View ATR time series of all given stocks atr_dict = { ticker : (df['ATR']/df['Close']).dropna().to_dict() \ if norm_atr else df['ATR'].dropna().to_dict() for ticker, df in df_dict.items() } df_p = pd.DataFrame.from_dict(atr_dict) # tickers Selection tickers = st.multiselect(f'ticker', options=[''] + list(df_dict.keys())) # ATR Time-Series fig = px.line( df_p, y=tickers if tickers else df_p.columns, labels={ 'x': 'Date', 'y': 'ATR' }, title=f'Historical Average True Range ({atr_period} bars)') show_plotly( fig ) #, height = chart_size, title = f"Price chart({interval}) for {l_tickers[0]}") # ATR Histogram fig = px.histogram( df_p, x=tickers if tickers else df_p.columns, barmode=chart_configs['barmode'], title=f'Average True Range ({atr_period} bars) Distribution', nbins=chart_configs['n_bins']) show_plotly(fig) # KER Time-Series KER_dict = { ticker: df['KER'].dropna().to_dict() for ticker, df in df_dict.items() } df_p = pd.DataFrame.from_dict(KER_dict) fig = px.line(df_p, y=tickers if tickers else df_p.columns, labels={ 'x': 'Date', 'y': 'KER' }, title=f'Historical efficiency ratio ({atr_period} bars)') show_plotly(fig) # KER histogram fig = px.histogram( df_p, x=tickers if tickers else df_p.columns, barmode=chart_configs['barmode'], title=f'Efficiency Ratio ({atr_period} bars) Distribution', nbins=chart_configs['n_bins']) show_plotly(fig) # Volume if is_single: str_ma_period = st.text_input( 'Volume moving averages period (comma-separated for multiple periods)' ) df_p = df_dict[l_tickers[0]] if str_ma_period: t = df_p.columns[0] for p in str_ma_period.split(','): df_p[f'Volume_{p}bars_MA'] = df_p["Volume"].rolling( int(p)).mean().shift() df_p = df_p[[c for c in df_p.columns if 'Volume' in c]] else: volume_dict = { ticker: df['Volume'].dropna().to_dict() for ticker, df in df_dict.items() } df_p = pd.DataFrame.from_dict(volume_dict) volume_scatter = px.line(df_p, y=tickers if tickers else df_p.columns, labels={ 'x': 'Date', 'y': 'Volume' }, title=f'volume scatter plot') volume_hist = px.histogram(df_p, x=tickers if tickers else df_p.columns, barmode=chart_configs['barmode'], title=f'Volume Distribution', nbins=chart_configs['n_bins']) show_plotly(volume_scatter) show_plotly(volume_hist)
def Main(): with st.sidebar.expander("GP"): st.info(f''' Graph Prices (open-high-low-close) * inspired by this [blog post](https://towardsdatascience.com/creating-a-finance-web-app-in-3-minutes-8273d56a39f8) and this [youtube video](https://youtu.be/OhvQN_yIgCo) * plots by Plotly with thanks to this [kaggle notebook](https://www.kaggle.com/mtszkw/technical-indicators-for-trading-stocks) ''') tickers = tickers_parser(st.text_input('enter stock ticker'), max_items = 1) with st.sidebar.expander('timeframe', expanded = True): today = datetime.date.today() end_date = st.date_input('Period End Date', value = today) if st.checkbox('pick start date'): start_date = st.date_input('Period Start Date', value = today - datetime.timedelta(days = 365)) else: tenor = st.text_input('Period', value = '6m') start_date = (BusinessDate(end_date) - tenor).to_date() st.info(f'period start date: {start_date}') # TODO: allow manual handling of data_start_date # l_interval = ['1d','1wk','1m', '2m','5m','15m','30m','60m','90m','1h','5d','1mo','3mo'] interval = st.selectbox('interval', options = ['1d', '1wk', '1mo']) is_intraday = interval.endswith(('m','h')) data_start_date = start_date if is_intraday else \ (BusinessDate(start_date) - "1y").to_date() if is_intraday: st.warning(f''' intraday data cannot extend last 60 days\n also, some features below might not work properly ''') if tickers: stock_obj = yf.Ticker(tickers) if not valid_stock(stock_obj): st.error(f''' {tickers} is an invalid ticker.\n Having trouble finding the right ticker?\n Check it out first in `DESC` :point_left: ''') return None side_config = st.sidebar.expander('charts configure', expanded = False) with side_config: show_df = st.checkbox('show price dataframe', value = False) chart_size = st.number_input('Chart Size', value = 1200, min_value = 400, max_value = 1500, step = 50) side_stock_info = get_stock_info_container(stock_obj.info, st_asset= st.sidebar) data = get_stocks_ohlc(tickers, start_date = data_start_date, end_date = end_date, interval = interval, proxies = get_proxies()) with st.expander('Indicators'): l_col, m_col , r_col = st.columns(3) with l_col: st.write('#### the moving averages') ma_type = st.selectbox('moving average type', options = ['', 'ema', 'sma', 'vwap']) periods = st.text_input('moving average periods (comma separated)', value = '22,11') if ma_type: for p in periods.split(','): data = add_moving_average(data, period = int(p), type = ma_type) st.write('#### volume-based indicators') # do_volume_profile = st.checkbox('Volume Profile') data = add_AD(data) if st.checkbox('Show Advance/ Decline') else data data = add_OBV(data) if st.checkbox('Show On Balance Volume') else data with m_col: st.write('#### MACD') do_MACD = st.checkbox('Show MACD?', value = True) fast = st.number_input('fast', value = 12) slow = st.number_input('slow', value = 26) signal = st.number_input('signal', value = 9) if do_MACD: data = add_MACD(data, fast = fast, slow = slow, signal = signal ) with r_col: st.write('#### oscillator') do_RSI = st.checkbox('RSI') data = add_RSI(data, n = st.number_input('RSI period', value = 13)) if do_RSI else data tup_RSI_hilo = st.text_input('RSI chart high and low line (comma separated):', value = '70,30').split(',') \ if do_RSI else None tup_RSI_hilo = [int(i) for i in tup_RSI_hilo] if tup_RSI_hilo else None if do_RSI: data_over_hilo_pct = sum( ((data['RSI']> tup_RSI_hilo[0]) | (data['RSI']< tup_RSI_hilo[1])) & (data.index > pd.Timestamp(start_date)) ) / len(data[data.index > pd.Timestamp(start_date)]) st.info(f""" {round(data_over_hilo_pct * 100, 2)}% within hilo\n 5% of peaks and valley should be within hilo """) st.write('#### True Range Related') atr_period = int(st.number_input('Average True Range Period', value = 13)) atr_ema = st.checkbox('use EMA for ATR', value = True) show_ATR = st.checkbox('show ATR?', value = False) if ma_type: st.write('##### ATR Channels') atr_ma_name = st.selectbox('select moving average for ATR channel', options = [''] + get_moving_average_col(data.columns)) atr_channels = st.text_input('Channel Lines (comma separated)', value = "1,2,3") \ if atr_ma_name else None fill_channels = st.checkbox('Fill Channels with color', value = False) \ if atr_ma_name else None else: atr_ma_name = None data = add_ATR(data, period = atr_period, use_ema = atr_ema, channel_dict = {atr_ma_name: [float(c) for c in atr_channels.split(',')]} \ if atr_ma_name else None ) st.write(f'##### Directional System') do_ADX = st.checkbox('Show ADX') data = add_ADX(data, period = st.number_input("ADX period", value = 13)) \ if do_ADX else data with st.expander('advanced settings'): l_col, m_col , r_col = st.columns(3) with l_col: st.write('#### Market Type Classification') mkt_class_period = int(st.number_input('peroid (match your trading time domain)', value = 66)) mkt_class = market_classification(data, period = mkt_class_period, debug = False) if mkt_class_period else None if mkt_class: side_stock_info.write(f'market is `{mkt_class}` for the last **{mkt_class_period} bars**') side_stock_info.write(f'[kaufman efficiency_ratio](https://strategyquant.com/codebase/kaufmans-efficiency-ratio-ker/) ({mkt_class_period} bars): `{round(efficiency_ratio(data, period = mkt_class_period),2)}`') st.write('#### Events') do_div = st.checkbox('show ex-dividend dates') if do_div: data = add_div_col(df_price = data, df_div = stock_obj.dividends) side_stock_info.write( stock_obj.dividends[stock_obj.dividends.index > pd.Timestamp(start_date)] ) do_earnings = st.checkbox('show earning dates') if do_earnings and isinstance(stock_obj.calendar, pd.DataFrame): data = add_event_col(df_price = data, df_events = stock_obj.calendar.T.set_index('Earnings Date'), event_col_name= "earnings") side_stock_info.write(stock_obj.calendar.T) if do_MACD and ma_type: st.write("#### Elder's Impulse System") impulse_ema = st.selectbox('select moving average for impulse', options = [''] + get_moving_average_col(data.columns)) data = add_Impulse(data, ema_name = impulse_ema) if impulse_ema else data avg_pen_data = None with m_col: if ma_type: st.write("#### Average Penetration for Entry/ SafeZone") fair_col = st.selectbox('compute average penetration below', options = [''] + get_moving_average_col(data.columns)) avg_pen_data = add_avg_penetration(df = data, fair_col = fair_col, num_of_bars = st.number_input('period (e.g. 4-6 weeks)', value = 30), # 4-6 weeks use_ema = st.checkbox('use EMA for penetration', value = False), ignore_zero = st.checkbox('ignore days without penetration', value = True), coef = st.number_input( 'SafeZone Coefficient (stops should be set at least 1x Average Penetration)', value = 1.0, step = 0.1), get_df = True, debug = True ) if fair_col else None with r_col: if do_MACD: st.write('#### MACD Bullish Divergence') if st.checkbox('Show Divergence'): data = detect_macd_divergence(data, period = st.number_input('within number of bars (should be around 3 months)', value = 66), threshold = st.number_input('current low threshold (% of previous major low)', value = 0.95), debug = True ) st.write(f'#### Detect Kangaroo Tails') tail_type = st.selectbox('Tail Type', options = ['', 0, 1, -1]) data = detect_kangaroo_tails(data, atr_threshold = st.number_input('ATR Threshold', value = 2.0), period = st.number_input('period', value = 22), tail_type = tail_type) \ if tail_type else data beta_events_to_plot, l_events_to_color, l_col_to_scatter = [], [], [] show_beta_features(data = data, l_events_to_color=l_events_to_color, l_col_to_scatter = l_col_to_scatter, atr_period = atr_period) if show_df: with st.expander(f'raw data (last updated: {data.index[-1].strftime("%c")})'): st.write(data) if isinstance(avg_pen_data, pd.DataFrame): with st.expander('Buy Entry (SafeZone)'): avg_pen_dict = { 'average penetration': avg_pen_data['avg_lp'][-1], 'ATR': avg_pen_data['ATR'][-1], 'penetration stdv': avg_pen_data['std_lp'][-1], 'number of penetrations within period': avg_pen_data['count_lp'][-1], 'last': avg_pen_data['Close'][-1], 'expected ema T+1': avg_pen_data[fair_col][-1] + (avg_pen_data[fair_col][-1] - avg_pen_data[fair_col][-2]) } avg_pen_dict = {k:round(v,2) for k,v in avg_pen_dict.items()} avg_pen_dict['buy target T+1'] = avg_pen_dict['expected ema T+1'] - avg_pen_dict['average penetration'] st.write(avg_pen_dict) plot_avg_pen = st.checkbox('plot buy SafeZone and show average penetration df') plot_target_buy = False # st.checkbox('plot target buy T+1') # if plot_avg_pen: # st.write(avg_pen_data) if not(show_ATR) and 'ATR' in data.columns: del data['ATR'] #TODO: fix tz issue for interval < 1d # see: https://stackoverflow.com/questions/16628819/convert-pandas-timezone-aware-datetimeindex-to-naive-timestamp-but-in-certain-t fig = plotly_ohlc_chart( df = data if is_intraday else data[data.index > pd.Timestamp(start_date)], vol_col = 'Volume', tup_rsi_hilo = tup_RSI_hilo, b_fill_channel = fill_channels if atr_ma_name else None ) #, show_volume_profile = do_volume_profile) # SafeZone if isinstance(avg_pen_data, pd.DataFrame): fig = add_Scatter(fig, df = avg_pen_data[avg_pen_data.index > pd.Timestamp(start_date)], target_col = 'buy_safezone') \ if plot_avg_pen else fig if plot_target_buy: fig.add_hline(y = avg_pen_dict['buy target T+1'] , line_dash = 'dot', row =1, col = 1) # Events for d in ['MACD_Divergence', 'kangaroo_tails', 'ex-dividend', 'earnings'] + beta_events_to_plot: if d in data.columns: fig = add_Scatter_Event(fig, data[data.index > pd.Timestamp(start_date)], target_col = d, anchor_col = 'Low', textposition = 'bottom center', fontsize = 8, marker_symbol = 'triangle-up', event_label = d[0]) # Color Events for d in l_events_to_color: fig = add_color_event_ohlc(fig, data[data.index > pd.Timestamp(start_date)], condition_col = d['column'], color = d['color'] ) if d['column'] in data.columns else fig # Scatter Columns for c in l_col_to_scatter: fig = add_Scatter(fig, data[data.index > pd.Timestamp(start_date)], target_col = c['column'], line_color = c['color']) show_plotly(fig, height = chart_size, title = f"Price chart({interval}) for {tickers} : {stock_obj.info['longName']}")
def Main(): with st.sidebar.expander("BETA"): st.info(f''' Beta Analysis vs Benchmark Security ''') default_tickers = get_index_tickers( st_asset = st.sidebar.expander('Load an Index', expanded = True) ) tickers = tickers_parser( st.text_input('enter stock ticker(s) [space separated]', value = default_tickers) ) with st.sidebar.expander('settings', expanded = False): today = datetime.date.today() end_date = st.date_input('Period End Date', value = today) if st.checkbox('pick start date'): start_date = st.date_input('Period Start Date', value = today - datetime.timedelta(days = 365)) else: tenor = st.text_input('Period', value = '250b') start_date = (BusinessDate(end_date) - tenor).to_date() st.info(f'period start date: {start_date}') l_interval = ['1d','1m', '2m','5m','15m','30m','60m','90m','1h','5d','1wk','1mo','3mo'] interval = st.selectbox('interval', options = l_interval) if tickers: side_config = st.sidebar.expander('charts configure', expanded = False) with side_config: show_ohlc = st.checkbox('ohlc chart', value = True) # b_two_col = st.checkbox('two-column view', value = True) chart_size = st.number_input('Chart Size', value = 500, min_value = 400, max_value = 1500) data_dict = get_yf_data(tickers, start_date = start_date, end_date = end_date, interval = interval) df_returns = data_dict['returns'].copy() with st.expander('view returns data'): st.subheader('Returns') st.write(df_returns) l_tickers = df_returns.columns.tolist() if len(l_tickers) != len(tickers.split(' ')): st.warning(f'having trouble finding the right ticker?\nCheck it out first in `DESC` :point_left:') l_col, r_col = st.columns(2) with l_col: benchmark_ticker = st.selectbox('benchmark security', options = tickers.split()) beta_json = get_betas(df_returns, benchmark_col = benchmark_ticker.upper()) # TODO: add dividend yield? beta_df = pd.DataFrame.from_dict(beta_json) with r_col: plot_var_options = [col for col in beta_df.columns if col not in ['ticker']] y_var = st.selectbox('y-axis variable', options = plot_var_options) x_var = st.selectbox('x-axis variable', options = plot_var_options) with st.expander('Betas Calcuation'): st.write(beta_df) fig = px.scatter(beta_df, x = x_var, y = y_var, color = 'ticker') fig.update_layout(showlegend = False) show_plotly(fig)