资源加载中... loading...

菲阿里四价策略

Author: 扫地僧, Date: 2019-12-25 15:57:21
Tags: 商品期货趋势Python

摘要

在期货市场,价格呈现一切。几乎所有的技术分析,如均线、布林线、MACD、KDJ等等,这些都是以价格为基础,通过特定的方法计算。包括基本面分析也是如此,通过分析近期和远期价差、期货和现货升贴水、上下游库存等等数据,计算当前价格是否合理,并预估未来的价格。既然如此,为什么不直接研究价格呢?今天我们讲的菲阿里四价策略就是完全根据价格来做出卖决定。

菲阿里简介

菲阿里是一位日本的交易者,主要偏向于商品期货日内主观交易。其大名远扬是在2001年的罗宾斯(ROBBINS-TAICOM)期货冠军大赛中,以1098%的成绩获得冠军。并且在之后的两年里再以709%、1131%的成绩夺冠。从成绩就知道,菲阿里是一个非常优秀的交易者。

幸运的是,菲阿里在《1000%的男人》这本书中详尽叙述了他的交易方法,菲阿里四价策略正是后人总结他的交易方法。虽然只是从外在形式加上自己的理解模仿了一部分,并不代表菲阿里全部交易精髓,但至少可以帮助我们在构建策略时拓展思路。

策略逻辑

菲阿里四价策略是一种比较简单的趋势性日内交易策略,四价分别是指:昨天高点、昨天低点、昨日收盘价、今天开盘价。从书中的交易笔记来看,菲阿里不使用任何分析工具,而是大量应用阻溢线的概念,也就是通常我们所说的阻力线和支撑线。

  • 阻力线 = 昨日最高价
  • 支撑线 = 昨日最低价

对于阻力线和支撑线的定义,他使用的是昨日最高价和昨日最低价,可以视为昨天价格的波动范围,这也意味着多头或空头只有足够的力量时,才会有效突破阻力线和支撑线。并且一旦有突破这个波动范围,则说明价格背后的动能较大,后续走势可能会沿最小阻力线运动的概率较大。

  • 多头开仓:价格突破阻力线
  • 空头开仓:价格突破支撑线

img

如果开盘价处于阻力线和支撑线之间,当价格向上突破阻力线就建立多头,当价格向下突破支撑线就建立空头。如果一切顺利,则一直持仓到收盘。这样做的好处是,符合了充分非必要条件,即突破不一定上涨/下跌,但上涨/下跌一定会突破,也就是始终守在行情发生的必经之路伺机而动,因为较大行情的上涨和下跌一旦出现,势必要突破阻力线和支撑线的。

当然这也是出错率最高的方法,因为很多时候价格只是暂时性的突破了关键位置,如果贸然开仓可能会面临价格随时反向运动的风险。这时就需要设置一些过滤条件,限制假突破造成的来回开平仓问题。另外在交易周期上也尽量避免波动过于混乱的5分钟周期以下K线。

但是开仓后,盈利了还好,如果遇到亏损,总不能从小亏损一直积累到大亏损,才在收盘时平仓吧,这样显然不合理。所以我们对于平仓有两种处理方式:收盘平仓和止损平仓。如果K线上破高点或下破低点后又回到原来的区间内,就要考虑止损了。

  • 多头平仓:收盘前5分钟或达到多头止损线
  • 空头平仓:收盘前5分钟或达到空头止损线

img

实际上菲阿里在主观交易中,还有很多交易方法,包括:开盘后先涨后跌,跌破开盘价做空,止损设在之前上涨的最高点;开盘后先跌后涨,突破开盘价做多,止损设在之前下跌的最低点。动手能力强的朋友可以在自己的策略中增改。

到这里你会发现,对于一天的价格走势来说,收盘价相对于开盘价的涨跌,其概率接近于50%。菲阿里的交易方法在胜率上就利于不败之地,再加上行情顺利的时候一直持仓到收盘,在行情不符合自己的预期时及时止损。形成了截断亏损,让利润奔跑的正向交易方式,这也是长期交易下来积累利润的原因。

策略编写

第1步:编写策略框架

