Untitled

 avatar
unknown
plain_text
2 years ago
25 kB
7
Indexable
from blueshift.library.pipelines.pipelines import average_volume_filter, technical_factor
from blueshift.library.technicals.indicators import rsi, sma, ema, adx, macd
from blueshift.pipeline import CustomFactor
from blueshift.pipeline.data import EquityPricing
import talib as ta
from blueshift.pipeline import Pipeline
from blueshift.errors import NoFurtherDataError
from blueshift.finance import commission, slippage
import numpy as np
from blueshift.api import (symbol,
                            order,
                            order_target_percent,
                            schedule_function,
                            schedule_once,
                            set_commission,
                            set_slippage,
                            date_rules,
                            time_rules,
                            get_open_positions,
                            get_order,
                            attach_pipeline,
                            pipeline_output,
                            get_datetime,
                            square_off,
                            order_value
                       )
import math
from scipy.stats import linregress
import pandas as pd
import talib as ta
import scipy
import cvxpy as cp
from scipy.optimize import minimize
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
from sklearn.inspection import permutation_importance
from scipy.stats import pearsonr

def adx_val(lookback):
    class ADXFactor(CustomFactor):
        inputs = [EquityPricing.high, EquityPricing.low, EquityPricing.close]
        def compute(self, today, assets, out, high, low, close):
            for idx in range(0, len(assets)):
                high_arr = high[:, idx]
                low_arr = low[:, idx]
                close_arr = close[:, idx]
                high_arr = high_arr[~np.isnan(high_arr)]
                low_arr = low_arr[~np.isnan(low_arr)]
                close_arr = close_arr[~np.isnan(close_arr)]
                # if idx < 10:
                    # print(high_arr, low_arr, close_arr)
                try:
                    returns = ta.ADX(high_arr, low_arr, close_arr, timeperiod = 14)
                    out[idx] = returns[-1]
                except:
                    out[idx] = 0
    return ADXFactor(window_length = lookback)

def my_volatility(lookback):
    class SignalVolatility(CustomFactor):
        inputs = [EquityPricing.close]
        def compute(self, today, assets, out, close):
            len_time = close.shape[0]
            len_assets = close.shape[1]
            for idx in range(0, len_assets):
                arr = np.array(close[:, idx])
                arr = arr[~np.isnan(arr)]
                if len(arr) != 0:
                    returns = ta.ROC(arr, 10)
                    returns = returns[~np.isnan(returns)]
                    out[idx] = np.std(returns)
                else:
                    out[idx] = 0  
    return SignalVolatility(window_length = lookback)

def dev_volume(lookback):
    class SignalDevVolume(CustomFactor):
        inputs = [EquityPricing.volume]
        def compute(self, today, assets, out, volume):
            historic = volume
            current = volume[0]
            mean_val = np.mean(historic, axis = 0)
            std_dev = np.std(historic, axis = 0)
            x_val = (current - mean_val) / std_dev
            out[:] = x_val
    return SignalDevVolume(window_length = lookback)


def avg_volume(lookback):
    class AvgDailyVolumeTraded(CustomFactor):
        inputs = [EquityPricing.close, EquityPricing.volume]
        def compute(self, today, assets, out, close, volume):
            close = np.nan_to_num(close)
            volume = np.mean(volume * close, axis=0)
            out[:] = volume
    return AvgDailyVolumeTraded(window_length = lookback)

def avg_liquidity(lookback):
    class SignalAverageLiquidity(CustomFactor):
        inputs = [EquityPricing.high, EquityPricing.low, EquityPricing.close, EquityPricing.volume]
        def compute(self, today, assets, out, high, low, close, volume):
            close = np.nan_to_num(close)
            num = close * volume
            # den = high - low
            # den[np.where(den < 0.1)] = 1e9
            val = np.mean(np.log(num), axis = 0)
            out[:] = val
    return SignalAverageLiquidity(window_length = lookback)


