示例#1
0
def get_futures_spread_carry_signals(**kwargs):

    ticker_list = kwargs['ticker_list']
    date_to = kwargs['date_to']

    if 'tr_dte_list' in kwargs.keys():
        tr_dte_list = kwargs['tr_dte_list']
    else:
        tr_dte_list = [
            exp.get_futures_days2_expiration({
                'ticker': x,
                'date_to': date_to
            }) for x in ticker_list
        ]

    if 'aggregation_method' in kwargs.keys(
    ) and 'contracts_back' in kwargs.keys():
        aggregation_method = kwargs['aggregation_method']
        contracts_back = kwargs['contracts_back']
    else:
        amcb_output = opUtil.get_aggregation_method_contracts_back(
            cmi.get_contract_specs(ticker_list[0]))
        aggregation_method = amcb_output['aggregation_method']
        contracts_back = amcb_output['contracts_back']

    if 'use_last_as_current' in kwargs.keys():
        use_last_as_current = kwargs['use_last_as_current']
    else:
        use_last_as_current = False

    if 'futures_data_dictionary' in kwargs.keys():
        futures_data_dictionary = kwargs['futures_data_dictionary']
    else:
        futures_data_dictionary = {
            x: gfp.get_futures_price_preloaded(ticker_head=x)
            for x in [cmi.get_contract_specs(ticker_list[0])['ticker_head']]
        }

    if 'contract_multiplier' in kwargs.keys():
        contract_multiplier = kwargs['contract_multiplier']
    else:
        contract_multiplier = cmi.contract_multiplier[cmi.get_contract_specs(
            ticker_list[0])['ticker_head']]

    if 'datetime5_years_ago' in kwargs.keys():
        datetime5_years_ago = kwargs['datetime5_years_ago']
    else:
        date5_years_ago = cu.doubledate_shift(date_to, 5 * 365)
        datetime5_years_ago = cu.convert_doubledate_2datetime(date5_years_ago)

    aligned_output = opUtil.get_aligned_futures_data(
        contract_list=ticker_list,
        tr_dte_list=tr_dte_list,
        aggregation_method=aggregation_method,
        contracts_back=contracts_back,
        date_to=date_to,
        futures_data_dictionary=futures_data_dictionary,
        use_last_as_current=use_last_as_current)

    aligned_data = aligned_output['aligned_data']
    current_data = aligned_output['current_data']

    last5_years_indx = aligned_data['settle_date'] >= datetime5_years_ago
    data_last5_years = aligned_data[last5_years_indx]

    ticker1_list = [
        current_data['c' + str(x + 1)]['ticker']
        for x in range(len(ticker_list) - 1)
    ]
    ticker2_list = [
        current_data['c' + str(x + 2)]['ticker']
        for x in range(len(ticker_list) - 1)
    ]
    yield_current_list = [
        100 * (current_data['c' + str(x + 1)]['close_price'] -
               current_data['c' + str(x + 2)]['close_price']) /
        current_data['c' + str(x + 2)]['close_price']
        for x in range(len(ticker_list) - 1)
    ]

    butterfly_current_list = [
        100 * (current_data['c' + str(x + 1)]['close_price'] -
               2 * current_data['c' + str(x + 2)]['close_price'] +
               current_data['c' + str(x + 3)]['close_price']) /
        current_data['c' + str(x + 2)]['close_price']
        for x in range(len(ticker_list) - 2)
    ]

    price_current_list = [
        current_data['c' + str(x + 1)]['close_price'] -
        current_data['c' + str(x + 2)]['close_price']
        for x in range(len(ticker_list) - 1)
    ]

    yield_history = [
        100 * (aligned_data['c' + str(x + 1)]['close_price'] -
               aligned_data['c' + str(x + 2)]['close_price']) /
        aligned_data['c' + str(x + 2)]['close_price']
        for x in range(len(ticker_list) - 1)
    ]

    butterfly_history = [
        100 * (aligned_data['c' + str(x + 1)]['close_price'] -
               2 * aligned_data['c' + str(x + 2)]['close_price'] +
               aligned_data['c' + str(x + 3)]['close_price']) /
        aligned_data['c' + str(x + 2)]['close_price']
        for x in range(len(ticker_list) - 2)
    ]

    change_5_history = [
        data_last5_years['c' + str(x + 1)]['change_5'] -
        data_last5_years['c' + str(x + 2)]['change_5']
        for x in range(len(ticker_list) - 1)
    ]

    change5 = [
        contract_multiplier * (current_data['c' + str(x + 1)]['change5'] -
                               current_data['c' + str(x + 2)]['change5'])
        for x in range(len(ticker_list) - 1)
    ]

    change10 = [
        contract_multiplier * (current_data['c' + str(x + 1)]['change10'] -
                               current_data['c' + str(x + 2)]['change10'])
        for x in range(len(ticker_list) - 1)
    ]

    change20 = [
        contract_multiplier * (current_data['c' + str(x + 1)]['change20'] -
                               current_data['c' + str(x + 2)]['change20'])
        for x in range(len(ticker_list) - 1)
    ]

    front_tr_dte = [
        current_data['c' + str(x + 1)]['tr_dte']
        for x in range(len(ticker_list) - 1)
    ]

    q_list = [
        stats.get_quantile_from_number({
            'x':
            yield_current_list[x],
            'y':
            yield_history[x].values,
            'clean_num_obs':
            max(100, round(3 * len(yield_history[x].values) / 4))
        }) for x in range(len(ticker_list) - 1)
    ]

    butterfly_q_list = [
        stats.get_quantile_from_number({
            'x':
            butterfly_current_list[x],
            'y':
            butterfly_history[x].values[-40:],
            'clean_num_obs':
            round(3 * len(butterfly_history[x].values[-40:]) / 4)
        }) for x in range(len(ticker_list) - 2)
    ]

    extreme_quantiles_list = [
        stats.get_number_from_quantile(
            y=x.values[:-40], quantile_list=[10, 25, 35, 50, 65, 75, 90])
        for x in butterfly_history
    ]
    butterfly_q10 = [x[0] for x in extreme_quantiles_list]
    butterfly_q25 = [x[1] for x in extreme_quantiles_list]
    butterfly_q35 = [x[2] for x in extreme_quantiles_list]
    butterfly_q50 = [x[3] for x in extreme_quantiles_list]
    butterfly_q65 = [x[4] for x in extreme_quantiles_list]
    butterfly_q75 = [x[5] for x in extreme_quantiles_list]
    butterfly_q90 = [x[6] for x in extreme_quantiles_list]

    butterfly_noise_list = [
        stats.get_stdev(x=butterfly_history[i].values[-20:])
        for i in range(len(ticker_list) - 2)
    ]
    butterfly_mean_list = [
        stats.get_mean(x=butterfly_history[i].values[-10:])
        for i in range(len(ticker_list) - 2)
    ]

    butterfly_z_list = [(butterfly_current_list[i] - butterfly_mean_list[i]) /
                        butterfly_noise_list[i]
                        for i in range(len(ticker_list) - 2)]

    percentile_vector = [
        stats.get_number_from_quantile(
            y=change_5_history[x].values,
            quantile_list=[1, 15, 85, 99],
            clean_num_obs=max(100,
                              round(3 * len(change_5_history[x].values) / 4)))
        for x in range(len(ticker_list) - 1)
    ]

    q1 = [x[0] for x in percentile_vector]
    q15 = [x[1] for x in percentile_vector]
    q85 = [x[2] for x in percentile_vector]
    q99 = [x[3] for x in percentile_vector]

    downside = [
        contract_multiplier * (q1[x] + q15[x]) / 2 for x in range(len(q1))
    ]
    upside = [
        contract_multiplier * (q85[x] + q99[x]) / 2 for x in range(len(q1))
    ]
    carry = [
        contract_multiplier *
        (price_current_list[x] - price_current_list[x + 1])
        for x in range(len(q_list) - 1)
    ]
    q_carry = [q_list[x] - q_list[x + 1] for x in range(len(q_list) - 1)]

    q_average = np.cumsum(q_list) / range(1, len(q_list) + 1)
    q_series = pd.Series(q_list)
    q_min = q_series.cummin().values
    q_max = q_series.cummax().values
    q_carry_average = [
        q_average[x] - q_list[x + 1] for x in range(len(q_list) - 1)
    ]
    q_carry_max = [q_max[x] - q_list[x + 1] for x in range(len(q_list) - 1)]
    q_carry_min = [q_min[x] - q_list[x + 1] for x in range(len(q_list) - 1)]

    reward_risk = [
        5 * carry[x] /
        ((front_tr_dte[x + 1] - front_tr_dte[x]) * abs(downside[x + 1]))
        if carry[x] > 0 else 5 * carry[x] /
        ((front_tr_dte[x + 1] - front_tr_dte[x]) * upside[x + 1])
        for x in range(len(carry))
    ]

    return pd.DataFrame.from_dict({
        'ticker1':
        ticker1_list,
        'ticker2':
        ticker2_list,
        'ticker1L': [''] + ticker1_list[:-1],
        'ticker2L': [''] + ticker2_list[:-1],
        'ticker_head':
        cmi.get_contract_specs(ticker_list[0])['ticker_head'],
        'front_tr_dte':
        front_tr_dte,
        'front_tr_dteL': [np.NAN] + front_tr_dte[:-1],
        'carry': [np.NAN] + carry,
        'q_carry': [np.NAN] + q_carry,
        'q_carry_average': [np.NAN] + q_carry_average,
        'q_carry_max': [np.NAN] + q_carry_max,
        'q_carry_min': [np.NAN] + q_carry_min,
        'butterfly_q': [np.NAN] + butterfly_q_list,
        'butterfly_z': [np.NAN] + butterfly_z_list,
        'reward_risk': [np.NAN] + reward_risk,
        'price':
        price_current_list,
        'priceL': [np.NAN] + price_current_list[:-1],
        'butterfly_q10': [np.NAN] + butterfly_q10,
        'butterfly_q25': [np.NAN] + butterfly_q25,
        'butterfly_q35': [np.NAN] + butterfly_q35,
        'butterfly_q50': [np.NAN] + butterfly_q50,
        'butterfly_q65': [np.NAN] + butterfly_q65,
        'butterfly_q75': [np.NAN] + butterfly_q75,
        'butterfly_q90': [np.NAN] + butterfly_q90,
        'butterfly_mean': [np.NAN] + butterfly_mean_list,
        'butterfly_noise': [np.NAN] + butterfly_noise_list,
        'q':
        q_list,
        'upside':
        upside,
        'downside':
        downside,
        'upsideL': [np.NAN] + upside[:-1],
        'downsideL': [np.NAN] + downside[:-1],
        'change5':
        change5,
        'change10':
        change10,
        'change20':
        change20
    })