# 策略主函数
def onTick():
    pass


# 程序入口
def main():
    while True:  # 进入无限循环模式
        onTick()  # 执行策略主函数
        Sleep(1000)  # 休眠1秒

定义一个main函数和一个onTick函数,然后再main函数中,写入while循环,重复执行onTick函数。

第2步:导入库

import time  # 用于转换时间格式

因为我们这个是日内策略,到时候需要判断K线时间戳,如果有持仓,并且临近收盘时平仓出局。那么我们就直接import time就可以了。

第3步:获取基础数据

bar_arr = _C(exchange.GetRecords, PERIOD_D1)  # 获取日线数组
if len(bar_arr) < 2:  # 如果小于2根K线
    return  # 返回继续等待数据
yesterday_high = bar_arr[-2]['High']  # 昨日最高价
yesterday_low = bar_arr[-2]['Low']  # 昨日最低价
yesterday_close = bar_arr[-2]['Close']  # 昨日收盘价
today_open = bar_arr[-1]['Open']  # 当日开盘价

菲阿里四价需要用到四个数据:昨日最高价、昨日最低价、昨日收盘价、当日开盘价。因为这些都是日线级别的数据,所以我们在使用GetRecords的时候,可以直接传入PERIOD_D1参数,表明我们要获取的是日K,这样无论你的策略加载的是哪个周期的数据,它始终获取的都是日线级别的数据。

另外,细心的朋友可能已经发现,为什么这一次在调用GetRecords的时候,代码的写法跟以前不一样?这次我们使用的是优宽量化软件内置的重试函数_C()。使用这个函数的好处是,该函数会一直调用指定函数到成功返回,这样可以避免直接使用GetRecords函数时,没有获取到数据导致报错的情况。

第4步:处理时间和获取最新价格

bar_arr = _C(exchange.GetRecords)  # 获取当前设置周期K线数组
current_time = bar_arr[-1]['Time']  # 获取当前K线时间戳
time_local = time.localtime(current_time / 1000)  # 处理时间戳
hour = int(time.strftime("%H", time_local))  # 格式化时间戳,并获取小时
minute = int(time.strftime("%M", time_local))  # 格式化时间戳,并获取分钟
current_close = bar_arr[-1]['Close']  # 获取最新价格

既然是获取当前的时间,那么肯定是使用获取当前设置周期的K线数据更为合适,所以需要重新使用一次GetRecords,这次我们同样也是使用_C()重试函数,在不传入参数的情况下,就是默认获取当前设置周期的K线数组。另外,获取最新价格的目的是,计算交易逻辑和下单。

第5步:处理时间

def trade_time(hour, minute):
    hour = str(hour)
    minute = str(minute)
    if len(minute) == 1:
        minute = "0" + minute
    return int(hour + minute)

之所以创建这个函数,是因为我们在开仓之前,需要判断当前时间,是否在我们规定的交易时间之内,以及有持仓的时候,当前时间是否临近收盘。在第4步中,我们已经获取到了当前K线小时和分钟,为了方便比较,我们采用小时加分钟的方法,比如:如果K线时间是9:05,那么trade_time返回的结果就是905;如果K线时间是14:30,那么trade_time返回的结果就是1430等等。

第6步:获取持仓

real_position = _C(exchange.GetPosition)  # 获取持仓数组
if len(real_position) > 0:  # 如果持仓数组长度大于0
    real_position = real_position[0]
    if real_position['ContractType'] == 'rb888':  # 如果持仓品种等于订阅品种
        if real_position['Type'] == 0 or real_position['Type'] == 2:  # 如果是多单
            mp = real_position['Amount']  # 赋值持仓为正数
        elif real_position['Type'] == 1 or real_position['Type'] == 3:
            mp = -real_position['Amount']  # 赋值持仓为负数
else:
    mp = 0  # 赋值持仓为0

在之前的章节中,我们一直使用的是虚拟持仓,虽然编写起来简单,但只能适用于理想环境下。为了更贴近实战,在之后的章节中,我们将使用真实的持仓数据。要获取真实的持仓数据,其实也没那么难,直接使用优宽量化软件的GetPosition方法,就可以获取到当前账户的所有持仓。