def make_screener(context):
    pipe = Pipeline()  

    # Momentum (RSI)
    momentum_filter = technical_factor(11, rsi, 10)
    pipe.add(momentum_filter, 'momentum')

    # # Exponential Moving Average
    # ema_filter = technical_factor(15, ema, 10)
    # pipe.add(ema_filter, 'ema')

    # Moving Average Convergence Divergence  (Not Working)
    # macd_filter = technical_factor(15, macd, 10)
    # pipe.add(macd_filter, 'macd')

    # Simple Moving Average
    # sma_filter = technical_factor(15, sma, 10)
    # pipe.add(sma_filter, 'sma')

    # Average Directional Index (Not Working)
    # adx_col = my_adx(20)
    # pipe.add(adx_col, 'adx')

    # Liquidity
    liquidity_filter = avg_liquidity(context.params['pipeline_lookback'])
    pipe.add(liquidity_filter, 'liquidity')

    # Average Volume
    volume_filter = avg_volume(context.params['pipeline_lookback'])
    pipe.add(volume_filter, 'avg_volume')

    # Recent Deviation in Volume
    dev_volume_filter = dev_volume(context.params['pipeline_lookback'])
    pipe.add(dev_volume_filter, 'dev_volume')

    # Volatility
    vol_filter = my_volatility(context.params['pipeline_lookback'])
    pipe.add(vol_filter, 'volatility')

    # ADX
    adx_factor = adx_val(context.params['pipeline_lookback'])
    pipe.add(adx_factor, 'adx')

    return pipe

def rsi_filter(num):
    if num >= 70:
        return 1
    elif num >= 30:
        return 0
    else:
        return -1

def screener(context, data):
    try:
        pipeline_results = pipeline_output('my_screener')
    except:
        print('no pipeline for {}'.format(get_datetime()))
        return []

    pipeline_results.replace([np.inf, -np.inf], 0, inplace=True)
    pipeline_results.fillna(0, inplace=True)
    pipeline_results['momentum'] = pipeline_results['momentum'].apply(rsi_filter)
    print(pipeline_results)
    return pipeline_results


def optimise_portfolio(context, data):
    price = context.signals = dict((security, None) for security in context.securities)
    for security in context.securities:
        price[security] = data.history(security, ['open', 'high', 'low', 'close'], 252, '1d')
    stocks = pd.DataFrame()
    stocks.index = list(price[context.securities[0]].index)
    for security in context.securities:
        stocks[str(security.name)] = price[security]['close']

    log_return = np.log(stocks/stocks.shift(1))
    numAssets = len(context.securities)

    def get_ret_vol_sr(weights):
        weights = np.array(weights)
        ret = np.sum(log_return.mean() * weights) * context.params['lookback']
        vol = np.sqrt(np.dot(weights.T,np.dot(log_return.cov()*context.params['lookback'],weights)))
        sr = ret/vol
        return np.array([ret,vol,sr])

    def neg_sharpe(weights):
        return get_ret_vol_sr(weights)[2] * -1

    cons = ({'type':'eq', 'fun': lambda x : np.sum(x) - 1})
    bound = (0, 1)
    bounds = list(bound for asset in range(numAssets))
    opt_results = minimize(neg_sharpe, numAssets * [1./ numAssets], method='SLSQP', bounds=bounds, constraints=cons)
    for i, key in enumerate(context.weights.keys()):
        context.weights[key] = opt_results.x[i]


def randForestRegressorNew(df):
    df_new = df.fillna(0)
    df_new = df_new.replace([np.inf, -np.inf], 0)
    x = df_new.columns[:-1]
    y = df_new.columns[-1]
    # print(x,y)
    # print(df_new[x], df_new[y])
    forest = LinearRegression()
    forest.fit(df_new[x], df_new[y])
    print('fit success')
    # result = permutation_importance(forest, df_new[x], df_new[y], n_repeats=10, random_state=42, n_jobs=2)
    return forest # result['importances_mean']/np.sum(result['importances_mean'])