def get_fm_signals(**kwargs):

    ticker_head = kwargs['ticker_head']
    date_to = kwargs['date_to']

    #print(ticker_head)

    ticker_class = cmi.ticker_class[ticker_head]

    datetime_to = cu.convert_doubledate_2datetime(date_to)

    date5_years_ago = cu.doubledate_shift(date_to,5*365)
    datetime5_years_ago = cu.convert_doubledate_2datetime(date5_years_ago)

    data_out = gfp.get_futures_price_preloaded(ticker_head=ticker_head,settle_date_to=datetime_to)

    data4day = data_out[data_out['settle_date']==datetime_to]
    data4day = data4day[data4day['tr_dte']>=20]

    if len(data4day.index)<2:
        return {'ticker': '', 'comm_net': np.nan, 'spec_net': np.nan,
            'comm_cot_index_slow': np.nan, 'comm_cot_index_fast': np.nan, 'trend_direction': np.nan,'curve_slope': np.nan,
            'rsi_3': np.nan, 'rsi_7': np.nan, 'rsi_14': np.nan,
            'change1': np.nan,
            'change1_instant': np.nan,
            'change5': np.nan,
            'change10': np.nan,
            'change20': np.nan,
            'change1_dollar': np.nan,
            'change1_instant_dollar': np.nan,
            'change5_dollar': np.nan,
            'change10_dollar': np.nan,
            'change20_dollar': np.nan}

    data4day.sort_values('volume', ascending=False, inplace=True)
    data4day = data4day.iloc[:2]

    data4day.sort_values('tr_dte',ascending=True,inplace=True)

    ticker1 = data4day['ticker'].iloc[0]
    ticker2 = data4day['ticker'].iloc[1]

    tr_dte_list = [data4day['tr_dte'].iloc[0], data4day['tr_dte'].iloc[1]]

    amcb_output = opUtil.get_aggregation_method_contracts_back({'ticker_head': ticker_head, 'ticker_class': cmi.ticker_class[ticker_head]})
    aggregation_method = amcb_output['aggregation_method']
    contracts_back = amcb_output['contracts_back']

    futures_data_dictionary = {ticker_head: data_out}

    aligned_output = opUtil.get_aligned_futures_data(contract_list=[ticker1,ticker2],
                                                          tr_dte_list=tr_dte_list,
                                                          aggregation_method=aggregation_method,
                                                          contracts_back=contracts_back,
                                                          date_to=date_to,
                                                          futures_data_dictionary=futures_data_dictionary,
                                                          use_last_as_current=False)

    aligned_data = aligned_output['aligned_data']
    current_data = aligned_output['current_data']

    yield1_current = 100*(current_data['c1']['close_price']-current_data['c2']['close_price'])/current_data['c2']['close_price']
    yield1 = 100*(aligned_data['c1']['close_price']-aligned_data['c2']['close_price'])/aligned_data['c2']['close_price']

    last5_years_indx = aligned_data['settle_date']>=datetime5_years_ago

    yield1_last5_years = yield1[last5_years_indx]
    curve_slope = stats.get_quantile_from_number({'x':yield1_current,'y': yield1_last5_years})

    ticker_head_data = gfp.get_futures_price_preloaded(ticker_head=ticker_head)
    ticker_head_data = ticker_head_data[ticker_head_data['settle_date'] <= datetime_to]

    if ticker_class in ['Index', 'FX', 'Metal', 'Treasury', 'STIR']:

        merged_data = ticker_head_data[ticker_head_data['tr_dte'] >= 10]
        merged_data.sort_values(['settle_date', 'tr_dte'],ascending=True,inplace=True)
        merged_data.drop_duplicates(subset=['settle_date'], keep='first', inplace=True)

        merged_data['ma200'] = merged_data['close_price'].rolling(200).mean()
        merged_data['ma200_10'] = merged_data['ma200']-merged_data['ma200'].shift(10)
    else:
        data_out_front = ticker_head_data[ticker_head_data['tr_dte'] <= 60]
        data_out_front.drop_duplicates(subset=['settle_date'], keep='last', inplace=True)

        data_out_back = ticker_head_data[ticker_head_data['tr_dte'] > 60]
        data_out_back.drop_duplicates(subset=['settle_date'], keep='last', inplace=True)

        merged_data = pd.merge(data_out_front[['settle_date','tr_dte','close_price']],data_out_back[['tr_dte','close_price','settle_date','ticker','change_1']],how='inner',on='settle_date')
        merged_data['const_mat']=((merged_data['tr_dte_y']-60)*merged_data['close_price_x']+
                                  (60-merged_data['tr_dte_x'])*merged_data['close_price_y'])/\
                                 (merged_data['tr_dte_y']-merged_data['tr_dte_x'])

        merged_data['ma200'] = merged_data['const_mat'].rolling(200).mean()
        merged_data['ma200_10'] = merged_data['ma200']-merged_data['ma200'].shift(10)

    merged_data = merged_data[merged_data['settle_date']==datetime_to]

    if len(merged_data.index) == 0:
        trend_direction = np.nan
    elif merged_data['ma200_10'].iloc[0]>=0:
        trend_direction = 1
    else:
        trend_direction = -1

    ticker_data = gfp.get_futures_price_preloaded(ticker=ticker2,settle_date_to=datetime_to)

    ticker_data = ti.rsi(data_frame_input=ticker_data, change_field='change_1', period=3)
    ticker_data = ti.rsi(data_frame_input=ticker_data, change_field='change_1', period=7)
    ticker_data = ti.rsi(data_frame_input=ticker_data, change_field='change_1', period=14)

    cot_output = cot.get_cot_data(ticker_head=ticker_head, date_to=date_to)

    daily_noise = np.std(ticker_data['change_1'].iloc[-60:])

    if len(cot_output.index)>0:

        if ticker_class in ['FX','STIR','Index','Treasury']:

            cot_output['comm_long'] = cot_output['Asset Manager Longs']+cot_output['Dealer Longs']
            cot_output['comm_short'] = cot_output['Asset Manager Shorts']+cot_output['Dealer Shorts']
            cot_output['comm_net'] = cot_output['comm_long']-cot_output['comm_short']

            cot_output['spec_long'] = cot_output['Leveraged Funds Longs']
            cot_output['spec_short'] = cot_output['Leveraged Funds Shorts']
            cot_output['spec_net'] = cot_output['spec_long']-cot_output['spec_short']

        else:
            cot_output['comm_long'] = cot_output['Producer/Merchant/Processor/User Longs']+cot_output['Swap Dealer Longs']
            cot_output['comm_short'] = cot_output['Producer/Merchant/Processor/User Shorts']+cot_output['Swap Dealer Shorts']
            cot_output['comm_net'] = cot_output['comm_long']-cot_output['comm_short']
            cot_output['spec_long'] = cot_output['Money Manager Longs']+cot_output['Other Reportable Longs']
            cot_output['spec_short'] = cot_output['Money Manager Shorts']+cot_output['Other Reportable Shorts']
            cot_output['spec_net'] = cot_output['spec_long']-cot_output['spec_short']

        if (datetime_to-cot_output['settle_date'].iloc[-1]).days>=10:
            comm_net = np.nan
            spec_net = np.nan
        else:
            comm_net = cot_output['comm_net'].iloc[-1]
            spec_net = cot_output['spec_net'].iloc[-1]

        comm_net_min_slow = cot_output['comm_net'].iloc[-156:].min()
        comm_net_max_slow = cot_output['comm_net'].iloc[-156:].max()

        comm_cot_index_slow = 100*(comm_net-comm_net_min_slow)/(comm_net_max_slow-comm_net_min_slow)

        comm_net_min_fast = cot_output['comm_net'].iloc[-52:].min()
        comm_net_max_fast = cot_output['comm_net'].iloc[-52:].max()

        comm_cot_index_fast = 100*(comm_net-comm_net_min_fast)/(comm_net_max_fast-comm_net_min_fast)

    else:
        comm_net = np.nan
        spec_net = np.nan
        comm_cot_index_slow = np.nan
        comm_cot_index_fast = np.nan

    contract_multiplier = cmi.contract_multiplier[ticker_head]

    return {'ticker': ticker2, 'comm_net': comm_net, 'spec_net': spec_net,
            'comm_cot_index_slow': comm_cot_index_slow, 'comm_cot_index_fast': comm_cot_index_fast, 'trend_direction': trend_direction,'curve_slope': curve_slope,
            'rsi_3': ticker_data['rsi_3'].iloc[-1], 'rsi_7': ticker_data['rsi_7'].iloc[-1], 'rsi_14': ticker_data['rsi_14'].iloc[-1],
            'change1': ticker_data['change1'].iloc[-1]/daily_noise,
            'change1_instant': ticker_data['change1_instant'].iloc[-1]/daily_noise,
            'change5': ticker_data['change5'].iloc[-1]/daily_noise,
            'change10': ticker_data['change10'].iloc[-1]/daily_noise,
            'change20': ticker_data['change20'].iloc[-1]/daily_noise,
            'change1_dollar': ticker_data['change1'].iloc[-1]*contract_multiplier,
            'change1_instant_dollar': ticker_data['change1_instant'].iloc[-1]*contract_multiplier,
            'change5_dollar': ticker_data['change5'].iloc[-1]*contract_multiplier,
            'change10_dollar': ticker_data['change10'].iloc[-1]*contract_multiplier,
            'change20_dollar': ticker_data['change20'].iloc[-1]*contract_multiplier}
