将quantopian的动量策略迁移到老虎证券量化api

原quantopian的动量策略,感谢原策略作者
首先需要获取数据
dataframe的列是各个股票的代码,index是时间,日频

def get_price(bars_num):
    stocks = read_sp_500code()
    df=pd.DataFrame(columns=['symbol','time','open','high','low','close','volume'])
    #每次取数据要取的条目数量
    items=math.floor(1000/bars_num)
    stock_num=len(stocks)
    print(stock_num)
    roundtrip=math.floor(stock_num/items)
    print(roundtrip)

    for i in range(roundtrip):
        bars = quote_client.get_bars(stocks[i*items:i*items+items],period=BarPeriod.DAY,begin_time=-1, end_time=-1,right=QuoteRight.BR,limit=bars_num)
        df=df.append(bars,ignore_index=True)
        print(bars)
    bars = quote_client.get_bars(stocks[roundtrip*items:stock_num],\
                     period=BarPeriod.DAY,begin_time=-1,end_time=-1,right=QuoteRight.BR,limit=bars_num)
    df=df.append(bars,ignore_index=True)
    df.drop(['time','open','high','low','volume'],axis=1,inplace=True)
    print(df)
  
    return_df=pd.DataFrame(columns=stocks)
    for i in range(len(stocks)):
        close = df[i*bars_num:i*bars_num+bars_num]['close'].values
        print(close)
        print(type(close))
        return_df[stocks[i]]=close
    print(return_df)
    return return_df
"""
Simple equity momentum model by Andreas Clenow
cloned from https://www.quantopian.com/posts/equity-momentum
Purpose: 
To capture momentum effect in US stock markets.
Implements two methods of reducing downside.
Index level trend filter, and minimum momentum.

Momentum analytic:
Uses annualized exponential regression slope, multiplied by R2, to adjust for fit.
Possibility to use average of two windows.

Settings:
* Investment universe
* Momentum windows (x2)
* Index trend filter on/off
* Index trend filter window
* Minimum required slope
* Exclude recent x days
* Trading frequency
* Cash management via bond etf, on off
* Bond ETF selection
* Sizing method, inverse vola or equal.


Suggested areas of research and improvements:
* Use pipeline to limit number of processed stocks, by putting the regression logic in there.
* Adding fundamental factors.
* Portfolio sizes
* Market cap weighting / inverse market cap weighting. (you may want to z-score and winsorize)
* Momentum analytic: There are many possible ways. Try simplifications and variations.


"""


from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume, CustomFactor, SimpleMovingAverage, Latest
from quantopian.pipeline.filters.morningstar import Q500US, Q1500US
from quantopian.pipeline.data import morningstar

import numpy as np  # we're using this for various math operations
from scipy import stats  # using this for the reg slope
import pandas as pd


def slope(ts):
    """
    Input: Price time series.
    Output: Annualized exponential regression slope, multipl
    """
    x = np.arange(len(ts))
    log_ts = np.log(ts)
    #print('This is ts',ts)
    #print('This is log_ts:\n',log_ts)
    #print(' ')
    slope, intercept, r_value, p_value, std_err = stats.linregress(x, log_ts)
    #print(slope)
    annualized_slope = (np.power(np.exp(slope), 250) - 1) * 100
    #print('This is annualized_slope',annualized_slope)
    return annualized_slope * (r_value ** 2)

def inv_vola_calc(ts):
    """
    Input: Price time series.
    Output: Inverse exponential moving average standard deviation. 
    Purpose: Provides inverse vola for use in vola parity position sizing.
    """
    returns = np.log(ts).diff()
    #print (type(returns))
    stddev = returns.ewm(halflife=20, ignore_na=True, min_periods=0,
                         adjust=True).std(bias=False).dropna()
    return 1 / stddev.iloc[-1]


def initialize(context):
    """
    Setting our variables. Modify here to test different
    iterations of the model.
    """
    
    # Investment Universe 1 = Q500US, 2 = Q1500US
    context.investment_set = 1
    
    
    # This version uses the average of two momentum slopes.
    # Want just one? Set them both to the same number.
    context.momentum_window = 60  # first momentum window.
    context.momentum_window2 = 90  # second momentum window
    
    # Limit minimum slope. Keep in mind that shorter momentum windows
    # yield more extreme slope numbers. Adjust one, and you may want
    # to adjust the other.
    context.minimum_momentum = 60  # momentum score cap
    
    # Fixed number of stocks in the portfolio. How diversified
    # do you want to be?
    context.number_of_stocks = 25  # portfolio size
    context.index_id = sid(8554) # identifier for the SPY. used for trend filter.
    context.index_average_window = 100  # moving average periods for index filter
    
    # enable/disable trend filter.
    context.index_trend_filter = True  
    
    # Most momentum research excludes most recent data.
    context.exclude_days = 5  # excludes most recent days from momentum calculation
    
    # Set trading frequency here.
    context.trading_frequency = date_rules.month_start()
    
    # identifier for the cash management etf, if used.
    context.use_bond_etf = True
    context.bond_etf = sid(23870) 
    
    # 1 = inv.vola. 2 = equal size. Suggest to implement 
    # market cap and inverse market cap as well. There's 
    # lots of room for development here.
    context.size_method = 2 
    
    

    # Schedule rebalance
    schedule_function(
        my_rebalance,
        context.trading_frequency,
        time_rules.market_open(
            hours=1))

    # Schedule daily recording of number of positions - For display in back
    # test results only.
    schedule_function(
        my_record_vars,
        date_rules.every_day(),
        time_rules.market_close())

    # Create our dynamic stock selector - getting the top 500 most liquid US
    # stocks.
    
    if(context.investment_set == 1):
        inv_set = Q500US()
        print(type(inv_set))
    elif(context.investment_set == 2):
        inv_set = Q1500US()
    
    attach_pipeline(make_pipeline(inv_set), 'investment_universe')