def compute_correlation(asset,context,data):
    offset = context.params['correlation_offset']
    window = context.params['correlation_window'] + 1
    benchmark_df = data.history(symbol('NIFTY'),['open','close'],360*window,'1m')
    cnt = 1
    corr = []
   
    stock_df = data.history(asset, ["open", "close"], 360*window , "1m")
    benchmark = []
    stock = []
    cnt = 0
    for i in range(360*window):
        if(cnt+offset+60 >= len(benchmark_df["close"]) or cnt+60 >= len(stock_df["close"])):
            break
    # while cnt < (360*window - offset - 60):
        benchmark.append((benchmark_df["open"][cnt+offset] + benchmark_df["close"][cnt + offset + 60]) / 2)
        stock.append((stock_df["open"][cnt] + stock_df["close"][cnt + 60]) / 2)
        cnt += 60
   
    if(len(benchmark)==0):
        print('benchmark len error')
    # print(asset)
    # print(pearsonr(benchmark, stock))
    coefficient, _ = pearsonr(benchmark, stock)
    # print(asset)
    return coefficient

def random_forest_filter(context, data):
    print('here')
    if(context.ml_params_init==False):
        
        print('None detected')
        pipeline_results = screener(context, data)
        pipeline_results = pipeline_results.sort_values('adx')[-100:]
        stock_list = pipeline_results.index
        # print(stock_list)
        corr_col = []
        for sec in stock_list:
            try:
                corr_col.append(compute_correlation(sec,context,data)) 
            except:
                corr_col.append(0)
            context.open_price[sec] = data.current(sec,'close')
        # print(corr_col)
        pipeline_results["Correlation"] = corr_col
        context.ml_params = pipeline_results
        context.ml_params_init = True
        # for sec in stock_list:
        #     # print(data.current(sec,'open'))
        #     if(math.isnan(data.current(sec,'close'))):
        #         print('error:', sec)
        #     context.open_price[sec] = data.current(sec,'close')#data.history(sec,'open',1,'1m')[0]
            # context.ml_params.loc[sec]['sma'] = context.ml_params.loc[sec]['sma'] / context.open_price[sec]
            # context.ml_params.loc[sec]['ema'] = context.ml_params.loc[sec]['ema'] / context.open_price[sec]

        return [symbol('TCS'), symbol('ITC'), symbol('INFY'), symbol('HDFCBANK'), symbol('HINDUNILVR')]
    
    print('here')
    df = context.ml_params
    stock_list = df.index
    # print(stock_list)
    results = []

    for sec in stock_list:
        try:
            data_close = data.current(sec,'close') #data.history(sec,'close',1,'1m')[0]
            # print(data_close)
        # if(len(data_close)!=0):
            results.append((data_close - context.open_price[sec])/data_close)
        # else:
            # results.append(0)
        except:
            data_close = data.current(sec, 'close')
            print(data_close)
            # print(data_close,data.history(sec,'close',1,'1m')[0])
            results.append(0)
            context.open_price[sec] = 100000
            print('error in computing results',sec)

    
    print('here')
    
    df["Results"] = results
    #todo: scale the parameters
    print('waiting for forest')
    forest = randForestRegressorNew(df)
    print('got forest')

    pipeline_results = screener(context, data)
    pipeline_results = pipeline_results.sort_values('adx')[-100:]
    # pipeline_results = pipeline_results.fillna(0)
    stock_list = pipeline_results.index
    #todo: update security list
    corr_col = []
    new_values = []
    print('got pipeline')
    for sec in stock_list:
        # print(sec)
        # print(sec, pipeline_results.loc[sec])
        
        try:
            context.open_price[sec] = data.current(sec,'close')

            pipeline_row = list(pipeline_results.loc[sec])
            corr = compute_correlation(sec,context,data)
            pipeline_row.append(corr)
            corr_col.append(corr)
            new_values.append([forest.predict([pipeline_row]), sec])
        except:
            print('error in compute correlation',sec)
            corr = 0
            corr_col.append(corr)

    print('prediction successful')
    pipeline_results["Correlation"] = corr_col
    context.ml_params = pipeline_results
    print('parameter update success')
    new_values.sort(reverse = True)
    print(new_values)
    top_assets = []
    len_stocks = len(new_values) // 10

    for i, x in enumerate(new_values):
        top_assets.append(x[1])
        if i > len_stocks:
            break
    print('asset extracted')
    return top_assets