示例#3
0
def get_futures_butterfly_signals(**kwargs):

    ticker_list = kwargs['ticker_list']
    date_to = kwargs['date_to']

    if 'tr_dte_list' in kwargs.keys():
        tr_dte_list = kwargs['tr_dte_list']
    else:
        tr_dte_list = [
            exp.get_futures_days2_expiration({
                'ticker': x,
                'date_to': date_to
            }) for x in ticker_list
        ]

    if 'aggregation_method' in kwargs.keys(
    ) and 'contracts_back' in kwargs.keys():
        aggregation_method = kwargs['aggregation_method']
        contracts_back = kwargs['contracts_back']
    else:
        amcb_output = opUtil.get_aggregation_method_contracts_back(
            cmi.get_contract_specs(ticker_list[0]))
        aggregation_method = amcb_output['aggregation_method']
        contracts_back = amcb_output['contracts_back']

    if 'use_last_as_current' in kwargs.keys():
        use_last_as_current = kwargs['use_last_as_current']
    else:
        use_last_as_current = False

    if 'futures_data_dictionary' in kwargs.keys():
        futures_data_dictionary = kwargs['futures_data_dictionary']
    else:
        futures_data_dictionary = {
            x: gfp.get_futures_price_preloaded(ticker_head=x)
            for x in [cmi.get_contract_specs(ticker_list[0])['ticker_head']]
        }

    if 'contract_multiplier' in kwargs.keys():
        contract_multiplier = kwargs['contract_multiplier']
    else:
        contract_multiplier = cmi.contract_multiplier[cmi.get_contract_specs(
            ticker_list[0])['ticker_head']]

    if 'datetime5_years_ago' in kwargs.keys():
        datetime5_years_ago = kwargs['datetime5_years_ago']
    else:
        date5_years_ago = cu.doubledate_shift(date_to, 5 * 365)
        datetime5_years_ago = cu.convert_doubledate_2datetime(date5_years_ago)

    if 'datetime2_months_ago' in kwargs.keys():
        datetime2_months_ago = kwargs['datetime2_months_ago']
    else:
        date2_months_ago = cu.doubledate_shift(date_to, 60)
        datetime2_months_ago = cu.convert_doubledate_2datetime(
            date2_months_ago)

    aligned_output = opUtil.get_aligned_futures_data(
        contract_list=ticker_list,
        tr_dte_list=tr_dte_list,
        aggregation_method=aggregation_method,
        contracts_back=contracts_back,
        date_to=date_to,
        futures_data_dictionary=futures_data_dictionary,
        use_last_as_current=use_last_as_current)
    if not aligned_output['success']:
        return {'success': False}

    current_data = aligned_output['current_data']
    aligned_data = aligned_output['aligned_data']

    month_diff_1 = 12 * (current_data['c1']['ticker_year'] -
                         current_data['c2']['ticker_year']) + (
                             current_data['c1']['ticker_month'] -
                             current_data['c2']['ticker_month'])
    month_diff_2 = 12 * (current_data['c2']['ticker_year'] -
                         current_data['c3']['ticker_year']) + (
                             current_data['c2']['ticker_month'] -
                             current_data['c3']['ticker_month'])

    weight_11 = 2 * month_diff_2 / (month_diff_1 + month_diff_1)
    weight_12 = -2
    weight_13 = 2 * month_diff_1 / (month_diff_1 + month_diff_1)

    price_1 = current_data['c1']['close_price']
    price_2 = current_data['c2']['close_price']
    price_3 = current_data['c3']['close_price']

    linear_interp_price2 = (weight_11 * aligned_data['c1']['close_price'] +
                            weight_13 * aligned_data['c3']['close_price']) / 2

    butterfly_price = aligned_data['c1']['close_price'] - 2 * aligned_data[
        'c2']['close_price'] + aligned_data['c3']['close_price']

    price_ratio = linear_interp_price2 / aligned_data['c2']['close_price']

    linear_interp_price2_current = (weight_11 * price_1 +
                                    weight_13 * price_3) / 2

    price_ratio_current = linear_interp_price2_current / price_2

    q = stats.get_quantile_from_number({
        'x':
        price_ratio_current,
        'y':
        price_ratio.values,
        'clean_num_obs':
        max(100, round(3 * len(price_ratio.values) / 4))
    })
    qf = stats.get_quantile_from_number({
        'x': price_ratio_current,
        'y': price_ratio.values[-40:],
        'clean_num_obs': 30
    })

    recent_quantile_list = [
        stats.get_quantile_from_number({
            'x': x,
            'y': price_ratio.values[-40:],
            'clean_num_obs': 30
        }) for x in price_ratio.values[-40:]
    ]

    weight1 = weight_11
    weight2 = weight_12
    weight3 = weight_13

    last5_years_indx = aligned_data['settle_date'] >= datetime5_years_ago
    last2_months_indx = aligned_data['settle_date'] >= datetime2_months_ago
    data_last5_years = aligned_data[last5_years_indx]

    yield1 = 100 * (
        aligned_data['c1']['close_price'] -
        aligned_data['c2']['close_price']) / aligned_data['c2']['close_price']
    yield2 = 100 * (
        aligned_data['c2']['close_price'] -
        aligned_data['c3']['close_price']) / aligned_data['c3']['close_price']

    yield1_last5_years = yield1[last5_years_indx]
    yield2_last5_years = yield2[last5_years_indx]

    yield1_current = 100 * (
        current_data['c1']['close_price'] -
        current_data['c2']['close_price']) / current_data['c2']['close_price']
    yield2_current = 100 * (
        current_data['c2']['close_price'] -
        current_data['c3']['close_price']) / current_data['c3']['close_price']

    butterfly_price_current = current_data['c1']['close_price']\
                            -2*current_data['c2']['close_price']\
                              +current_data['c3']['close_price']

    #return {'yield1': yield1, 'yield2': yield2, 'yield1_current':yield1_current, 'yield2_current': yield2_current}

    yield_regress_output = stats.get_regression_results({
        'x':
        yield2,
        'y':
        yield1,
        'x_current':
        yield2_current,
        'y_current':
        yield1_current,
        'clean_num_obs':
        max(100, round(3 * len(yield1.values) / 4))
    })

    yield_regress_output_last5_years = stats.get_regression_results({
        'x':
        yield2_last5_years,
        'y':
        yield1_last5_years,
        'x_current':
        yield2_current,
        'y_current':
        yield1_current,
        'clean_num_obs':
        max(100, round(3 * len(yield1_last5_years.values) / 4))
    })

    bf_qz_frame_short = pd.DataFrame()
    bf_qz_frame_long = pd.DataFrame()

    if (len(yield1) >= 40) & (len(yield2) >= 40):

        recent_zscore_list = [
            (yield1[-40 + i] - yield_regress_output['alpha'] -
             yield_regress_output['beta'] * yield2[-40 + i]) /
            yield_regress_output['residualstd'] for i in range(40)
        ]

        bf_qz_frame = pd.DataFrame.from_dict({
            'bf_price':
            butterfly_price.values[-40:],
            'q':
            recent_quantile_list,
            'zscore':
            recent_zscore_list
        })

        bf_qz_frame = np.round(bf_qz_frame, 8)
        bf_qz_frame.drop_duplicates(['bf_price'], keep='last', inplace=True)

        # return bf_qz_frame

        bf_qz_frame_short = bf_qz_frame[(bf_qz_frame['zscore'] >= 0.6)
                                        & (bf_qz_frame['q'] >= 85)]
        bf_qz_frame_long = bf_qz_frame[(bf_qz_frame['zscore'] <= -0.6)
                                       & (bf_qz_frame['q'] <= 12)]

    if bf_qz_frame_short.empty:
        short_price_limit = np.NAN
    else:
        short_price_limit = bf_qz_frame_short['bf_price'].min()

    if bf_qz_frame_long.empty:
        long_price_limit = np.NAN
    else:
        long_price_limit = bf_qz_frame_long['bf_price'].max()

    zscore1 = yield_regress_output['zscore']
    rsquared1 = yield_regress_output['rsquared']

    zscore2 = yield_regress_output_last5_years['zscore']
    rsquared2 = yield_regress_output_last5_years['rsquared']

    second_spread_weight_1 = yield_regress_output['beta']
    second_spread_weight_2 = yield_regress_output_last5_years['beta']

    butterfly_5_change = data_last5_years['c1']['change_5']\
                             - (1+second_spread_weight_1)*data_last5_years['c2']['change_5']\
                             + second_spread_weight_1*data_last5_years['c3']['change_5']

    butterfly_5_change_current = current_data['c1']['change_5']\
                             - (1+second_spread_weight_1)*current_data['c2']['change_5']\
                             + second_spread_weight_1*current_data['c3']['change_5']

    butterfly_1_change = data_last5_years['c1']['change_1']\
                             - (1+second_spread_weight_1)*data_last5_years['c2']['change_1']\
                             + second_spread_weight_1*data_last5_years['c3']['change_1']

    percentile_vector = stats.get_number_from_quantile(
        y=butterfly_5_change.values,
        quantile_list=[1, 15, 85, 99],
        clean_num_obs=max(100, round(3 * len(butterfly_5_change.values) / 4)))

    downside = contract_multiplier * (percentile_vector[0] +
                                      percentile_vector[1]) / 2
    upside = contract_multiplier * (percentile_vector[2] +
                                    percentile_vector[3]) / 2
    recent_5day_pnl = contract_multiplier * butterfly_5_change_current

    residuals = yield1 - yield_regress_output[
        'alpha'] - yield_regress_output['beta'] * yield2

    regime_change_ind = (residuals[last5_years_indx].mean() -
                         residuals.mean()) / residuals.std()

    seasonal_residuals = residuals[aligned_data['c1']['ticker_month'] ==
                                   current_data['c1']['ticker_month']]
    seasonal_clean_residuals = seasonal_residuals[np.isfinite(
        seasonal_residuals)]
    clean_residuals = residuals[np.isfinite(residuals)]

    contract_seasonality_ind = (
        seasonal_clean_residuals.mean() -
        clean_residuals.mean()) / clean_residuals.std()

    yield1_quantile_list = stats.get_number_from_quantile(
        y=yield1, quantile_list=[10, 90])
    yield2_quantile_list = stats.get_number_from_quantile(
        y=yield2, quantile_list=[10, 90])

    noise_ratio = (yield1_quantile_list[1] - yield1_quantile_list[0]) / (
        yield2_quantile_list[1] - yield2_quantile_list[0])

    daily_noise_recent = stats.get_stdev(x=butterfly_1_change.values[-20:],
                                         clean_num_obs=15)
    daily_noise_past = stats.get_stdev(
        x=butterfly_1_change.values,
        clean_num_obs=max(100, round(3 * len(butterfly_1_change.values) / 4)))

    recent_vol_ratio = daily_noise_recent / daily_noise_past

    alpha1 = yield_regress_output['alpha']

    residuals_last5_years = residuals[last5_years_indx]
    residuals_last2_months = residuals[last2_months_indx]

    residual_current = yield1_current - alpha1 - second_spread_weight_1 * yield2_current

    z3 = (residual_current - residuals_last5_years.mean()) / residuals.std()
    z4 = (residual_current - residuals_last2_months.mean()) / residuals.std()

    yield_change = (alpha1 + second_spread_weight_1 * yield2_current -
                    yield1_current) / (1 + second_spread_weight_1)

    new_yield1 = yield1_current + yield_change
    new_yield2 = yield2_current - yield_change

    price_change1 = 100 * (
        (price_2 * (new_yield1 + 100) / 100) - price_1) / (200 + new_yield1)
    price_change2 = 100 * (
        (price_3 * (new_yield2 + 100) / 100) - price_2) / (200 + new_yield2)

    theo_pnl = contract_multiplier * (
        2 * price_change1 - 2 * second_spread_weight_1 * price_change2)

    aligned_data['residuals'] = residuals
    aligned_output['aligned_data'] = aligned_data

    grouped = aligned_data.groupby(aligned_data['c1']['cont_indx'])
    aligned_data['shifted_residuals'] = grouped['residuals'].shift(-5)
    aligned_data['residual_change'] = aligned_data[
        'shifted_residuals'] - aligned_data['residuals']

    mean_reversion = stats.get_regression_results({
        'x':
        aligned_data['residuals'].values,
        'y':
        aligned_data['residual_change'].values,
        'clean_num_obs':
        max(100, round(3 * len(yield1.values) / 4))
    })

    theo_spread_move_output = su.calc_theo_spread_move_from_ratio_normalization(
        ratio_time_series=price_ratio.values[-40:],
        starting_quantile=qf,
        num_price=linear_interp_price2_current,
        den_price=current_data['c2']['close_price'],
        favorable_quantile_move_list=[5, 10, 15, 20, 25])

    theo_pnl_list = [
        x * contract_multiplier * 2
        for x in theo_spread_move_output['theo_spread_move_list']
    ]

    return {
        'success': True,
        'aligned_output': aligned_output,
        'q': q,
        'qf': qf,
        'theo_pnl_list': theo_pnl_list,
        'ratio_target_list': theo_spread_move_output['ratio_target_list'],
        'weight1': weight1,
        'weight2': weight2,
        'weight3': weight3,
        'zscore1': zscore1,
        'rsquared1': rsquared1,
        'zscore2': zscore2,
        'rsquared2': rsquared2,
        'zscore3': z3,
        'zscore4': z4,
        'zscore5': zscore1 - regime_change_ind,
        'zscore6': zscore1 - contract_seasonality_ind,
        'zscore7': zscore1 - regime_change_ind - contract_seasonality_ind,
        'theo_pnl': theo_pnl,
        'regime_change_ind': regime_change_ind,
        'contract_seasonality_ind': contract_seasonality_ind,
        'second_spread_weight_1': second_spread_weight_1,
        'second_spread_weight_2': second_spread_weight_2,
        'downside': downside,
        'upside': upside,
        'yield1': yield1,
        'yield2': yield2,
        'yield1_current': yield1_current,
        'yield2_current': yield2_current,
        'bf_price': butterfly_price_current,
        'short_price_limit': short_price_limit,
        'long_price_limit': long_price_limit,
        'noise_ratio': noise_ratio,
        'alpha1': alpha1,
        'alpha2': yield_regress_output_last5_years['alpha'],
        'residual_std1': yield_regress_output['residualstd'],
        'residual_std2': yield_regress_output_last5_years['residualstd'],
        'recent_vol_ratio': recent_vol_ratio,
        'recent_5day_pnl': recent_5day_pnl,
        'price_1': price_1,
        'price_2': price_2,
        'price_3': price_3,
        'last5_years_indx': last5_years_indx,
        'price_ratio': price_ratio,
        'mean_reversion_rsquared': mean_reversion['rsquared'],
        'mean_reversion_signif': (mean_reversion['conf_int'][1, :] < 0).all()
    }
