def coherent_evolve(transaction_cost=0):
    bt_st_date = "2020-09-13"

    bt_date_range = [
        "-".join([
            str(pd.to_datetime(bt_st_date).year - i),
            str(pd.to_datetime(bt_st_date).month),
            str(pd.to_datetime(bt_st_date).day)
        ]) for i in range(0, 21, 5)
    ]

    fig, axs = plt.subplots(4, 2, figsize=(10, 14), dpi=800)
    cmaps = sns.diverging_palette(220, 20, center="dark", n=2)

    ax0s = []

    labels = []

    perfs = pd.DataFrame(columns=pd.MultiIndex.from_product(
        [['2020', '2015', '2010', '2005'], [(20, 50, 150), (30, 60, 160)]],
        names=['end_year', 'trading_rules']))

    for i, end_date in enumerate(bt_date_range[:-1]):

        st_date = bt_date_range[i + 1]
        start_bt_date_1yr_plus = "-".join([
            str(pd.to_datetime(st_date).year - 1),
            str(pd.to_datetime(st_date).month),
            str(pd.to_datetime(st_date).day)
        ])

        df, b_df = get_df_stoxx600(start_bt_date_1yr_plus, end_date)

        b_df = b_df[st_date:end_date]
        ret_b = b_df.pct_change().fillna(0)

        port_ret = ret_b.rename('short').to_frame()

        ## index

        ew_eq_curve = (1 + ret_b).cumprod().rename('short').to_frame()

        ax0s.append(ew_eq_curve['short'].plot(ax=axs[i, 0],
                                              lw=0.75,
                                              c='g',
                                              label='index'))

        for s, sigs in enumerate([(20, 50, 150), (30, 60, 160)]):

            port_ret['long'] = trend_trading(df,
                                             st_date,
                                             end_date,
                                             sigs=sigs,
                                             transaction_cost=0)

            port_ret = port_ret.fillna(0)

            ew_eq_curve = (port_ret + 1).cumprod()

            ax0s.append(ew_eq_curve['long'].plot(ax=axs[i, 0],
                                                 lw=0.75,
                                                 c=cmaps[s]))

            ## combined PnL

            combined_PnL = (port_ret['long'] - port_ret['short'] + 1).cumprod()
            combined_PnL.plot(ax=axs[i, 1], c=cmaps[s])

            perfs[(end_date[:4], sigs)] = port_stats(ew_eq_curve['long'],
                                                     ew_eq_curve['short'])

            # print(port_ret['long'])

            # print(port_stats(ew_eq_curve['long'], ew_eq_curve['short']))

    custom_lines = [
        Line2D([0], [0], color=cmaps[0], lw=0.75),
        Line2D([0], [0], color=cmaps[1], lw=0.75),
        Line2D([0], [0], color='g', lw=0.75)
    ]

    fig.legend(custom_lines,
               ['long (20,50,150)', 'long (30,60,160)', 'short index'],
               ncol=len(custom_lines),
               loc="upper center")
    plt.setp(ax0s, ylabel='Cumulative Return')
    # plt.show()
    plt.savefig(f"stress_trend_follow/coherent_evolve.png",
                bbox_inches='tight')  #
    plt.close()
    # perfs = perfs.T

    with pd.option_context('display.max_rows', None, 'display.max_columns',
                           None):

        print(perfs.T.to_latex())

        r1 = perfs.iloc[:,
                        perfs.columns.get_level_values(1) == (
                            30, 60, 160)].droplevel('trading_rules', axis=1)
        r2 = perfs.iloc[:,
                        perfs.columns.get_level_values(1) == (
                            20, 50, 150)].droplevel('trading_rules', axis=1)

        print((r1 - r2).sort_index(axis=1).to_latex())