def find_stocks_forest(context, data):
    try:
        context.securities = random_forest_filter(context,data)
    except Exception as e:
        print('error in random  forest',e)
        context.securities = [symbol('TCS'), symbol('ITC'), symbol('INFY'), symbol('HDFCBANK'), symbol('HINDUNILVR')]

    print(context.securities)
    context.signals = dict((security, 0) for security in context.securities)
    context.target_position = dict((security, [0, 0]) for security in context.securities)
    context.set_flag = dict((security, 0) for security in context.securities)
    context.counter = dict((security, 0) for security in context.securities)
    context.weights = dict((security, 1 / len(context.securities)) for security in context.securities)
    context.trade = True
    if(context.params['Optimizer_status']):
        optimise_portfolio(context, data)
    print(context.weights)

def ohlc4(security, context, data):
    price = data.history(security, ['open', 'high', 'low', 'close'], context.params['lookback'], context.params['indicator_freq']).iloc[::-1]
    # print(price["close"][0])
    price['ohlc4'] = (price['open'] + price['high'] + price['low'] + price['close'])/4
    price.reset_index(inplace=True)
    price = price.rename(columns={'index': 'timestamp'})
    return price

def get_channel(price, n):
    reg = list(range(1, n))
    mid = np.mean(price[0:n])
    slope = linregress(reg, price[:-1]).slope - linregress(reg, price[1:]).slope
    intercept = mid - slope * (n / 2) + (1 - n % 2) * slope / 2
    endy = intercept + slope * (n - 1)
    dev = 0.0
    for x in range(0, n):
        dev += math.pow(price[x] - (intercept + slope * (n - x)), 2)
    dev = math.sqrt(dev / n)
    return intercept, endy, dev, slope

def rising(price, n):
    return all(i < j for i, j in zip(price[:n], price[1:n + 1]))

def falling(price, n):
    return all(i > j for i, j in zip(price[:n], price[1:n + 1]))

def standard_deviation(price, len):
    mid = price[0:len].mean()
    dev = 0.0
    for x in range(0, len):
        dev += math.pow(price[x] - mid, 2)
    dev = math.sqrt(dev / (len - 1))
    return dev

def initialize(context):
    context.params = {
                      'pipeline_lookback':30,
                      'lookback': 30,
                      'indicator_freq': '30m',
                      'buy_signal_threshold': 0.5,
                      'sell_signal_threshold': -0.5,
                      'SMA_period_short': 15,
                      'SMA_period_long': 60,
                      'RSI_period': 60,
                      'trade_freq': 60,
                      'leverage': 1,
                      'i_deviation': 2,
                      'BBands_period': 14,
                      'Pipeline_status': False,
                      'Optimizer_status': False,
                      'correlation_offset':60,
                      'correlation_window':1,
                      'num_ml_params':4,
                      'max_orders': 3}

    schedule_function(find_stocks_forest, date_rule=date_rules.month_start(0),time_rule = time_rules.on_open())

    attach_pipeline(make_screener(context), name='my_screener')
    context.securities = [symbol('UNIONBANK'), symbol('EASEMYTRIP'), symbol('LSIL'), symbol('FEDERALBNK'), symbol('HINDCOPPER')]

    set_commission(commission.PerShare(cost=0.0, min_trade_cost=0.0))
    set_slippage(slippage.FixedSlippage(0.00))

    context.signals = dict((security, 0) for security in context.securities)
    context.target_position = dict((security, [0, 0]) for security in context.securities)
    context.set_flag = dict((security, 0) for security in context.securities)
    context.counter = dict((security, 0) for security in context.securities)
    context.weights = dict((security, 1 / len(context.securities)) for security in context.securities)
    context.ml_params = None
    context.ml_params_init = False
    # for i in range(context.params['num_ml_params']):
    #     context.ml_params[i] = [0]*len(context.securities)
    # context.results = [0]*len(context.securities)
    context.open_price = dict()

    schedule_function(run_strategy,
        date_rule=date_rules.every_day(),
        time_rule=time_rules.every_nth_minute(context.params['trade_freq']))

    schedule_function(square, date_rules.every_day(),
                      time_rules.market_close(minutes=15))
   
    schedule_function(stop_trading, date_rules.month_end(),
                      time_rules.market_close(minutes=15))

    context.trade = False    
    context.take_profit = None
    context.stop_loss = None