def get_vcs_signals_legacy(**kwargs):

    aligned_indicators_output = get_aligned_option_indicators_legacy(**kwargs)

    if not aligned_indicators_output['success']:
        return {'atm_vol_ratio': np.NaN, 'q': np.NaN, 'q2': np.NaN, 'q1': np.NaN, 'q5': np.NaN,
            'fwd_vol_q': np.NaN, 'fwd_vol_q2': np.NaN, 'fwd_vol_q1': np.NaN, 'fwd_vol_q5': np.NaN,
             'atm_real_vol_ratio': np.NaN, 'q_atm_real_vol_ratio': np.NaN,
             'atm_real_vol_ratio_ratio': np.NaN, 'q_atm_real_vol_ratio_ratio': np.NaN,
             'tr_dte_diff_percent': np.NaN,'downside': np.NaN, 'upside': np.NaN, 'theta1': np.NaN, 'theta2': np.NaN, 'hist': []}

    hist = aligned_indicators_output['hist']
    current = aligned_indicators_output['current']
    settle_datetime = cu.convert_doubledate_2datetime(kwargs['settle_date'])

    settle_datetime_1year_back = settle_datetime-dt.timedelta(360)
    settle_datetime_5year_back = settle_datetime-dt.timedelta(5*360)

    hist['atm_vol_ratio'] = hist['c1']['imp_vol']/hist['c2']['imp_vol']

    fwd_var = hist['c2']['cal_dte']*(hist['c2']['imp_vol']**2)-hist['c1']['cal_dte']*(hist['c1']['imp_vol']**2)
    fwd_vol_sq = fwd_var/(hist['c2']['cal_dte']-hist['c1']['cal_dte'])
    fwd_vol_adj = np.sign(fwd_vol_sq)*((abs(fwd_vol_sq)).apply(np.sqrt))
    hist['fwd_vol_adj'] = fwd_vol_adj

    fwd_var = current['cal_dte'][1]*(current['imp_vol'][1]**2)-current['cal_dte'][0]*(current['imp_vol'][0]**2)
    fwd_vol_sq = fwd_var/(current['cal_dte'][1]-current['cal_dte'][0])
    fwd_vol_adj = np.sign(fwd_vol_sq)*(np.sqrt(abs(fwd_vol_sq)))

    atm_vol_ratio = current['imp_vol'][0]/current['imp_vol'][1]

    hist['atm_real_vol_ratio'] = hist['c1']['imp_vol']/hist['c1']['close2close_vol20']
    atm_real_vol_ratio = current['imp_vol'][0]/current['close2close_vol20'][0]

    hist['atm_real_vol_ratio_ratio'] = (hist['c1']['imp_vol']/hist['c1']['close2close_vol20'])/(hist['c2']['imp_vol']/hist['c2']['close2close_vol20'])
    atm_real_vol_ratio_ratio = (current['imp_vol'][0]/current['close2close_vol20'][0])/(current['imp_vol'][0]/current['close2close_vol20'][0])

    hist_1year = hist[hist.index >= settle_datetime_1year_back]
    hist_5year = hist[hist.index >= settle_datetime_5year_back]

    q = stats.get_quantile_from_number({'x': atm_vol_ratio,
                                        'y': hist['atm_vol_ratio'].values, 'clean_num_obs': max(100, round(3*len(hist.index)/4))})

    q2 = stats.get_quantile_from_number({'x': atm_vol_ratio, 'y': hist['atm_vol_ratio'].values[-40:], 'clean_num_obs': 30})

    q1 = stats.get_quantile_from_number({'x': atm_vol_ratio,
                                        'y': hist_1year['atm_vol_ratio'].values, 'clean_num_obs': max(50, round(3*len(hist_1year.index)/4))})

    q5 = stats.get_quantile_from_number({'x': atm_vol_ratio,
                                        'y': hist_5year['atm_vol_ratio'].values, 'clean_num_obs': max(100, round(3*len(hist_5year.index)/4))})

    fwd_vol_q = stats.get_quantile_from_number({'x': fwd_vol_adj,
                                                'y': hist['fwd_vol_adj'].values, 'clean_num_obs': max(100, round(3*len(hist.index)/4))})

    fwd_vol_q2 = stats.get_quantile_from_number({'x': fwd_vol_adj,
                                                 'y': hist['fwd_vol_adj'].values[-40:], 'clean_num_obs': 30})

    fwd_vol_q1 = stats.get_quantile_from_number({'x': fwd_vol_adj,
                                                 'y': hist_1year['fwd_vol_adj'].values, 'clean_num_obs': max(50, round(3*len(hist_1year.index)/4))})

    fwd_vol_q5 = stats.get_quantile_from_number({'x': fwd_vol_adj,
                                                 'y': hist_5year['fwd_vol_adj'].values, 'clean_num_obs': max(100, round(3*len(hist_5year.index)/4))})

    q_atm_real_vol_ratio = stats.get_quantile_from_number({'x': atm_real_vol_ratio,
                                                           'y': hist['atm_real_vol_ratio'].values, 'clean_num_obs': max(100, round(3*len(hist.index)/4))})

    q_atm_real_vol_ratio_ratio = stats.get_quantile_from_number({'x': atm_real_vol_ratio_ratio,
                                                                 'y': hist['atm_real_vol_ratio_ratio'].values, 'clean_num_obs': max(100, round(3*len(hist.index)/4))})

    tr_dte_diff_percent = round(100*(current['tr_dte'][1]-current['tr_dte'][0])/current['tr_dte'][0])

    profit5 = hist['c1']['profit5']-hist['c2']['profit5']

    clean_indx = profit5.notnull()
    clean_data = hist[clean_indx]

    if clean_data.empty:
        downside = np.NaN
        upside = np.NaN
    else:
        last_available_align_date = clean_data.index[-1]
        clean_data = clean_data[clean_data.index >= last_available_align_date-dt.timedelta(5*365)]
        profit5 = clean_data['c1']['profit5']-clean_data['c2']['profit5']

        percentile_vector = stats.get_number_from_quantile(y=profit5.values,
                                                       quantile_list=[1, 15, 85, 99],
                                                       clean_num_obs=max(100, round(3*len(profit5.values)/4)))

        downside = (percentile_vector[0]+percentile_vector[1])/2
        upside = (percentile_vector[2]+percentile_vector[3])/2

    return {'atm_vol_ratio': atm_vol_ratio, 'q': q, 'q2': q2, 'q1': q1, 'q5': q5,
            'fwd_vol_q': fwd_vol_q, 'fwd_vol_q2': fwd_vol_q2, 'fwd_vol_q1': fwd_vol_q1, 'fwd_vol_q5': fwd_vol_q5,
             'atm_real_vol_ratio': atm_real_vol_ratio, 'q_atm_real_vol_ratio': q_atm_real_vol_ratio,
             'atm_real_vol_ratio_ratio': atm_real_vol_ratio_ratio, 'q_atm_real_vol_ratio_ratio': q_atm_real_vol_ratio_ratio,
            'tr_dte_diff_percent': tr_dte_diff_percent, 'downside': downside, 'upside': upside, 'theta1': current['theta'][0], 'theta2': current['theta'][1], 'hist': hist}
