예제 #1
0
def get_ticker_data_with_technicals(
        ticker:str, interval:str = '1d', trade_date = datetime.date.today(),
        tenor:str = '2y', data_buffer_tenor:str = '1y',
        l_ma_periods:list = [22,11],
        atr_params = {'period': 13, 'use_ema': True, 'channel_dict' : None},
        return_all_dates = False
        ):
    ''' Get Price Data for a Ticker and add various Technical Indicators intended for
    trend following strategies backtesting/ screening
    Args:
        l_ma_periods: list of moving averages periods to add
        return_all_dates: if False, return only data from start_date
    '''
    start_date = (BusinessDate(trade_date)- tenor).to_date()
    data_start_date = (BusinessDate(start_date) - data_buffer_tenor).to_date()
    df = get_stocks_ohlc(tickers = tickers_parser(ticker),
            interval = interval,
            start_date = data_start_date,
            end_date = trade_date,
            proxies = get_proxies()
            )

    for p in l_ma_periods:
        df = add_moving_average(df, period = p, type = 'ema')
    df = add_MACD(df)
    df = add_ATR(df, **atr_params) if atr_params else df
    return df if return_all_dates else df[df.index > pd.Timestamp(start_date)]
예제 #2
0
def show_upcoming_div(df, st_asset, timeframe_params, atr_period=22):
    with st_asset:
        if not isinstance(df, pd.DataFrame):
            st.warning('no upcoming dividends found')
            return None
        div_tickers = df['ticker'].tolist()

        # Get Price Data
        ohlc_dict = get_ohlc_data(
            div_tickers,
            start_date=timeframe_params['data_start_date'],
            end_date=timeframe_params['end_date'],
            interval=timeframe_params["interval"])
        if 'ATR' not in ohlc_dict[div_tickers[0]].columns:
            ohlc_dict = {
                t: add_ATR(df.copy(),
                           period=atr_period,
                           use_ema=True,
                           channel_dict=None)
                for t, df in ohlc_dict.items()
            }
        # Update DVD df
        atr = df['ticker'].apply(lambda x: ohlc_dict[x]['ATR'][-1])
        df.insert(loc=5, column='ATR', value=atr)
        df.insert(loc=4, column='div/ATR', value=df['Div'] / atr)
        st.write(df)

        if st.checkbox('export tickers'):
            st.info(" ".join(div_tickers))

        visualize_dvd_df(df)
예제 #3
0
def detect_vol_breakout(df,
                        period: int = 22,
                        threshold: float = 1,
                        ignore_gap=True,
                        do_buy=True,
                        add_entry_price=False,
                        ignore_volume=False):
    ''' add two new columns vol_breakout and entry_price to df
    '''
    # assert 'impulse' in df.columns
    threshold *= 1 if do_buy else -1
    ATR_col = 'ATR_' + str(int(period))
    df = add_ATR(df,
                 period=period,
                 use_ema=True,
                 col_name=ATR_col,
                 channel_dict=None,
                 return_TR=False) if ATR_col not in df.columns else df

    close_diff = (df['Close'] - df['Open']) if ignore_gap else \
                (df['Close'] - df['Close'].shift())

    # this doesn't seem to work, check 336.hk on 11/9
    volume_confirms = df['Volume'] > df['Volume'].shift().rolling(
        int(period)).mean()
    vol_breakout = close_diff > (df[ATR_col].shift() * threshold) if do_buy else \
                    close_diff < (df[ATR_col].shift() * threshold)
    if not ignore_volume:
        vol_breakout = (vol_breakout & volume_confirms)
    df['vol_breakout'] = vol_breakout

    if add_entry_price:
        # df['entry_price'] = (df['Open'] + df['ATR'].shift() * threshold) * vol_breakout
        df['entry_price'] = df['Close'] * vol_breakout
    return df