def make_pipeline(investment_set): 
    """
    This will return the selected stocks by market cap, dynamically updated.
    """
    # Base universe 
    base_universe = investment_set 
    yesterday_close = USEquityPricing.close.latest

    pipe = Pipeline(
        screen=base_universe,
        columns={
            'close': yesterday_close,
        }
    )
    return pipe


def my_rebalance(context, data):
    """
    Our monthly rebalancing routine
    """

    # First update the stock universe. 
    context.output = pipeline_output('investment_universe')
    context.security_list = context.output.index
    print(context.security_list)
    print(context.security_list[0])
    print(type(context.security_list[0]))
    # Get data
    hist_window = max(context.momentum_window,
                      context.momentum_window2) + context.exclude_days
   
    hist = data.history(context.security_list, "close", hist_window, "1d")
    #hist_only = data.history('Equity(24 [AAPL])', "close", hist_window, "1d")
    #print(hist_only)
    #print(hist)
    data_end = -1 * context.exclude_days # exclude most recent data

    momentum1_start = -1 * (context.momentum_window + context.exclude_days)
    momentum_hist1 = hist[momentum1_start:data_end]

    momentum2_start = -1 * (context.momentum_window2 + context.exclude_days)
    momentum_hist2 = hist[momentum2_start:data_end]

    # Calculate momentum scores for all stocks.
    momentum_list = momentum_hist1.apply(slope)  # Mom Window 1
    momentum_list2 = momentum_hist2.apply(slope)  # Mom Window 2
    #print(momentum_list)
    # Combine the lists and make average
    momentum_concat = pd.concat((momentum_list, momentum_list2))
    mom_by_row = momentum_concat.groupby(momentum_concat.index)
    mom_means = mom_by_row.mean()

    # Sort the momentum list, and we've got ourselves a ranking table.
    ranking_table = mom_means.sort_values(ascending=False)

    # Get the top X stocks, based on the setting above. Slice the dictionary.
    # These are the stocks we want to buy.
    buy_list = ranking_table[:context.number_of_stocks]
    final_buy_list = buy_list[buy_list > context.minimum_momentum] # those who passed minimum slope requirement

    # Calculate inverse volatility, for position size.
    inv_vola_table = hist[buy_list.index].apply(inv_vola_calc)
    # sum inv.vola for all selected stocks.
    sum_inv_vola = np.sum(inv_vola_table)

    # Check trend filter if enabled.
    if (context.index_trend_filter):
        index_history = data.history(
            context.index_id,
            "close",
            context.index_average_window,
            "1d")  # Gets index history
        index_sma = index_history.mean()  # Average of index history
        current_index = index_history[-1]  # get last element
        # declare bull if index is over average
        bull_market = current_index > index_sma

    # if trend filter is used, only buy in bull markets
    # else always buy
    if context.index_trend_filter:
        can_buy = bull_market
    else:
        can_buy = True


    equity_weight = 0.0 # for keeping track of exposure to stocks
    
    # Sell positions no longer wanted.
    for security in context.portfolio.positions:
        if (security not in final_buy_list):
            if (security.sid != context.bond_etf):
                # print 'selling %s' % security
                order_target(security, 0.0)
                
    vola_target_weights = inv_vola_table / sum_inv_vola
    
    for security in final_buy_list.index:
        # allow rebalancing of existing, and new buys if can_buy, i.e. passed trend filter.
        if (security in context.portfolio.positions) or (can_buy): 
            if (context.size_method == 1):
                weight = vola_target_weights[security]
            elif (context.size_method == 2):
                weight = (1.0 / context.number_of_stocks)
                print context.number_of_stocks
            order_target_percent(security, weight)
            equity_weight += weight
    
       

    # Fill remaining portfolio with bond ETF
    etf_weight = max(1 - equity_weight, 0.0)

    print 'equity exposure should be %s ' % equity_weight

    if (context.use_bond_etf):
        order_target_percent(context.bond_etf, etf_weight)

def my_record_vars(context, data):
    """
    This routine just records number of open positions and exposure level
    for display purposes.
    """
    etf_exp = 0.0
    pos_count = 0
    eq_exp = 0.0
    for position in context.portfolio.positions.itervalues():
        pos_count += 1
        if (position.sid == context.bond_etf):
            etf_exp += (position.amount * position.last_sale_price) / \
                context.portfolio.portfolio_value
        else:
            eq_exp += (position.amount * position.last_sale_price) / \
                context.portfolio.portfolio_value

    record(equity_exposure=eq_exp)
    record(bond_exposure=etf_exp)
    record(tot_exposure=context.account.leverage)
    record(positions=pos_count)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值