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日收益。
在這裏插入圖片描述

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))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章