[量化学院-策略开发]多头排列回踩买入策略

多头排列
择时
趋势跟踪
标签: #<Tag:0x00007f5c070f5a90> #<Tag:0x00007f5c070f5900> #<Tag:0x00007f5c070f5720>

(iQuant) #1

什么是均线

金融市场上每个人都有一套自己的分析方法,无论你是一个技术派、基本面派、消息派还是量化投资派,对于“均线”这个名词一定不会陌生。虽说这个概念诞生于市场技术分析领域,但由于它的通俗易用,均线一直受到投资者和市场分析人士的青睐。

均线的全称是移动平均线(MA)。移动平均线是个什么概念?即通过等权或指数加权的方式,计算一段时期内的平均价格,是将某一段时间的收盘价之和除以该周期。 比如,日线MA5的意思就是说,5天内的收盘价除以5。

从这张图你应该可以看出,移动平均线,由于是一个均值的画线,因此它平滑了市场数据中的棱角和起伏波动,并且展示出已经走出来的基本价格趋势。

挺好理解吧?正因为均线是一个简洁易懂的概念,因此成为目前市场上运用最广泛的技术指标。不同周期的均线如何组合排列?如何交叉背离?与其他指标有怎么样的关系?这些问题背后的逻辑,都成为了均线应用最基本的理念支撑,这些不同的理念也创造了不同的均线策略。

今天给大家讲一讲,由均线衍生的策略:多头排列回踩点。

正如标题所言,这个策略有两个非常醒目的特点,好懂、好用。好懂,是指这个策略本身并没有什么复杂的概念,理解起来非常容易;好用,是指这个策略的应用效果非常不错。本文就从这两个角度入手,手把手教大家如何玩赚这个策略!

什么是多头排列回踩点?

想明白这7个字组合在一起的含义,咱们先把它拆分一下。什么是多头排列?什么是回踩点?

多头排列首先是一个均线排列形态,能够预判趋势目前是多头占上风的。而这个预判背后所支撑的逻辑,源于均线的排列方式。人们在长期使用均线这个指标的过程中,通过总结经验和数据,认为均线的排列可以粗分为两种情况:多头排列和空头排列。多头排列就是市场趋势是强势上升势,均线在5—10—20—40—120K线下支撑排列向上为多头排列。我们先看一个简单的三均线多头排列形态。

从图可以看出,多头排列形态的判定标准是:短期均线依次在长期均线之上,因此股价有较好的支撑。说的再简单一些,如果把均线理解为买入成本的平均值,均线排列依次短期线、中期线、长期线由上而下依次排列,这说明我们过去买进的成本很低,做短线的、中线的、长线的都有赚头,市场一片向上均线多头排列趋势为强势上升势,操作思维为多头思维。

回踩点又是什么含义呢?很简单,就是价格前期冲高后出现了小幅回调下行。值得注意的是,在这个策略中,回踩是有条件的,即回踩幅度并没有破坏均线多头排列的格局。在这种情况下,由于每一条不同周期的均线都是支撑,所以回撤往往成为一个较理想的进场点位。假如某只股票,前期多头排列,但是回踩之后,整体均线形态受到巨大破坏,那么这只股票就不符合我们的筛选条件了。

回踩点能够具体的量化,如果在多头排列的状态下当根K线击穿了10日均线,那么可以视作这根K线是一个回踩点,发出买入信号。咱们来看看2017年2月以来贵州茅台(600519)的K线图上出现的几个多头趋势回踩点,现在看来,这几个回踩点都是绝好的回调买入机会。

多头排列回踩买入策略蕴含了投资过程中最重要的两个核心思想,一是选股,从3000只股票中选出目前走势符合多头排列的股票,因为这样的股票后市更有上涨空间,如果某只股票有一波趋势,那么这种简单的选股法则并不会遗漏这只股票,就拿贵州茅台举例,这只“白马股”一路走牛,通过市盈率、市值等因子很可能选不到这只股票,但是多头排列选股并不会错失这只股票。二是择时,当股票选出来以后,我们要择时买入,而回踩点正是一个绝好的时机进场。

交易系统

验证策略有效性需要通过在历史数据上进行客观准确的回测,我们先构建该策略完整交易系统:

  1. 股票池为所有沪深A股

  2. 均线周期选择为[5,10,20,40,120],短期均线值依次大于长期均线值

  3. 回踩点的定义为当根K线击穿10日均线,未击穿更长周期均线,并仍满足多头排列

  4. 买入股票数上限为100只,等权重买入,当持有股票数100只时,有股票卖出才买入股票

  5. 买入时机:多头策略回踩点的第二天开盘买入

  6. 卖出时机:当5日均线下穿40日均线,第二天开盘卖出

回测结果

以下为回测结果。回测时间为2013年年初到2015年1月,总收益78.67%,年化收益率为34.3%,因为回测期间本身大盘也不错,所以还需参看更长时间的表现。本文目的是希望大家对回测机制更为熟悉,更能灵活地开发量化策略。