def square(context,data):
    square_off()
    context.counter = dict((security, 0) for security in context.securities)

def square_off_positions(context,data):
    # square_off()
    context.trade = False
    # data_h = data.history(symbol('TCS'),['open','close','volume','high','low'],1,'1m')
    # data_h.reset_index(inplace=True)
    # data_h = data_h.rename(columns = {'index':'timestamp'})
    # print("Squaring off at ", data_h['timestamp'])

# def before_trading_start(context, data):
#     context.trade = True

def stop_trading(context, data):
    context.trade = False

def run_strategy(context, data):
    if context.trade == False:
        return
    for security in context.securities:
        try:
            price = ohlc4(security, context, data)
        except:
            print("Price Error")
            return
        pos = get_open_positions()
        curr_price = data.current(security,'close')
        if context.set_flag[security] == -1 and len(price['close'])!=0:
            context.target_position[security] = [abs(price['close'][0] - price['open'][0]), price['low'][0]]
            context.set_flag[security] = 0
        #     print(context.target_position[security], pos[security].buy_price)
        try:
            trade = signal_function(security, price, context, data)
        except:
            trade = 0

        if trade == 1:
            remain_capital = context.account.available_funds
            curr_price = data.current(security,'close')
            order_val = int(remain_capital*context.weights[security])

            # print(security, trade)
            order_id = order_value(security, 5 * order_val)
            context.set_flag[security] = -1
            if(order_id!=None):
                context.counter[security] = context.counter[security]+1
            # print(security)
            # order_id = order_target_percent(security, 100 * context.weights[security])

        elif trade == -1:
            order_id = order_target_percent(security, 0)
            if(order_id!=None):
                context.counter[security] = 0

def signal_function(security, price, context, data):
    #y_low, y_high, dev, slope = get_channel(price['ohlc4'], context.params['lookback'])
    std_dev = standard_deviation(price['ohlc4'], context.params['lookback'])
    # print(y_low, y_high, dev, slope, std_dev)
    long_c1 = rising(price['ohlc4'], 2)
    #long_n = price['close'][4] < price['open'][4] and price['close'][3] < price['open'][3] and price['close'][2] < price['open'][2] and price['close'][1] < price['open'][1] and (abs(price['close'][1] - price['open'][1]) > abs(price['close'][2] - price['open'][2])) and (abs(price['close'][2] - price['open'][2]) > abs(price['close'][3] - price['open'][3])) and (abs(price['close'][3] - price['open'][3]) > abs(price['close'][4] - price['open'][4]))
    # print(long_c1)

    # avg = ta.SMA(price['close'].values, 20)
    #print("Sma", avg)
    long_c2 = (price['ohlc4'][0] > price['ohlc4'][1] + std_dev / 2)
    long_c3 = price['high'][1] <= price['close'][0]
    long_condition = (long_c1 or long_c2) and long_c3 #or rising(avg[-4:], 3)))
    exit_c1 = falling(price['ohlc4'], 2)
    exit_c2 =  (price['ohlc4'][0] < price['ohlc4'][1] - std_dev / 2)
    exit_c3 = (price['low'][1] >= price['close'][0])
    # #exit_n = price['close'][4] > price['open'][4] and price['close'][3] > price['open'][3] and price['close'][2] > price['open'][2] and price['close'][1] > price['open'][1] and (abs(price['close'][1] - price['open'][1]) > abs(price['close'][2] - price['open'][2])) and (abs(price['close'][2] - price['open'][2]) > abs(price['close'][3] - price['open'][3])) and (abs(price['close'][3] - price['open'][3]) > abs(price['close'][4] - price['open'][4]))
    # exit_condition = exit_c1 #or exit_c2) and exit_c3 #or falling(avg[-4:], 3)))
    exit_condition = exit_c1
    # print(long_condition, exit_condition)
    open_positions = get_open_positions()
    present = (security in open_positions)
    curr_price = data.current(security, 'close')
    # print(long_c1, exit_c1)
    if long_condition and (not present or open_positions[security].quantity == 0):
        return 1
    # if present and open_positions[security].quantity > 0:      
    #     if exit_condition:
    #         return -1
    #     if curr_price < 0.99 * open_positions[security].buy_price:
    #         return -1
    return 0

