策略示例

纸上得来终觉浅。本章给出五个典型策略的完整代码,覆盖日线趋势、多股票择时、日内做 T、盘前抢板、tick 高频四种节奏,每个都标注运行频率和关键逻辑,改参数就能变成自己的策略。

一、双均线策略(日线)

思路:5 日均线上穿 20 日均线全仓买入,下穿则清仓。运行频率:日线。

def initialize(context):
    g.security = '600570.SS'
    set_universe(g.security)

def before_trading_start(context, data):
    # 盘前取近 20 日收盘价
    h = get_history(20, '1d', 'close', g.security, fq='dypre',
                    include=False, is_dict=True)
    g.close = h[g.security]['close']

def handle_data(context, data):
    current_price = data[g.security].close
    # 拼上当前价合成完整序列
    import numpy as np
    close = np.concatenate((g.close, [current_price]))
    ma5  = close[-5:].mean()
    ma20 = close[-20:].mean()

    if ma5 > ma20 and get_position(g.security).amount == 0:
        order_value(g.security, context.portfolio.cash)
        log.info('金叉买入')
    elif ma5 < ma20 and get_position(g.security).enable_amount > 0:
        order_target(g.security, 0)
        log.info('死叉清仓')

要点:盘前把历史数据缓存到 g.close,盘中只拼一根当前 K 线,避免 handle_data 内重复拉数据。

二、MACD 多股票金叉策略(日线)

思路:在沪深 300 成分股中,找 DIF、DEA 均为正且 MACD 由负转正(强势金叉)的标的买入;DIF、DEA 均为负且 MACD 由正转负(弱势死叉)则卖出。等权持仓 10 只。运行频率:日线。

def initialize(context):
    g.hold_num = 10
    set_benchmark('000300.SS')

def before_trading_start(context, data):
    g.stocks = get_index_stocks('000300.SS')
    g.stocks = filter_stock_by_status(g.stocks, ['ST', 'HALT', 'DELISTING'])
    h = get_history(100, '1d', 'close', security_list=g.stocks,
                    fq='dypre', include=False, is_dict=True)
    g.close_dict = {s: h[s]['close'] for s in g.stocks}
    g.every_value = context.portfolio.portfolio_value / g.hold_num

def handle_data(context, data):
    for s in g.stocks:
        close = g.close_dict[s]
        dif, dea, macd = get_MACD(close, 12, 26, 9)
        DIF, DEA = dif[-1], dea[-1]
        macd_now, macd_pre = macd[-1], macd[-2]
        pos = get_position(s)

        if pos.amount == 0:
            # 强势金叉 + 资金充足
            if DIF > 0 and DEA > 0 and macd_pre < 0 and macd_now >= 0:
                if context.portfolio.cash >= g.every_value * 0.8:
                    order_target_value(s, g.every_value)
                    log.info('买入 %s' % s)
        else:
            # 弱势死叉
            if DIF < 0 and DEA < 0 and macd_pre >= 0 and macd_now < 0:
                order_target(s, 0)
                log.info('卖出 %s' % s)

要点:盘前一次性批量拉 100 日收盘价(is_dict=True 取数更快),handle_data 里只做计算和下单,避免循环中重复请求接口。

三、日内 T+0 策略(分钟)

思路:先持有一份底仓,盘中用 5 分钟和 15 分钟 RSI 多空共振判断做正 T(先买后卖)或反 T(先卖后买),盈利 1% 恢复头寸,收盘前清算。运行频率:分钟。

import numpy as np

def initialize(context):
    g.amount = 100            # 一份标准头寸
    g.rate = 1                # 做 T 涨跌幅 1%
    g.L, g.S = 50, 80         # 长短周期 RSI 阈值
    g.security = '510500.SS'
    g.ini_buy = False
    if not is_trade():
        set_limit_mode('UNLIMITED')

def before_trading_start(context, data):
    g.B_flag = False          # 正 T 开关
    g.S_flag = False          # 反 T 开关
    g.handle_flag = True