完整的策略如下,大家可以 克隆到自己的账户进行研究。

克隆策略

数据准备函数

In [7]:
def prepare(context):
    # 加载原始数据
    stock_raw_data = D.history_data(context.instruments, context.start_date, context.end_date, ['close','low'])
    # 包含多个周期均线值的股票数据
    stock_ma_data = stock_raw_data.groupby('instrument').apply(ma_calculate)
    # 每日买入股票的数据框
    context.daily_stock_to_buy= stock_ma_data.groupby('date').apply(open_pos_con)
    # 每日卖出股票的数据框
    context.daily_stock_to_sell= stock_ma_data.groupby('date').apply(close_pos_con)

# 计算多个周期均线的函数
def ma_calculate(df):
    ma_list = [5,10,20,40,120]
    for ma_len in ma_list:
        df['ma_'+str(ma_len)] = pd.rolling_mean(df['close'], ma_len)
    return df

# 函数:求满足开仓条件的股票列表
def open_pos_con(df):
    return list(df[(df['ma_5']>df['ma_10'])&(df['ma_10']>df['ma_20'])&(df['ma_20']>df['ma_40'])&(df['ma_40']>df['ma_120'])&(df['low']<df['ma_10'])].instrument)

# 函数:求满足平仓条件的股票列表
def close_pos_con(df):
    return list(df[df['ma_5']<df['ma_40']].instrument)

策略逻辑主体函数

In [8]:
# 初始化虚拟账户状态,只在第一个交易日运行
def initialize(context):
    # 设置手续费,买入时万3,卖出是千分之1.3,不足5元以5元计
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    context.num_stock = 100 # 最多同时持有100只股票
    
# 策略交易逻辑,每个交易日运行一次
def handle_data(context, data):
    date = data.current_dt.strftime('%Y-%m-%d')  # 日期
    buy_stock = context.daily_stock_to_buy[date]  # 当日符合买入条件的股票
    sell_stock = context.daily_stock_to_sell[date]  # 当日符合卖出条件的股票
    weight = 1/context.num_stock   # 等权重配置
    stock_hold_num = len(context.portfolio.positions)  # 目前持有的股票数量
    remain_num = context.num_stock - stock_hold_num   # 还可以买入的股票数量,需要把卖出的股票加回来
    # 初始化当日买入订单的数量为0 
    order_count = 0
    # 买入股票
    for i in buy_stock:
        # 如果发送买入订单的股票数量已经超过了还可以买入的股票数量,那么应退出for循环
        if order_count >= remain_num:
            break
        # 对于没有买入的股票且可以交易的股票,应买入
        if context.portfolio.positions[context.symbol(i)].amount == 0 and data.can_trade(context.symbol(i)):
            order_target_percent(context.symbol(i), weight)
            order_count += 1  # 统计一下当天股票买入数量
    # 卖出股票
    for j in sell_stock:
        if context.portfolio.positions[context.symbol(j)].amount > 0 and data.can_trade(context.symbol(j)):
            order_target_percent(context.symbol(j), 0)

策略回测接口

In [9]:
# 策略回测接口: https://bigquant.com/docs/strategy_backtest.html
m = M.trade.v3(
    instruments=D.instruments(market='CN_STOCK_A'),
    start_date='2013-01-01',
    end_date='2015-01-21',
    prepare=prepare, # 数据准备函数
    initialize=initialize, # 初始化函数
    handle_data=handle_data, # 策略主体函数
    # 买入订单以开盘价成交
    order_price_field_buy='open',
    # 卖出订单以开盘价成交
    order_price_field_sell='open',
    capital_base=1000000,
    benchmark='000300.INDX',
)
[2018-03-22 20:25:10.915836] INFO: bigquant: backtest.v7 开始运行..
[2018-03-22 20:25:52.494662] INFO: algo: set price type:backward_adjusted
[2018-03-22 20:28:30.866130] INFO: Performance: Simulated 496 trading days out of 496.
[2018-03-22 20:28:30.870726] INFO: Performance: first open: 2013-01-04 01:30:00+00:00
[2018-03-22 20:28:30.872439] INFO: Performance: last close: 2015-01-21 07:00:00+00:00
  • 收益率78.67%
  • 年化收益率34.3%
  • 基准收益率40.66%
  • 阿尔法0.22
  • 贝塔0.54
  • 夏普比率1.46
  • 胜率0.428
  • 盈亏比2.838
  • 收益波动率20.29%
  • 信息比率0.79
  • 最大回撤17.89%
[2018-03-22 20:28:38.278630] INFO: bigquant: backtest.v7 运行完成[207.362808s].

社区干货与精选整理(持续更新中...)
鳄鱼线策略,请大神帮忙看看,为何报错
求一个针对dateframe的函数定义及调用范例!