예제 #4
0
def detect_VCP(df,
               ma_cascade=[100, 50, 25],
               lvpb_period=22,
               ATR_period=5,
               debug_mode=False,
               normalize_ATR=False,
               col_name='VCP_setup'):
    '''Minervini's Volatility Contraction Pattern detection
    returns True for when all [a] all the price MAs are in ascending order,
    and [b] all the ATR MAs are in descending order, and
    [c] there are Low Volume Pullbacks within the ATR Period.
    '''
    assert len(
        ma_cascade) >= 2, 'detect_VCP: must have at least 2 moving averages'
    assert ma_cascade == sorted(
        ma_cascade)[::-1], 'detect_VCP: ma_cascade must be descending'
    # [a] checking for trend
    for p in ma_cascade:
        df = add_moving_average(df, period=p)
    s_trending = True
    for i in range(len(ma_cascade))[1:]:
        s_trending = s_trending & (df[f'ema_{ma_cascade[i]}'] >
                                   df[f'ema_{ma_cascade[i-1]}'])

    # [b] check for volatility contraction
    df = add_ATR(df,
                 period=ATR_period,
                 use_ema=True,
                 channel_dict=None,
                 normalize=normalize_ATR)
    df = detect_volatility_contraction(df,
                                       ma_cascade=ma_cascade,
                                       debug=debug_mode)

    # [c]
    df = detect_low_vol_pullback(df, period=lvpb_period)
    s_has_LVPB = df['LVPB'].rolling(ATR_period).max().shift()

    if debug_mode:
        df['VCP_trending_check'] = s_trending
        df['VCP_LVPB_check'] = s_has_LVPB

    df[col_name] = s_trending & df['VCP'] & s_has_LVPB
    return df
예제 #5
0
def detect_volatility_contraction(df,
                                  atr_periods=[11, 5],
                                  period: int = 100,
                                  threshold: float = 0.05,
                                  normalize=True,
                                  use_ema=True,
                                  col_name='VCP',
                                  debug=False):
    ''' return a column to indicate volatility contraction using ATR
    Args:
        atr_periods: check for ATRs being lower in cascading order (slowest to fastest)
        period: look-back period, number of bars
        threshold: current ATR must be less than this percentile within the look-back period
    '''
    assert 'ATR' in df.columns, 'detect_volatiliy_contraction: must add ATR first'
    assert len(
        atr_periods
    ) >= 2, 'detect_volatility_contraction: must have at least two moving average periods'
    for p in atr_periods:
        df = add_ATR(df,
                     period=p,
                     col_name=f'ATR_{p}',
                     use_ema=use_ema,
                     channel_dict=None,
                     normalize=normalize)
    df['ATR_percentile'] = df['ATR'].rolling(
        int(period)).apply(lambda A: get_percentile(x=A[-1], A=A))
    df[col_name] = df['ATR_percentile'] < threshold

    for i in range(len(atr_periods))[1:]:
        ATR_slow = f'ATR_{atr_periods[i-1]}'
        ATR_fast = f'ATR_{atr_periods[i]}'
        df[col_name] = df[col_name] & (df[ATR_slow] > df[ATR_fast])

    if not debug:
        for p in atr_periods:
            del df[f'ATR_{p}']
        del df['ATR_percentile']
    return df
예제 #6
0
def get_ATR_calc(df,
                 period,
                 use_ema,
                 atr_multiplier=2,
                 var=1000,
                 price_col='Adj Close'):
    '''
    return a dictionary of various ATR calcuations
    Args:
        df: dataframe of prices from yfinance for a Single Stock
        var: Value At Risk
    '''
    data = add_ATR(df, period=period, use_ema=use_ema, channel_dict=None)
    ATR = data['ATR'][-1]
    price = data[price_col][-1]
    shares = int(var /
                 (atr_multiplier * ATR))  # Basically always rounding down)
    return {
        'ATR': ATR,
        price_col: price,
        'ATR%Price': ATR / price,
        'num_shares': shares,
        'position_size': shares * price
    }