# def extract_stock_random_forest_weights(context,data):
#     data_p = context.ml_params
   
#     results = []
#     idx = 0
#     for(sec in context.securities):
#         data_close = data.history(sec,'close',1,'1m')
#         results.append((data_close[0] - context.open_price[idx])/data_close[0])
#         idx = idx +1
#     data_p.append(results)
#     #todo: scale the parameters
#     df = pd.DataFrame(data_p)
#     weights = randForestRegressorNew(df)
#     return weights


# def sort_random_forest(context,data,weights):
#     pipeline_results = pipeline_output('my_screener')
#     #todo: update security list
#     new_values = []
#     sorted_asset = []
#     for(sec in context.securities):
#         new_values.append([compute_correlation(sec,context,data)*weights[0]+
#                             pipeline_results['momentum'][sec]*weights[1]+
#                             pipeline_results['liquidity'][sec]*weights[2]+
#                             pipeline_results['avg_volume'][sec]*weights[3],sec])
#     new_values.sort()
#     for x in new_values:
#         sorted_asset.append(x[1])

#     return sorted_asset


# def update_random_forest_param(context,data):
#     ml_params = [[],[],[],[]]
#     context.open_price = []
#     pipeline_results = pipeline_output('my_screener')
#     for sec in context.securities:
#         data_open = data.history(sec,'open',1,'1m')
#         ml_params[0].append(compute_correlation(sec,context,data))
#         ml_params[1].append(pipeline_results['momentum'][sec])
#         ml_params[2].append(pipeline_results['liquidity'][sec])
#         ml_params[3].append(pipeline_results['avg_volume'][sec])
#         context.open_price.append(data_open[0])

#     context.ml_params = ml_params

# def find_stocks(context, data):
#     data_h = data.history(symbol('TCS'),['open','close','volume','high','low'],1,'1m')
#     data_h.reset_index(inplace=True)
#     data_h = data_h.rename(columns = {'index':'timestamp'})
#     if(context.params['Pipeline_status']):
#         context.securities = screener(context, data)
#     else:
#         context.securities = [symbol('TCS'), symbol('ITC'), symbol('INFY'), symbol('HDFCBANK'), symbol('HINDUNILVR')]
#     # print(screener(context, data))
#     context.trade = True
#     open_positions = get_open_positions()
#     # print(data_h['timestamp'])
#     for asset in open_positions:
#         print('open_position_detected',asset)
#     context.signals = dict((security, 0) for security in context.securities)
#     context.target_position = dict((security, [0, 0]) for security in context.securities)
#     context.set_flag = dict((security, 0) for security in context.securities)
#     context.weights = dict((security, 1 / len(context.securities)) for security in context.securities)
#     # context.buy_price = dict((security, 0) for security in context.securities)
#     # context.profit = dict((security, 0) for security in context.securities)

#     if(context.params['Optimizer_status']):
#         optimise_portfolio(context, data)
#     print(context.weights)
Editor is loading...