def structural_pred(start_bt_date='2007-01-01', end_bt_date='2010-01-01'):
    '''
    deviate from the optimal trading rule by using

    signal_{x} > signal_{x+1} > signal_{x+2}.


    :param sigs:
    :param start_bt_date:
    :param end_bt_date:
    :return:
    '''

    start_bt_date_1yr_plus = "-".join(
        [str(int(start_bt_date.split('-')[0]) - 1)] +
        start_bt_date.split('-')[1:])

    df, stoxx600 = get_df_stoxx600(
        start_bt_date_1yr_plus=start_bt_date_1yr_plus, end_bt_date=end_bt_date)
    transaction_cost = 0.0000

    ret_stoxx600 = stoxx600[start_bt_date:].pct_change().fillna(0)
    eq_curve_stoxx600 = (ret_stoxx600 + 1).cumprod()

    sigs = (30, 60, 160)

    closing_period = 100
    # deviations = list(zip(list(range(sigs[0]+2, closing_period+2)), list(range(sigs[1]+1, closing_period+1)), [sigs[2]] * closing_period))
    # deviations.insert(0,sigs)

    deviations = [(20, 50, 150), (30, 59, 158), (30, 60, 160),
                  (32, 61, 160)]  #, (30,58,157) ,(40,65,160)

    cmaps = sns.diverging_palette(220, 20, center="dark", n=len(deviations))
    fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(10, 3.5), dpi=800)
    # fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(100, 50))

    ax0s = []

    labels = []
    long_end_PnLs = []

    plt_freq = 1
    for i, new_sigs in enumerate(deviations):

        temp_fast, temp_mid, temp_slow = trend_follow_sigs(df, new_sigs)

        long_signal = ((temp_fast > temp_mid) &
                       (temp_mid > temp_slow)).astype(int)
        long_holdings = long_signal.shift(1)[start_bt_date:]

        ret_df = df[start_bt_date:].pct_change(1).fillna(0)

        arr_transaction_cost = [0, 0, 0, 0, transaction_cost] * (
            len(ret_df.index) // 5) + [0] * (len(ret_df.index) % 5)

        ## equal weighting
        long_w = (1 / long_holdings.sum(axis=1)).replace([np.inf, -np.inf], 0)

        port_ret = (long_holdings.mul(long_w, axis='index') *
                    ret_df).sum(axis=1).rename('long').to_frame()
        port_ret['benchmark'] = ret_stoxx600
        port_ret = port_ret.fillna(0)

        ## Long/ Short Side Plot
        ew_eq_curve = (port_ret + 1).cumprod()

        # plt_freq = 10 if direction == 'Neg Deviations' else 30

        # print(f"new sigs {new_sigs}, {type(new_sigs[0])}")
        if i % plt_freq == 0 or new_sigs == [30, 60, 160]:
            ax0s.append(ew_eq_curve['long'].plot(ax=ax0, lw=0.75, c=cmaps[i]))
            if new_sigs == (20, 50, 150):
                labels.append(f"{','.join(map(str, new_sigs))} (benchmark)")
            else:
                labels.append(",".join(map(str, new_sigs)))

            ## combined PnL
            combined_PnL = (port_ret['long'] - port_ret['benchmark'] -
                            arr_transaction_cost + 1).cumprod()
            combined_PnL.plot(ax=ax1, lw=0.70, c=cmaps[i])

        long_end_PnLs.append(
            (",".join(map(str,
                          new_sigs)), ew_eq_curve['long'].tail(1).values[0]))

    # ax0.legend(loc='upper center', fontsize='small', ncol=len(deviations)//5)
    # ax1.legend(loc='upper center', fontsize='small', ncol=len(deviations)//5)

    b = eq_curve_stoxx600.plot(ax=ax0, lw=0.70, c='g', label='index')
    ax0s.append(b)
    labels.append("index")

    fig.legend(
        ax0s,  # The line objects
        labels=labels,  # The labels for each line
        loc="upper center",  # Position of legend
        borderaxespad=0.1,  # Small spacing around legend box
        # fontsize='small',
        ncol=len(labels))

    plt.setp(ax0s, ylabel='Cumulative Return')
    # plt.show()
    plt.savefig(f"stress_trend_follow/pnl_struct_pred.png",
                bbox_inches='tight')
    plt.close()
Esempio n. 3
0
import bt_tools
import pandas as pd
import matplotlib.pyplot as plt

from risk_parity import risk_parity_weighting

ed_date = '2020-09-13'
st_date = '2015-09-13'
st_two_date = '2014-09-13'
start_bt_date_1yr_plus = '2012-09-13'

# df, b_df = bt_tools.get_df_sp500(start_bt_date_1yr_plus, ed_date)
df, b_df = bt_tools.get_df_stoxx600(start_bt_date_1yr_plus, ed_date)
# df, b_df = bt_tools.get_df_ftse250(start_bt_date_1yr_plus, ed_date)

##  index returns
port_df = b_df.loc[st_date:ed_date].pct_change().fillna(0).rename(
    'short').to_frame()

sigs = (30, 60, 160)
fast_MA, mid_MA, slow_MA = bt_tools.trend_follow_sigs(df, sigs)

entry_long = ((fast_MA > mid_MA) & (mid_MA > slow_MA)).astype(int)
exit_long = ((fast_MA < mid_MA) & (mid_MA > slow_MA)).astype(int)
pos_df_long = bt_tools.get_position_df(df, entry_long, exit_long)

# eq_risk_w_long = bt_tools.equal_risk_weighting(df, pos_df_long, st_date, ed_date)
# eq_risk_w_long.to_pickle('eq_risk_w_long.pkl')

eq_risk_w_long = pd.read_pickle("eq_risk_w_long.pkl")
def stress_trend_follow(direction='Pos Deviations',
                        start_bt_date='2007-01-01',
                        end_bt_date='2010-01-01'):
    dev_name = direction  ##
    dd_devs = {
        'Pos Deviations': range(0, 51, 1),
        'Neg Deviations': range(0, -21, -1)
    }

    start_bt_date_1yr_plus = "-".join(
        [str(int(start_bt_date.split('-')[0]) - 1)] +
        start_bt_date.split('-')[1:])

    df, stoxx600 = get_df_stoxx600(
        start_bt_date_1yr_plus=start_bt_date_1yr_plus, end_bt_date=end_bt_date)
    transaction_cost = 0.0010

    ret_stoxx600 = stoxx600[start_bt_date:].pct_change().fillna(0)
    eq_curve_stoxx600 = (ret_stoxx600 + 1).cumprod()

    sigs = [20, 50, 150]

    deviations = dd_devs[dev_name]

    cmaps = sns.diverging_palette(250,
                                  15,
                                  s=75,
                                  l=40,
                                  center="dark",
                                  n=len(deviations))
    fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(10, 3.5), dpi=800)

    ax0s = []

    labels = []
    long_end_PnLs = []
    long_mean_Sharpe = []
    for i, plus_t in enumerate(deviations):
        new_sigs = list(map(lambda x: x + plus_t, sigs))
        temp_fast, temp_mid, temp_slow = trend_follow_sigs(df, new_sigs)

        long_signal = ((temp_fast > temp_mid) &
                       (temp_mid > temp_slow)).astype(int)
        long_holdings = long_signal.shift(1)[start_bt_date:]

        ret_df = df[start_bt_date:].pct_change(1).fillna(0)

        arr_transaction_cost = [0, 0, 0, 0, transaction_cost] * (
            len(ret_df.index) // 5) + [0] * (len(ret_df.index) % 5)

        ## equal weighting
        long_w = (1 / long_holdings.sum(axis=1)).replace([np.inf, -np.inf], 0)

        port_ret = (long_holdings.mul(long_w, axis='index') *
                    ret_df).sum(axis=1).rename('long').to_frame()
        port_ret['index'] = ret_stoxx600
        port_ret = port_ret.fillna(0)

        ## Long/ Short Side Plot
        ew_eq_curve = (port_ret + 1).cumprod()

        plt_freq = 10 if direction == 'Neg Deviations' else 30

        # print(f"new sigs {new_sigs}, {type(new_sigs[0])}")
        if i % plt_freq == 0 or new_sigs == [30, 60, 160]:
            ax0s.append(ew_eq_curve['long'].plot(ax=ax0, lw=0.75, c=cmaps[i]))
            if new_sigs == [20, 50, 150]:
                labels.append(f"{','.join(map(str,new_sigs))} (benchmark)")
            else:
                labels.append(",".join(map(str, new_sigs)))

            ## combined PnL
            combined_PnL = (port_ret['long'] - port_ret['index'] -
                            arr_transaction_cost + 1).cumprod()
            combined_PnL.plot(ax=ax1, lw=0.70, c=cmaps[i])
        ## bar EndPnL
        long_end_PnLs.append(
            (",".join(map(str,
                          new_sigs)), ew_eq_curve['long'].tail(1).values[0]))
        long_mean_Sharpe.append(
            (",".join(map(str, new_sigs)), sharpe(ew_eq_curve['long'])))

    # ax0.legend(loc='upper center', fontsize='small', ncol=len(deviations)//5)
    # ax1.legend(loc='upper center', fontsize='small', ncol=len(deviations)//5)

    b = eq_curve_stoxx600.plot(ax=ax0, lw=0.70, c='g', label='index')
    ax0s.append(b)
    labels.append("index")

    fig.legend(
        ax0s,  # The line objects
        labels=labels,  # The labels for each line
        loc="upper center",  # Position of legend
        borderaxespad=0.1,  # Small spacing around legend box
        # fontsize='small',
        ncol=len(labels))

    plt.setp(ax0s, ylabel='Cumulative Return')
    # plt.show()
    plt.savefig(
        f"stress_trend_follow/pnl_{direction.lower().replace(' ','_')}.png",
        bbox_inches='tight')
    plt.close()

    ## Bar mean Sharpe ratio
    long_mean_Sharpe = pd.DataFrame(long_mean_Sharpe,
                                    columns=['sig', 'sharpe'])

    fig, ax = plt.subplots(1, 1, figsize=(5, 3.5), dpi=800)
    ax.bar(long_mean_Sharpe['sig'], long_mean_Sharpe['sharpe'])
    plt.xticks(rotation=90, fontsize=6)
    plt.ylabel('avg. annual Sharpe')
    # plt.show()
    plt.savefig(
        f"stress_trend_follow/bar_mean_sharpe_{direction.lower().replace(' ', '_')}.png",
        bbox_inches='tight')
    plt.close()

    ## Bar End PnL
    long_end_PnLs = pd.DataFrame(long_end_PnLs, columns=['sig', 'PnL'])

    fig, ax = plt.subplots(1, 1, figsize=(5, 3.5), dpi=800)
    ax.bar(long_end_PnLs['sig'], long_end_PnLs['PnL'])
    plt.xticks(rotation=90, fontsize=6)
    plt.ylabel('End Cumulative Return')
    # plt.show()
    plt.savefig(
        f"stress_trend_follow/bar_end_{direction.lower().replace(' ', '_')}.png",
        bbox_inches='tight')
    plt.close()
def long_short_trend(transaction_cost=0.0000, plot=False):
    start_bt_date_1yr_plus = '2014-09-13'
    start_bt_date = '2015-09-13'
    end_bt_date = '2020-09-13'
    df, stoxx600 = get_df_stoxx600(
        start_bt_date_1yr_plus=start_bt_date_1yr_plus, end_bt_date=end_bt_date)
    # df, stoxx600 = get_df_sp500(start_bt_date_1yr_plus=start_bt_date_1yr_plus, end_bt_date=end_bt_date)

    ## short index

    port_ret = stoxx600.loc[start_bt_date:end_bt_date].pct_change().rename(
        'short_index').to_frame()

    ## sides return
    port_ret['long'] = trend_trading(df,
                                     start_bt_date,
                                     end_bt_date,
                                     sigs=(20, 50, 150),
                                     transaction_cost=0)
    port_ret['short_trend'] = trend_trading(df,
                                            start_bt_date,
                                            end_bt_date,
                                            sigs=(20, 50, 150),
                                            transaction_cost=0,
                                            direction='short')
    port_ret = port_ret.fillna(0)

    ## short trend side

    plt.rcParams["figure.dpi"] = 800

    with pd.option_context('display.max_rows', None, 'display.max_columns',
                           None):
        print(port_ret['short_trend'].sort_values(ascending=False))
    (-port_ret['short_trend'].fillna(0) + 1).cumprod().plot(
        figsize=(5, 3.5), legend=True, lw=0.75, fontsize=10).legend(loc=2)
    plt.ylabel('Cumulative Return')
    plt.savefig("short_trend_follow/short_side_pnl.png")
    plt.close()

    ## ew_eq_curves
    eq_eq_curves = (port_ret + 1).cumprod()

    plt.rcParams["figure.dpi"] = 800

    eq_eq_curves.plot(figsize=(5, 3.5), legend=True, lw=0.75,
                      fontsize=10).legend(loc=2)
    plt.ylabel('Cumulative Return')
    plt.savefig("short_trend_follow/pnls")
    plt.close()

    ## combined Pnl

    plt.rcParams["figure.dpi"] = 800
    combined_PnL = (
        port_ret['long'] -
        port_ret['short_trend']).rename('long_short_trend').to_frame()
    combined_PnL['benchmark'] = (port_ret['long'] - port_ret['short_index'])
    combined_PnL = (combined_PnL.fillna(0) + 1).cumprod()
    combined_PnL.plot(figsize=(5, 3.5), legend=True, lw=0.75,
                      fontsize=10).legend(loc=2)
    plt.ylabel('Cumulative Return')
    plt.savefig("short_trend_follow/combined_pnl")
    plt.close()

    # eq_eq_curves['long'] = (port_ret['long'] +1).cumprod()
    # eq_eq_curves['short'] = (port_ret['short'] +1).cumprod()
    ## performance_analysis

    performance_analysis(eq_eq_curves['long'], eq_eq_curves['short_trend'])

    ## portfolio analysis

    return pd.DataFrame({
        'benchmark':
        port_stats(eq_eq_curves['long'], eq_eq_curves['short_index']),
        'long_short_trend':
        port_stats(eq_eq_curves['long'], eq_eq_curves['short_trend'])
    })