def get_vcs_signals(**kwargs):

    aligned_indicators_output = get_aligned_option_indicators(**kwargs)

    if not aligned_indicators_output['success']:
        return {
            'hist': [],
            'current': [],
            'atm_vol_ratio': np.NaN,
            'fwd_vol': np.NaN,
            'downside': np.NaN,
            'upside': np.NaN,
            'real_vol_ratio': np.NaN,
            'atm_real_vol_ratio': np.NaN,
            'theta': np.NaN,
            'q': np.NaN,
            'q1': np.NaN,
            'fwd_vol_q': np.NaN
        }

    hist = aligned_indicators_output['hist']
    current = aligned_indicators_output['current']

    settle_datetime = cu.convert_doubledate_2datetime(kwargs['settle_date'])
    settle_datetime_1year_back = settle_datetime - dt.timedelta(360)

    hist['atm_vol_ratio'] = hist['c1']['imp_vol'] / hist['c2']['imp_vol']

    if 'atm_vol_ratio' in kwargs.keys():
        atm_vol_ratio = kwargs['atm_vol_ratio']
    else:
        atm_vol_ratio = current['imp_vol'][0] / current['imp_vol'][1]

    hist_1year = hist[hist.index >= settle_datetime_1year_back]

    q = stats.get_quantile_from_number({
        'x':
        atm_vol_ratio,
        'y':
        hist['atm_vol_ratio'].to_numpy(),
        'clean_num_obs':
        max(100, round(3 * len(hist.index) / 4))
    })

    q1 = stats.get_quantile_from_number({
        'x':
        atm_vol_ratio,
        'y':
        hist_1year['atm_vol_ratio'].to_numpy(),
        'clean_num_obs':
        max(50, round(3 * len(hist_1year.index) / 4))
    })

    fwd_var = hist['c2']['cal_dte'] * (hist['c2']['imp_vol']**
                                       2) - hist['c1']['cal_dte'] * (
                                           hist['c1']['imp_vol']**2)
    fwd_vol_sq = fwd_var / (hist['c2']['cal_dte'] - hist['c1']['cal_dte'])
    fwd_vol_adj = np.sign(fwd_vol_sq) * ((abs(fwd_vol_sq)).apply(np.sqrt))
    hist['fwd_vol_adj'] = fwd_vol_adj

    fwd_var = current['cal_dte'][1] * (current['imp_vol'][1]**
                                       2) - current['cal_dte'][0] * (
                                           current['imp_vol'][0]**2)
    fwd_vol_sq = fwd_var / (current['cal_dte'][1] - current['cal_dte'][0])
    fwd_vol_adj = np.sign(fwd_vol_sq) * (np.sqrt(abs(fwd_vol_sq)))

    fwd_vol_q = stats.get_quantile_from_number({
        'x':
        fwd_vol_adj,
        'y':
        hist['fwd_vol_adj'].to_numpy(),
        'clean_num_obs':
        max(100, round(3 * len(hist.index) / 4))
    })

    clean_indx = hist['c1']['profit5'].notnull()
    clean_data = hist[clean_indx]

    if clean_data.empty:
        downside = np.NaN
        upside = np.NaN
    else:
        last_available_align_date = clean_data.index[-1]
        clean_data = clean_data[clean_data.index >= last_available_align_date -
                                dt.timedelta(5 * 365)]
        profit5 = clean_data['c1']['profit5'] - clean_data['c2']['profit5']

        percentile_vector = stats.get_number_from_quantile(
            y=profit5.to_numpy(),
            quantile_list=[1, 15, 85, 99],
            clean_num_obs=max(100, round(3 * len(profit5.to_numpy()) / 4)))

        downside = (percentile_vector[0] + percentile_vector[1]) / 2
        upside = (percentile_vector[2] + percentile_vector[3]) / 2

    return {
        'hist':
        hist,
        'current':
        current,
        'atm_vol_ratio':
        atm_vol_ratio,
        'fwd_vol':
        fwd_vol_adj,
        'downside':
        downside,
        'upside':
        upside,
        'real_vol_ratio':
        current['close2close_vol20'][0] / current['close2close_vol20'][1],
        'atm_real_vol_ratio':
        current['imp_vol'][0] / current['close2close_vol20'][0],
        'theta':
        current['theta'][1] - current['theta'][0],
        'q':
        q,
        'q1':
        q1,
        'fwd_vol_q':
        fwd_vol_q
    }