def handle_data(context, data):
    if not g.handle_flag:
        return
    k = get_current_kline_count()      # 当日已走完的分钟数
    if not g.ini_buy:
        order(g.security, g.amount)    # 开盘先建底仓
        g.ini_buy = True
        g.handle_flag = False
        return
    if k < 30:
        return

    if k % 5 == 0:                     # 每 5 分钟判断一次
        h5  = get_history(100, '5m',  'close', g.security, is_dict=True)[g.security]['close']
        h15 = get_history(100, '15m', 'close', g.security, is_dict=True)[g.security]['close']
        now_price = data[g.security].close
        h15 = np.concatenate((h15, [now_price]))
        rsi5  = get_rsi(h5)[-1]
        rsi15 = get_rsi(h15)[-1]

        if rsi15 > g.L and rsi5 > g.S and not g.B_flag:
            order(g.security, g.amount); g.B_flag = True; g.B_cost = now_price
        if rsi15 < 100-g.L and rsi5 < 100-g.S and not g.S_flag:
            order(g.security, -g.amount); g.S_flag = True; g.S_cost = now_price

    # 盈利恢复头寸
    if g.B_flag and data[g.security].price >= g.B_cost * (1 + g.rate/100):
        order(g.security, -g.amount); g.B_flag = False
    if g.S_flag and data[g.security].price <= g.S_cost * (1 - g.rate/100):
        order(g.security, g.amount); g.S_flag = False

    # 收盘前清算回底仓量
    if k >= 238:
        order_target(g.security, g.amount)

def get_rsi(arr, periods=14):
    # 自实现 RSI,返回与输入等长的列表
    rsi = [np.nan] * len(arr)
    if len(arr) <= periods:
        return rsi
    up = down = 0
    for i in range(1, periods + 1):
        if arr[i] >= arr[i-1]: up += arr[i] - arr[i-1]
        else: down += arr[i-1] - arr[i]
    up, down = up/periods, down/periods
    rsi[periods] = 100 - 100/(1 + up/(down or 1e-9))
    for j in range(periods+1, len(arr)):
        u = max(arr[j]-arr[j-1], 0); d = max(arr[j-1]-arr[j], 0)
        up = (up*(periods-1)+u)/periods
        down = (down*(periods-1)+d)/periods
        rsi[j] = 100 - 100/(1 + up/(down or 1e-9))
    return rsi

要点:日内做 T 的前提是先有底仓(绕开 T+1);用 get_current_kline_count() 控制判断节奏;收盘前用 order_target 恢复到初始头寸。

四、集合竞价追涨停(定时任务)

思路:每天 9:23 用 run_daily 触发,若最新价已触及涨停价则买入。运行频率:日线/分钟均可。

def initialize(context):
    g.security = '600570.SS'
    set_universe(g.security)
    run_daily(context, auction_func, time='9:23')

def auction_func(context):
    snap = get_snapshot(g.security)
    last_px = float(snap[g.security]['last_px'])
    up_px   = float(snap[g.security]['up_px'])
    if last_px >= up_px:
        order(g.security, 100, limit_price=up_px)
        log.info('追涨停买入 %s' % g.security)

def handle_data(context, data):
    pass

要点:run_daily 只能在 initialize 中注册;集合竞价阶段的实时行情要用 get_snapshot 而非 data 对象。

五、tick 级别均线策略(仅交易)

思路:每 3 秒触发一次,用最新 tick 价合成实时均线,金叉买入、死叉卖出。运行频率:tick(仅交易)。

def initialize(context):
    g.security = '600570.SS'
    set_universe(g.security)
    run_interval(context, tick_ma, seconds=3)

def before_trading_start(context, data):
    h = get_history(10, '1d', 'close', g.security, fq='pre', include=False)
    g.close_arr = h['close'].values

def tick_ma(context):
    snap = get_snapshot(g.security)
    price = snap[g.security]['last_px']
    ma5  = (price + g.close_arr[-4:].sum()) / 5
    ma10 = (price + g.close_arr[-9:].sum()) / 10

    if ma5 > ma10:
        order_value(g.security, context.portfolio.cash)
        log.info('tick 金叉买入')
    elif ma5 < ma10 and get_position(g.security).amount > 0:
        order_target(g.security, 0)
        log.info('tick 死叉卖出')

def handle_data(context, data):
    pass

要点:run_interval 只在交易时段(9:30~15:00)触发;tick_datarun_interval 累计线程数上限为 5,超过会堵塞。

小结

五个示例覆盖了 PTrade 的四种节奏:日线趋势、多股择时、分钟日内、盘前定时、tick 高频。抄起来改股票池和参数就能跑,但记住先回测、再模拟、最后小仓位试水。进阶内容看期货与定时任务:期货专用与定时任务