예제 #7
0
def alpha_over_beta(df):
    ''' a volatility breakout entry with a low vol setup
    ref: https://www.alphaoverbeta.net/trading-the-volatility-breakout-system/
    '''

    # check ATR cross over
    for p in [5, 14]:
        ATR_col = 'ATR_' + str(int(p))
        df = add_ATR(df,
                     period=p,
                     use_ema=True,
                     col_name=ATR_col,
                     channel_dict=None,
                     return_TR=False)

    # add 3 EMA
    for p in [5, 11, 22]:
        df = add_moving_average(df, period=p, type='ema', price_col='Close')

    vol_breakout = (df["ema_5"] > df["ema_11"]) & (df["ema_11"]> df["ema_22"]) & \
            (df["ATR_5"] > df["ATR_14"]) & (df["ATR_5"].shift() < df["ATR_14"].shift())
    df['vol_breakout'] = vol_breakout
    df['entry_price'] = df['Close'] * vol_breakout
    return df
예제 #8
0
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)
예제 #9
0
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']}")
예제 #10
0
def show_past_div(df, st_asset, timeframe_params, atr_period):
    with st_asset:
        if not isinstance(df, pd.DataFrame):
            st.warning('no dividends found')
            return None
        div_tickers = df['ticker'].unique().tolist()
        df = df[
            df['Ex-date'] < pd.Timestamp(timeframe_params['end_date'])].copy()

        # Get Price Data
        ohlc_dict = get_ohlc_data(
            div_tickers,
            start_date=timeframe_params['data_start_date'],
            end_date=timeframe_params['end_date'],
            interval=timeframe_params["interval"])
        if 'ATR' not in ohlc_dict[div_tickers[0]].columns:
            ohlc_dict = {
                t: add_ATR(df.copy(),
                           period=atr_period,
                           use_ema=True,
                           channel_dict=None)
                for t, df in ohlc_dict.items()
            }
        # add ATR, TR
        atr = df.apply(lambda row: get_df_value_by_date(
            df=ohlc_dict[row['ticker']],
            target_col='ATR',
            date=(BusinessDate(row['Ex-date']) - '1b').to_date()),
                       axis=1)
        tr = df.apply(
            lambda row: get_df_value_by_date(df=ohlc_dict[row['ticker']],
                                             target_col='TR',
                                             date=row['Ex-date'].date()),
            axis=1)
        df.insert(loc=4, column='ATR_ex-1', value=atr)
        df.insert(loc=5, column='TR_ex', value=tr)
        df.insert(loc=4, column='div/ATR', value=df['Div'] / atr)

        # User-Input on Capture
        if st.checkbox('test dividend capture'):
            l_col, r_col = st.beta_columns(2)
            atr_factor = l_col.number_input('number of ATR to risk', value=2.0)
            df['capture_risk'] = df.apply(
                lambda row: atr_factor * row['ATR_ex-1']
                if row['div/ATR'] > atr_factor else 0,
                axis=1)
            df['capture_reward'] = df.apply(
                lambda row: row['Div'] - row['TR_ex']
                if row['capture_risk'] > 0 else 0,
                axis=1)
            df['capture_r_multiplier'] = df.apply(
                lambda row: row['capture_reward'] / row['capture_risk']
                if row['capture_risk'] > 0 else 0,
                axis=1)

            results_dict = {
                'trades':
                len(df[df["capture_risk"] > 0]),
                'wins':
                len(df[df['capture_reward'] > 0]),
                'expectancy':
                df[df["capture_risk"] > 0]['capture_r_multiplier'].mean()
            }
            results_dict[
                'win_rate'] = results_dict['wins'] / results_dict['trades']
            r_col.write(results_dict)
        st.write(df)

        visualize_dvd_df(df.dropna(subset=['Div', 'ATR_ex-1']))