def get_futures_spread_carry_signals(**kwargs):

    ticker_list = kwargs['ticker_list']
    date_to = kwargs['date_to']

    if 'tr_dte_list' in kwargs.keys():
        tr_dte_list = kwargs['tr_dte_list']
    else:
        tr_dte_list = [exp.get_futures_days2_expiration({'ticker': x,'date_to': date_to}) for x in ticker_list]

    if 'aggregation_method' in kwargs.keys() and 'contracts_back' in kwargs.keys():
        aggregation_method = kwargs['aggregation_method']
        contracts_back = kwargs['contracts_back']
    else:
        amcb_output = opUtil.get_aggregation_method_contracts_back(cmi.get_contract_specs(ticker_list[0]))
        aggregation_method = amcb_output['aggregation_method']
        contracts_back = amcb_output['contracts_back']

    if 'use_last_as_current' in kwargs.keys():
        use_last_as_current = kwargs['use_last_as_current']
    else:
        use_last_as_current = False

    if 'futures_data_dictionary' in kwargs.keys():
        futures_data_dictionary = kwargs['futures_data_dictionary']
    else:
        futures_data_dictionary = {x: gfp.get_futures_price_preloaded(ticker_head=x) for x in [cmi.get_contract_specs(ticker_list[0])['ticker_head']]}

    if 'contract_multiplier' in kwargs.keys():
        contract_multiplier = kwargs['contract_multiplier']
    else:
        contract_multiplier = cmi.contract_multiplier[cmi.get_contract_specs(ticker_list[0])['ticker_head']]

    if 'datetime5_years_ago' in kwargs.keys():
        datetime5_years_ago = kwargs['datetime5_years_ago']
    else:
        date5_years_ago = cu.doubledate_shift(date_to,5*365)
        datetime5_years_ago = cu.convert_doubledate_2datetime(date5_years_ago)

    if 'datetime2_months_ago' in kwargs.keys():
        datetime2_months_ago = kwargs['datetime2_months_ago']
    else:
        date2_months_ago = cu.doubledate_shift(date_to,60)
        datetime2_months_ago = cu.convert_doubledate_2datetime(date2_months_ago)

    aligned_output = opUtil.get_aligned_futures_data(contract_list=ticker_list,
                                                          tr_dte_list=tr_dte_list,
                                                          aggregation_method=aggregation_method,
                                                          contracts_back=contracts_back,
                                                          date_to=date_to,
                                                          futures_data_dictionary=futures_data_dictionary,
                                                          use_last_as_current=use_last_as_current)

    aligned_data = aligned_output['aligned_data']
    current_data = aligned_output['current_data']

    last5_years_indx = aligned_data['settle_date']>=datetime5_years_ago
    data_last5_years = aligned_data[last5_years_indx]

    ticker1_list = [current_data['c' + str(x+1)]['ticker'] for x in range(len(ticker_list)-1)]
    ticker2_list = [current_data['c' + str(x+2)]['ticker'] for x in range(len(ticker_list)-1)]
    yield_current_list = [100*(current_data['c' + str(x+1)]['close_price']-
                           current_data['c' + str(x+2)]['close_price'])/
                           current_data['c' + str(x+2)]['close_price']
                            for x in range(len(ticker_list)-1)]

    price_current_list = [current_data['c' + str(x+1)]['close_price']-current_data['c' + str(x+2)]['close_price']
                            for x in range(len(ticker_list)-1)]


    yield_history = [100*(aligned_data['c' + str(x+1)]['close_price']-
                           aligned_data['c' + str(x+2)]['close_price'])/
                           aligned_data['c' + str(x+2)]['close_price']
                            for x in range(len(ticker_list)-1)]

    change_5_history = [data_last5_years['c' + str(x+1)]['change_5']-
                           data_last5_years['c' + str(x+2)]['change_5']
                            for x in range(len(ticker_list)-1)]

    change5 = [contract_multiplier*(current_data['c' + str(x+1)]['change5']-
                           current_data['c' + str(x+2)]['change5'])
                            for x in range(len(ticker_list)-1)]

    change10 = [contract_multiplier*(current_data['c' + str(x+1)]['change10']-
                           current_data['c' + str(x+2)]['change10'])
                            for x in range(len(ticker_list)-1)]

    change20 = [contract_multiplier*(current_data['c' + str(x+1)]['change20']-
                           current_data['c' + str(x+2)]['change20'])
                            for x in range(len(ticker_list)-1)]

    front_tr_dte = [current_data['c' + str(x+1)]['tr_dte'] for x in range(len(ticker_list)-1)]

    q_list = [stats.get_quantile_from_number({'x': yield_current_list[x],
                                'y': yield_history[x].values,
                                'clean_num_obs': max(100, round(3*len(yield_history[x].values)/4))})
                                for x in range(len(ticker_list)-1)]

    percentile_vector = [stats.get_number_from_quantile(y=change_5_history[x].values,
                                                       quantile_list=[1, 15, 85, 99],
                                                       clean_num_obs=max(100, round(3*len(change_5_history[x].values)/4)))
                                                       for x in range(len(ticker_list)-1)]

    q1 = [x[0] for x in percentile_vector]
    q15 = [x[1] for x in percentile_vector]
    q85 = [x[2] for x in percentile_vector]
    q99 = [x[3] for x in percentile_vector]

    downside = [contract_multiplier*(q1[x]+q15[x])/2 for x in range(len(q1))]
    upside = [contract_multiplier*(q85[x]+q99[x])/2 for x in range(len(q1))]
    carry = [contract_multiplier*(price_current_list[x]-price_current_list[x+1]) for x in range(len(q_list)-1)]
    q_carry = [q_list[x]-q_list[x+1] for x in range(len(q_list)-1)]
    reward_risk = [5*carry[x]/((front_tr_dte[x+1]-front_tr_dte[x])*abs(downside[x+1])) if carry[x]>0
      else 5*carry[x]/((front_tr_dte[x+1]-front_tr_dte[x])*upside[x+1]) for x in range(len(carry))]

    return pd.DataFrame.from_items([('ticker1',ticker1_list),
                         ('ticker2',ticker2_list),
                         ('ticker_head',cmi.get_contract_specs(ticker_list[0])['ticker_head']),
                         ('front_tr_dte',front_tr_dte),
                         ('carry',[np.NAN]+carry),
                         ('q_carry',[np.NAN]+q_carry),
                         ('reward_risk',[np.NAN]+reward_risk),
                         ('price',price_current_list),
                         ('q',q_list),
                         ('upside',upside),
                         ('downside',downside),
                         ('change5',change5),
                         ('change10',change10),
                         ('change20',change20)])