GetPosition返回的是一个包含字典的一维数组:

# 例子
[{'MarginLevel': 16, 'Amount': 1.0, 'FrozenAmount': 0.0, 'Price': 3540.0, 'Profit': -30.0, 'Margin': 2124.0, 'Type': 0, 'ContractType': 'rb888'}]

其中,Amount是持仓数量、Price是开仓价格、Profit是当前利润、Type是多空方向(根据优宽量化API的定义,Type的值是0或2是代表多头,1或3代表空头)、ContractType是合约代码。而我们把多头持仓规整为正数,把空头持仓规整为负数。

第7步:设置止损

# 设置多头止损
if today_open / yesterday_high > 1.005:  # 如果当天开盘价大于昨天最高价
    long_stop_loss = yesterday_high  # 设置多头止损价为昨天最高价
elif today_open / yesterday_high < 0.995:  # 如果当天开盘价小于昨天最高价
    long_stop_loss = today_open  # 设置多头止损价为当天开盘价
else:  # 如果当天开盘价接近于昨天最高价
    long_stop_loss = (yesterday_high + yesterday_low) / 2  # 设置多头止损为昨天中间价

# 设置空头止损
if today_open / yesterday_low < 0.995:  # 如果当天开盘价小于昨天最低价
    short_stop_loss = yesterday_low  # 设置空头止损价为昨天最低价
elif today_open / yesterday_low > 1.005:  # 如果当天开盘价大于昨天最低价
    short_stop_loss = today_open  # 设置空头止损价为当天开盘价
else:  # 如果当天开盘价接近于昨天最低价
    short_stop_loss = (yesterday_high + yesterday_low) / 2  # 设置多头止损为昨天中间价

在大多数情况下,价格突破阻力线和支撑线,就把止损设置到当前开盘价这个位置。但是这里面有个问题:如果当天开盘价大于阻力线,而价格往下走;或者当天开盘价小于支撑线,而价格往上走,会造成逻辑错误,导致频繁开平仓。

为了解决这个问题,我们需要根据当天开盘与阻力线和支撑线的位置关系,分别设置不同的止损价格。如果当天开盘价大于昨天最高价0.5%,那么就把多头的止损设置在昨天的最高价;如果当天开盘价在阻力线和支撑线之间,那么多头的止损价格还是当天的开盘价;如果当天开盘价接近于昨天最高价,那么就把多头的止损设置在昨天的中间价。设置空头的止损也是根据这个道理。

第8步:下单交易

if mp > 0:  # 如果当前持有多单
    if current_close < long_stop_loss or trade_time(hour, minute) > 1450:  # 如果当前价格小于多头止损线,或者超过规定的交易时间
        exchange.SetDirection("closebuy")  # 设置交易方向和类型
        exchange.Sell(current_close - 1, 1)  # 平多单
if mp < 0:  # 如果当前持有空单
    if current_close > short_stop_loss or trade_time(hour, minute) > 1450:  # 如果当前价格大于空头止损线,或者超过规定的交易时间
        exchange.SetDirection("closesell")  # 设置交易方向和类型
        exchange.Buy(current_close + 1, 1)  # 平空单
if mp == 0 and 930 < trade_time(hour, minute) < 1450:  # 如果当前无持仓,并且在规定的交易时间内
    if current_close > yesterday_high:  # 如果当前价格大于昨天最高价
        exchange.SetDirection("buy")  # 设置交易方向和类型
        exchange.Buy(current_close + 1, 1)  # 开多单
    elif current_close < yesterday_low:  # 如果价格小于昨天最低价
        exchange.SetDirection("sell")  # 设置交易方向和类型
        exchange.Sell(current_close - 1, 1)  # 开空单

万事俱备就可以下单交易了,为了避免逻辑错误,最好是把平仓逻辑写到开仓逻辑的前面。

总结

