quantopian寻找策略之mean_reversion

股价有向均线回归的趋势,利用这个特点,可以在技术指标处于超卖阶段寻找那些上涨速度快的流通性好的股票买入,形成下面的策略。策略来源quantopian。
对于市场上流通性最好的1500只股票在pipeline中先进行一波过滤:
1.年收益率排名前500
2.20日平均成交量大于100万股
3.股价高于1刀
4.rsi小于50
把过滤出的股票按照200日收益降序排列取前十名放入待买股票列表。
接下来用标普500指数的200日均线的98%做风控,指数走弱全部清仓并且不开新仓。
卖出持仓中不在待买股票列表中的标的。
买入持仓中没有且在待买股票列表中的标的,仓位按股票个数平均分配。

这个策略主要是用200日收益去向250日收益去靠拢。

几个需要注意的函数:

class roc_200days(CustomFactor):
    inputs = [USEquityPricing.close] 
    window_length = 200+1

    def compute(self, today, assets, out, close):
        print(len(close[-1]))
        out[:] = ((close[-1] - close[0]) / close[0]) * 100
        print(len(out))
        print(out)

运行时输出:

2010-01-04 21:45  PRINT 7932
2010-01-04 21:45  PRINT 7932
2010-01-04 21:45  PRINT [ 196.97048283  178.26086957  107.60141788 ...,           nan   13.84790011
  112.76595745]

这里面定义了一个custom_factor,close是一个201*7932的ndarray。上面这个类roc_200days的作用是不断获取200日收益。
quantopian寻找策略之mean_reversion_第1张图片

import quantopian.algorithm as algo
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.filters import QTradableStocksUS, Q1500US
from quantopian.pipeline.factors import SimpleMovingAverage, RSI, CustomFactor, Returns, Latest
from quantopian.algorithm import attach_pipeline, pipeline_output

class roc_200days(CustomFactor):
    inputs = [USEquityPricing.close] 
    window_length = 200+1

    def compute(self, today, assets, out, close):
        out[:] = ((close[-1] - close[0]) / close[0]) * 100


def initialize(context):
    set_commission(commission.PerTrade(cost=0.00))
    set_slippage(slippage.FixedSlippage(spread=0.00))
    set_long_only()
 
    schedule_function(is_positive_trend, date_rules.week_start(), time_rules.market_open(hours=0, minutes=59), half_days=False)
    schedule_function(rebalance_sell, date_rules.week_start(), time_rules.market_open(hours=1), half_days=False)
    schedule_function(rebalance_buy, date_rules.week_start(), time_rules.market_open(hours=2), half_days=False)
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close(), half_days=False)

    algo.attach_pipeline(make_pipeline(), 'my_pipeline')

    
def make_pipeline():
    TOTAL_STOCKS = 500
    returns_1_yr = Returns(window_length = 252)
    base_universe = returns_1_yr.top(TOTAL_STOCKS, mask = Q1500US())
    
    avg_volume = SimpleMovingAverage(inputs=[USEquityPricing.volume],window_length=20)
    filter_volume = avg_volume > 1000000

    last_price = Latest(inputs=[USEquityPricing.close], window_length=1) 
    filter_price = last_price > 1

    rsi = RSI(inputs=[USEquityPricing.close], window_length=3)
    filter_overbought = rsi < 50

    roc = roc_200days()
    
    stocks_to_trade = base_universe & filter_volume & filter_price & filter_overbought

    return Pipeline(
        columns = {
            'stocks': stocks_to_trade,
            'avg_volume': avg_volume,
            'roc': roc,
        },
        screen = (stocks_to_trade)
    )


def before_trading_start(context, data):
    context.my_output = pipeline_output('my_pipeline')
    context.candidates = context.my_output.sort_values('roc', ascending=False).head(10).index.tolist()
    
    
def is_positive_trend(context, data):  
    can_trade = False
    spy = symbol('SPY')
    price_history = data.history(spy, fields='close', bar_count=200, frequency='1d')      
    context.spy_close = data.current(spy,'close')
    context.sma200 = price_history.mean()
    context.sma200_buffered = context.sma200 * 0.98
    if context.spy_close > context.sma200_buffered:
        can_trade = True 
    context.is_trend = can_trade

    
def rebalance_sell(context, data):  
    if not context.is_trend:
        empty_bags(context, data)
    else:   
        for security in context.portfolio.positions:
            if security not in context.candidates and data.can_trade(security):
                order_target_percent(security, 0)  

                
def rebalance_buy(context, data): 
    if get_open_orders():
        log.info("Unexpected open orders: " + str(len(context.portfolio.positions)))
        print_orders()

    if context.is_trend is False:
        return

    needed_cash = get_cash_amount(context, data)
    for security in context.candidates:
        if security not in context.portfolio.positions and data.can_trade(security): 
            if(needed_cash < context.portfolio.cash):
                order_value(security, needed_cash)

    
def my_record_vars(context, data):
    record(spy=context.spy_close, sma200=context.sma200, sma200_buffered=context.sma200_buffered)

        
def get_cash_amount(context, data):
    weight = 0
    num_stocks = 0
    for security in context.candidates:
        if security not in context.portfolio.positions and data.can_trade(security):
            if data.can_trade(security):
                num_stocks = num_stocks + 1  

    if num_stocks > 0:
        weight = 1.0 / num_stocks   
    spend = context.portfolio.cash * weight  
    return spend


def empty_bags(context, data):
    for security in context.portfolio.positions:
        if data.can_trade(security):
            order_target_percent(security, 0)

            
def print_orders():
    open_orders = get_open_orders()
    if len(open_orders) == 0:
        return
    for security, orders in open_orders.iteritems():
        for order in orders:
            log.info("Active order for " + str(order.amount) + " shares of " + str(security))

你可能感兴趣的:(量化交易)