def get_futures_butterfly_signals(**kwargs):

    ticker_list = kwargs['ticker_list']
    date_to = kwargs['date_to']

    if 'tr_dte_list' in kwargs.keys():
        tr_dte_list = kwargs['tr_dte_list']
    else:
        tr_dte_list = [exp.get_futures_days2_expiration({'ticker': x,'date_to': date_to}) for x in ticker_list]

    if 'aggregation_method' in kwargs.keys() and 'contracts_back' in kwargs.keys():
        aggregation_method = kwargs['aggregation_method']
        contracts_back = kwargs['contracts_back']
    else:
        amcb_output = opUtil.get_aggregation_method_contracts_back(cmi.get_contract_specs(ticker_list[0]))
        aggregation_method = amcb_output['aggregation_method']
        contracts_back = amcb_output['contracts_back']

    if 'use_last_as_current' in kwargs.keys():
        use_last_as_current = kwargs['use_last_as_current']
    else:
        use_last_as_current = False

    if 'futures_data_dictionary' in kwargs.keys():
        futures_data_dictionary = kwargs['futures_data_dictionary']
    else:
        futures_data_dictionary = {x: gfp.get_futures_price_preloaded(ticker_head=x) for x in [cmi.get_contract_specs(ticker_list[0])['ticker_head']]}

    if 'contract_multiplier' in kwargs.keys():
        contract_multiplier = kwargs['contract_multiplier']
    else:
        contract_multiplier = cmi.contract_multiplier[cmi.get_contract_specs(ticker_list[0])['ticker_head']]

    if 'datetime5_years_ago' in kwargs.keys():
        datetime5_years_ago = kwargs['datetime5_years_ago']
    else:
        date5_years_ago = cu.doubledate_shift(date_to,5*365)
        datetime5_years_ago = cu.convert_doubledate_2datetime(date5_years_ago)

    if 'datetime2_months_ago' in kwargs.keys():
        datetime2_months_ago = kwargs['datetime2_months_ago']
    else:
        date2_months_ago = cu.doubledate_shift(date_to,60)
        datetime2_months_ago = cu.convert_doubledate_2datetime(date2_months_ago)

    aligned_output = opUtil.get_aligned_futures_data(contract_list=ticker_list,
                                                          tr_dte_list=tr_dte_list,
                                                          aggregation_method=aggregation_method,
                                                          contracts_back=contracts_back,
                                                          date_to=date_to,
                                                          futures_data_dictionary=futures_data_dictionary,
                                                          use_last_as_current=use_last_as_current)
    current_data = aligned_output['current_data']
    aligned_data = aligned_output['aligned_data']

    month_diff_1 = 12*(current_data['c1']['ticker_year']-current_data['c2']['ticker_year'])+(current_data['c1']['ticker_month']-current_data['c2']['ticker_month'])
    month_diff_2 = 12*(current_data['c2']['ticker_year']-current_data['c3']['ticker_year'])+(current_data['c2']['ticker_month']-current_data['c3']['ticker_month'])

    weight_11 = 2*month_diff_2/(month_diff_1+month_diff_1)
    weight_12 = -2
    weight_13 = 2*month_diff_1/(month_diff_1+month_diff_1)

    price_1 = current_data['c1']['close_price']
    price_2 = current_data['c2']['close_price']
    price_3 = current_data['c3']['close_price']

    linear_interp_price2 = (weight_11*aligned_data['c1']['close_price']+weight_13*aligned_data['c3']['close_price'])/2

    butterfly_price = aligned_data['c1']['close_price']-2*aligned_data['c2']['close_price']+aligned_data['c3']['close_price']

    price_ratio = linear_interp_price2/aligned_data['c2']['close_price']

    linear_interp_price2_current = (weight_11*price_1+weight_13*price_3)/2

    price_ratio_current = linear_interp_price2_current/price_2

    q = stats.get_quantile_from_number({'x': price_ratio_current, 'y': price_ratio.values, 'clean_num_obs': max(100, round(3*len(price_ratio.values)/4))})
    qf = stats.get_quantile_from_number({'x': price_ratio_current, 'y': price_ratio.values[-40:], 'clean_num_obs': 30})

    recent_quantile_list = [stats.get_quantile_from_number({'x': x, 'y': price_ratio.values[-40:], 'clean_num_obs': 30}) for x in price_ratio.values[-40:]]

    weight1 = weight_11
    weight2 = weight_12
    weight3 = weight_13

    last5_years_indx = aligned_data['settle_date']>=datetime5_years_ago
    last2_months_indx = aligned_data['settle_date']>=datetime2_months_ago
    data_last5_years = aligned_data[last5_years_indx]

    yield1 = 100*(aligned_data['c1']['close_price']-aligned_data['c2']['close_price'])/aligned_data['c2']['close_price']
    yield2 = 100*(aligned_data['c2']['close_price']-aligned_data['c3']['close_price'])/aligned_data['c3']['close_price']

    yield1_last5_years = yield1[last5_years_indx]
    yield2_last5_years = yield2[last5_years_indx]

    yield1_current = 100*(current_data['c1']['close_price']-current_data['c2']['close_price'])/current_data['c2']['close_price']
    yield2_current = 100*(current_data['c2']['close_price']-current_data['c3']['close_price'])/current_data['c3']['close_price']

    butterfly_price_current = current_data['c1']['close_price']\
                            -2*current_data['c2']['close_price']\
                              +current_data['c3']['close_price']

    yield_regress_output = stats.get_regression_results({'x':yield2, 'y':yield1,'x_current': yield2_current, 'y_current': yield1_current,
                                                         'clean_num_obs': max(100, round(3*len(yield1.values)/4))})
    yield_regress_output_last5_years = stats.get_regression_results({'x':yield2_last5_years, 'y':yield1_last5_years,
                                                                     'x_current': yield2_current, 'y_current': yield1_current,
                                                                     'clean_num_obs': max(100, round(3*len(yield1_last5_years.values)/4))})

    bf_qz_frame_short = pd.DataFrame()
    bf_qz_frame_long = pd.DataFrame()

    if (len(yield1) >= 40)&(len(yield2) >= 40):

        recent_zscore_list = [(yield1[-40+i]-yield_regress_output['alpha']-yield_regress_output['beta']*yield2[-40+i])/yield_regress_output['residualstd'] for i in range(40)]

        bf_qz_frame = pd.DataFrame.from_items([('bf_price', butterfly_price.values[-40:]),
                                           ('q',recent_quantile_list),
                                           ('zscore', recent_zscore_list)])

        bf_qz_frame = np.round(bf_qz_frame, 8)
        bf_qz_frame.drop_duplicates(['bf_price'], take_last=True, inplace=True)

    # return bf_qz_frame

        bf_qz_frame_short = bf_qz_frame[(bf_qz_frame['zscore'] >= 0.6) & (bf_qz_frame['q'] >= 85)]
        bf_qz_frame_long = bf_qz_frame[(bf_qz_frame['zscore'] <= -0.6) & (bf_qz_frame['q'] <= 12)]

    if bf_qz_frame_short.empty:
        short_price_limit = np.NAN
    else:
        short_price_limit = bf_qz_frame_short['bf_price'].min()

    if bf_qz_frame_long.empty:
        long_price_limit = np.NAN
    else:
        long_price_limit = bf_qz_frame_long['bf_price'].max()

    zscore1= yield_regress_output['zscore']
    rsquared1= yield_regress_output['rsquared']

    zscore2= yield_regress_output_last5_years['zscore']
    rsquared2= yield_regress_output_last5_years['rsquared']

    second_spread_weight_1 = yield_regress_output['beta']
    second_spread_weight_2 = yield_regress_output_last5_years['beta']

    butterfly_5_change = data_last5_years['c1']['change_5']\
                             - (1+second_spread_weight_1)*data_last5_years['c2']['change_5']\
                             + second_spread_weight_1*data_last5_years['c3']['change_5']

    butterfly_5_change_current = current_data['c1']['change_5']\
                             - (1+second_spread_weight_1)*current_data['c2']['change_5']\
                             + second_spread_weight_1*current_data['c3']['change_5']

    butterfly_1_change = data_last5_years['c1']['change_1']\
                             - (1+second_spread_weight_1)*data_last5_years['c2']['change_1']\
                             + second_spread_weight_1*data_last5_years['c3']['change_1']

    percentile_vector = stats.get_number_from_quantile(y=butterfly_5_change.values,
                                                       quantile_list=[1, 15, 85, 99],
                                                       clean_num_obs=max(100, round(3*len(butterfly_5_change.values)/4)))

    downside = contract_multiplier*(percentile_vector[0]+percentile_vector[1])/2
    upside = contract_multiplier*(percentile_vector[2]+percentile_vector[3])/2
    recent_5day_pnl = contract_multiplier*butterfly_5_change_current

    residuals = yield1-yield_regress_output['alpha']-yield_regress_output['beta']*yield2

    regime_change_ind = (residuals[last5_years_indx].mean()-residuals.mean())/residuals.std()
    contract_seasonality_ind = (residuals[aligned_data['c1']['ticker_month'] == current_data['c1']['ticker_month']].mean()-residuals.mean())/residuals.std()

    yield1_quantile_list = stats.get_number_from_quantile(y=yield1, quantile_list=[10, 90])
    yield2_quantile_list = stats.get_number_from_quantile(y=yield2, quantile_list=[10, 90])

    noise_ratio = (yield1_quantile_list[1]-yield1_quantile_list[0])/(yield2_quantile_list[1]-yield2_quantile_list[0])

    daily_noise_recent = stats.get_stdev(x=butterfly_1_change.values[-20:], clean_num_obs=15)
    daily_noise_past = stats.get_stdev(x=butterfly_1_change.values, clean_num_obs=max(100, round(3*len(butterfly_1_change.values)/4)))

    recent_vol_ratio = daily_noise_recent/daily_noise_past

    alpha1 = yield_regress_output['alpha']

    residuals_last5_years = residuals[last5_years_indx]
    residuals_last2_months = residuals[last2_months_indx]

    residual_current = yield1_current-alpha1-second_spread_weight_1*yield2_current

    z3 = (residual_current-residuals_last5_years.mean())/residuals.std()
    z4 = (residual_current-residuals_last2_months.mean())/residuals.std()

    yield_change = (alpha1+second_spread_weight_1*yield2_current-yield1_current)/(1+second_spread_weight_1)

    new_yield1 = yield1_current + yield_change
    new_yield2 = yield2_current - yield_change

    price_change1 = 100*((price_2*(new_yield1+100)/100)-price_1)/(200+new_yield1)
    price_change2 = 100*((price_3*(new_yield2+100)/100)-price_2)/(200+new_yield2)

    theo_pnl = contract_multiplier*(2*price_change1-2*second_spread_weight_1*price_change2)

    aligned_data['residuals'] = residuals
    aligned_output['aligned_data'] = aligned_data

    grouped = aligned_data.groupby(aligned_data['c1']['cont_indx'])
    aligned_data['shifted_residuals'] = grouped['residuals'].shift(-5)
    aligned_data['residual_change'] = aligned_data['shifted_residuals']-aligned_data['residuals']

    mean_reversion = stats.get_regression_results({'x':aligned_data['residuals'].values,
                                                         'y':aligned_data['residual_change'].values,
                                                          'clean_num_obs': max(100, round(3*len(yield1.values)/4))})

    theo_spread_move_output = su.calc_theo_spread_move_from_ratio_normalization(ratio_time_series=price_ratio.values[-40:],
                                                  starting_quantile=qf,
                                                  num_price=linear_interp_price2_current,
                                                  den_price=current_data['c2']['close_price'],
                                                  favorable_quantile_move_list=[5, 10, 15, 20, 25])

    theo_pnl_list = [x*contract_multiplier*2  for x in theo_spread_move_output['theo_spread_move_list']]

    return {'aligned_output': aligned_output, 'q': q, 'qf': qf,
            'theo_pnl_list': theo_pnl_list,
            'ratio_target_list': theo_spread_move_output['ratio_target_list'],
            'weight1': weight1, 'weight2': weight2, 'weight3': weight3,
            'zscore1': zscore1, 'rsquared1': rsquared1, 'zscore2': zscore2, 'rsquared2': rsquared2,
            'zscore3': z3, 'zscore4': z4,
            'zscore5': zscore1-regime_change_ind,
            'zscore6': zscore1-contract_seasonality_ind,
            'zscore7': zscore1-regime_change_ind-contract_seasonality_ind,
            'theo_pnl': theo_pnl,
            'regime_change_ind' : regime_change_ind,'contract_seasonality_ind': contract_seasonality_ind,
            'second_spread_weight_1': second_spread_weight_1, 'second_spread_weight_2': second_spread_weight_2,
            'downside': downside, 'upside': upside,
             'yield1': yield1, 'yield2': yield2, 'yield1_current': yield1_current, 'yield2_current': yield2_current,
            'bf_price': butterfly_price_current, 'short_price_limit': short_price_limit,'long_price_limit':long_price_limit,
            'noise_ratio': noise_ratio,
            'alpha1': alpha1, 'alpha2': yield_regress_output_last5_years['alpha'],
            'residual_std1': yield_regress_output['residualstd'], 'residual_std2': yield_regress_output_last5_years['residualstd'],
            'recent_vol_ratio': recent_vol_ratio, 'recent_5day_pnl': recent_5day_pnl,
            'price_1': price_1, 'price_2': price_2, 'price_3': price_3, 'last5_years_indx': last5_years_indx,
            'price_ratio': price_ratio,
            'mean_reversion_rsquared': mean_reversion['rsquared'],
            'mean_reversion_signif' : (mean_reversion['conf_int'][1, :] < 0).all()}