虽然距离比赛结束已经有近20年之久了,但以今天的眼光看那时的交易,毫无过时感。但需要注意的是,策略仅仅作为思路拓展,不能直接用于实盘。对于菲阿里策略来说,它提供了一个很好的入场参考工具,我们可以根据自己对市场的认知做更深的开发,市场是不断进化的,如果笃信一个交易方式,最终会被市场淘汰。


import time  										# 导入库,用于转换时间格式

mp = 0  												# 虚拟持仓

def trade_time(hour, minute):
    minute = str(minute)
    if len(minute) == 1:
        minute = "0" + minute
    return int(str(hour) + minute)

def onTick():
    _C(exchange.SetContractType, "rb888")  			# 订阅期货品种
    bar_arr = _C(exchange.GetRecords, PERIOD_D1)	# 获取日线数组
    if len(bar_arr) < 2:  							# 如果小于2根K线
        return  										# 返回继续等待数据
    yh = bar_arr[-2]['High']  						# 昨日最高价
    yl = bar_arr[-2]['Low']  						# 昨日最低价
    today_open = bar_arr[-1]['Open']  				# 当日开盘价
    bar_arr = _C(exchange.GetRecords)  				# 获取当前设置周期K线数组
    current = bar_arr[-1]['Time']  					# 获取当前K线时间戳
    local = time.localtime(current / 1000)  		# 处理时间戳
    hour = int(time.strftime("%H", local))  		# 格式化时间戳,并获取小时
    minute = int(time.strftime("%M", local))  		# 格式化时间戳,并获取分钟
    price = bar_arr[-1]['Close']  					# 获取最新价格
    global mp
    # 设置多头止损
    if today_open / yh > 1.005:  				# 如果当天开盘价大于昨天最高价
        long_stop = yh  							# 设置多头止损价为昨天最高价
    elif today_open / yh < 0.995:  				# 如果当天开盘价小于昨天最高价
        long_stop = today_open  					# 设置多头止损价为当天开盘价
    else:  										# 如果当天开盘价接近昨天最高价
        long_stop = (yh + yl) / 2  				# 设置多头止损为昨天中间价
    # 设置空头止损
    if today_open / yl < 0.995:  				# 如果当天开盘价小于昨天最低价
        short_stop = yl  						# 设置空头止损价为昨天最低价
    elif today_open / yl > 1.005:  				# 如果当天开盘价大于昨天最低价
        short_stop = today_open  				# 设置空头止损价为当天开盘价
    else:  										# 如果当天开盘价接近昨天最低价
        short_stop = (yh + yl) / 2  				# 设置多头止损为昨天中间价
    # 下单交易
    trading = trade_time(hour, minute)
    if mp > 0:  									# 如果当前持有多单
# 如果当前价格小于多头止损线,或者超过规定的交易时间
        if price < long_stop or trading > 1450:  
            exchange.SetDirection("closebuy")  	# 设置交易方向和类型
            exchange.Sell(price - 1, 1)  			# 平多单
            mp = 0  								# 重置虚拟持仓
    if mp < 0:  									# 如果当前持有空单
# 如果当前价格大于空头止损线,或者超过规定的交易时间
        if price > short_stop or trading > 1450:  
            exchange.SetDirection("closesell")  	# 设置交易方向和类型
            exchange.Buy(price, 1)  				# 平空单
            mp = 0  								# 重置虚拟持仓
# 如果当前无持仓,并且在规定的交易时间内
    if mp == 0 and 930 < trading < 1450:  		
        if price > yh:  							# 如果当前价格大于昨天最高价
            exchange.SetDirection("buy")  		# 设置交易方向和类型
            exchange.Buy(price, 1)  				# 开多单
            mp = 1  								# 重置虚拟持仓
        elif price < yl:  						# 如果价格小于昨天最低价
            exchange.SetDirection("sell")  		# 设置交易方向和类型
            exchange.Sell(price - 1, 1)  			# 开空单
            mp = -1  								# 重置虚拟持仓

def main():
    while True:   								# 无限循环
        onTick()  								# 执行策略主函数
        Sleep(1000)  								# 休眠1秒
template: strategy.tpl:40:21: executing "strategy.tpl" at <.api.GetStrategyListByName>: wrong number of args for GetStrategyListByName: want 7 got 6