之前听过一句话:要想赚大钱必须学会长线持仓,但如果要赚快钱就要学会日内交易。如今的量化交易范围之广令人惊叹,各种交易策略层出不穷,其中最为流行的就是日内交易策略。日内交易是一种快进快出的交易方式,由于可以控制隔夜风险的特点,得到了很多交易者的推崇和接受。为了帮助大家了解日内交易,丰富策略仓库,本节我们将深入了解商品期货中最为流行的日内策略之一日内高低点突破策略。
日内交易的目的是以更小的损失,来获取当天市场微小的价格波动所带来的利润。它是指开仓和平仓在同一天内或同一交易时间段内完成的交易方式,开仓和平仓可以是单次,也可以是多次,只要是开平仓在同一个交易日前结束就行。
理论上日内交易不承担隔夜的跳空风险,是一种较完美的低风险交易策略,但实际上并非如此,虽然日内交易回避了跳空所带来的风险,同时也错失了跳空所带来的利润。但如果以正确的方式交易,通过配合不同的交易规则,日内交易往往也能产生丰厚的回报。
我们知道判断上涨趋势最简单的方法是,当前低点比前一个低点更高,当前高点也比前一个高点更高;同理下跌趋势最简单的方法是,当前低点比前一个低点更低,当前高点也比前一个高点更低。但如果仅仅以高低点的比较去判断趋势的涨跌,这未免太过简陋,因为价格可能在一个点上来回跳动几十次甚至上百次,从而导致交易过于频繁。
所以我们需要设定一个价格区间来过滤这些日常杂波,来对简单的高低点突破策略进行完善。我们可以根据历史行情所出现的最高价和最低价,组成一个包含上轨和下轨的通道。根据顺势交易的原则,当价格突破上轨时多头开仓,当价格突破下轨时空头开仓。
有人统计过,大部分的窄幅止损都是无效的,小空间的止损会频繁打脸,所以我们要做的就是设计一个宽幅止损:如果多头开仓后,价格不升反跌,我们所要做的不是立即止损,而是等待观望,直到价格跌破下轨才止损出局;空头开仓后也是如此,当价格不跌反升,继续等待价格是否会自我修正,直到跌破上轨才止损出局。
第1步:编写策略框架 编写策略就像盖房子一样,先把地基和框架搭建好,再往里面填充东西。我们这里用了两个函数,一个是main主函数,另一个是onTick函数,程序会先从main函数执行代码,在main函数中,我们用了一个无限循环模式,重复执行onTick函数。
# 策略主函数
def onTick():
pass
# 程序入口
def main():
while True: # 进入无限循环模式
onTick() # 执行策略主函数
Sleep(1000) # 休眠1秒
第2步:导入time库
# 导入库
import time
因为日内策略在编写的时候,要判断当前的时间来控制开平仓逻辑,这个策略在设计的时候是:只能在9点30分之14点50分之间开仓,14点50分之后全部平仓,其余的时间都过滤掉了。所以就需要引入time时间库。
第3步:设置全局变量
mp = 0 # 用于控制虚拟持仓
on_line = 0 # 上轨
under_line = 0 # 下轨
在全局变量中,mp主要用于控制虚拟持仓,判断持仓一般分为两种,一种是真实的账户持仓,另一种就是虚拟持仓,还有一种是真实持仓和虚拟持仓联合判断。实盘时我们只使用真实持仓就足够了,但这里为了简化策略,作为演示使用虚拟持仓。on_line和under_line分别记录上轨和下轨。
第4步:处理时间
# 处理时间函数
def can_time(hour, minute):
hour = str(hour)
minute = str(minute)
if len(minute) == 1:
minute = "0" + minute
return int(hour + minute)
exchange.SetContractType("MA888") # 订阅期货品种
bar_arr = exchange.GetRecords() # 获取K线数组
time_new = bar_arr[len(bar_arr) - 1]['Time'] # 获取当根K线的时间戳
time_local_new = time.localtime(time_new / 1000) # 处理时间戳
hour_new = int(time.strftime("%H", time_local_new)) # 格式化时间戳,并获取小时
minute_new = int(time.strftime("%M", time_local_new)) # 格式化时间戳,并获取分钟
day_new = int(time.strftime("%d", time_local_new)) # 格式化时间戳,并获取日期
time_previous = bar_arr[len(bar_arr) - 2]['Time'] # 获取上根K线的时间戳
time_local_previous = time.localtime(time_previous / 1000) # 处理时间戳
day_previous = int(time.strftime("%d", time_local_previous)) # 格式化时间戳,并获取日期
处理时间一共用于两个地方:一个是判断当前时间是否在我们规定的交易时间内,如果是在这个时间之内并且达到开仓条件就开仓,如果不是在这个时间之内并且当前有持仓就全部平仓;另一个是判断当前K线是不是最新交易日的K线,如果当前K线是最新交易日的K线,就重置on_line和under_line的值。
第4步:计算高低点上下轨
global mp, on_line, under_line # 引入全局变量
high = bar_arr[len(bar_arr) - 2]['High'] # 获取上根K线的最高价
low = bar_arr[len(bar_arr) - 2]['Low'] # 获取上根K线的最低价
if day_new != day_previous or len(bar_arr) == 1: # 如果当前是第一个K线,或者是最新一根K线
on_line = high * up # 重置上轨
under_line = low * down # 重置下轨
if can_time(hour_new, minute_new) < 930: # 如果不是在规定交易的时间内
if high > on_line: # 如果上根K线最高价大于上轨
on_line = high * up # 重置上轨
elif low < under_line: # 如果上根K线最低价小于下轨
under_line = low * down # 重置上轨
计算高低点上下轨的逻辑其实非常简单:如果当前是第一根K线,那么on_line和under_line的值分别是最高价和最低价,如果当前K线是最新交易日的K线,就重置on_line和under_line的值为最高价和最低价;一旦在规定的交易时间内,on_line和under_line的值就固定不变了,除非在这个时间之外并且如果上根K线最高价大于on_line就重置为最新的最高价;如果上根K线最低价小于under_line就重置为最新的最低价。
第5步:下单交易
close_new = bar_arr[len(bar_arr) - 1]['Close'] # 获取最新价格(卖价),用于开平仓
if mp == 0 and 930 < can_time(hour_new, minute_new) < 1450: # 如果当前无持仓,并且在规定的交易时间内
if close_new > on_line: # 如果价格大于上轨
exchange.SetDirection("buy") # 设置交易方向和类型
exchange.Buy(close_new, 1) # 开多单
mp = 1 # 设置虚拟持仓的值,即有多单
elif close_new < under_line: # 如果价格小于下轨
exchange.SetDirection("sell") # 设置交易方向和类型
exchange.Sell(close_new - 1, 1) # 开空单
mp = -1 # 设置虚拟持仓的值,即有空单
# 如果持多单,并且价格小于下轨或者非规定的交易时间
if mp > 0 and (close_new < under_line or can_time(hour_new, minute_new) > 1450):
exchange.SetDirection("closebuy") # 设置交易方向和类型
exchange.Sell(close_new - 1, 1) # 平多单
mp = 0 # 设置虚拟持仓的值,即空仓
# 如果持空单,并且价格大于上轨或者非规定的交易时间
if mp < 0 and (high > on_line or can_time(hour_new, minute_new) > 1450):
exchange.SetDirection("closesell") # 设置交易方向和类型
exchange.Buy(close_new, 1) # 平空单
mp = 0 # 设置虚拟持仓的值,即空仓
在下单交易之前,我们要先获取当前最新价格,因为在下单时需要在函数中传入下单价格。然后使用if语句,根据之前设计的交易逻辑,先是判断当前的持仓状态,然后再判断当前时间状态,以及最新价格与上下轨的相互位置关系,最后下单交易并重置虚拟持仓状态。需要注意的是在期货交易下单之前,先指定交易的方向类型,即:开多、开空、平多、平空。调用exchange.SetDirection()函数,分别传入:“buy”、“sell”、“closebuy”、“closesell”。
# 回测配置
'''backtest
start: 2015-02-22 00:00:00
end: 2019-10-17 00:00:00
period: 5m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
# 导入库
import time
# 定义全局变量
mp = 0 # 用于控制虚拟持仓
on_line = 0 # 上轨
under_line = 0 # 下轨
def onTick():
# 处理时间函数
def can_time(hour, minute):
hour = str(hour)
minute = str(minute)
if len(minute) == 1:
minute = "0" + minute
return int(hour + minute)
exchange.SetContractType("MA888") # 订阅期货品种
bar_arr = exchange.GetRecords() # 获取K线数组
time_new = bar_arr[len(bar_arr) - 1]['Time'] # 获取当根K线的时间戳
time_local_new = time.localtime(time_new / 1000) # 处理时间戳
hour_new = int(time.strftime("%H", time_local_new)) # 格式化时间戳,并获取小时
minute_new = int(time.strftime("%M", time_local_new)) # 格式化时间戳,并获取分钟
day_new = int(time.strftime("%d", time_local_new)) # 格式化时间戳,并获取日期
time_previous = bar_arr[len(bar_arr) - 2]['Time'] # 获取上根K线的时间戳
time_local_previous = time.localtime(time_previous / 1000) # 处理时间戳
day_previous = int(time.strftime("%d", time_local_previous)) # 格式化时间戳,并获取日期
global mp, on_line, under_line # 引入全局变量
high = bar_arr[len(bar_arr) - 2]['High'] # 获取上根K线的最高价
low = bar_arr[len(bar_arr) - 2]['Low'] # 获取上根K线的最低价
if day_new != day_previous or len(bar_arr) == 1: # 如果当前是第一个K线,或者是最新一根K线
on_line = high * up # 重置上轨
under_line = low * down # 重置下轨
if can_time(hour_new, minute_new) < 930: # 如果不是在规定交易的时间内
if high > on_line: # 如果上根K线最高价大于上轨
on_line = high * up # 重置上轨
elif low < under_line: # 如果上根K线最低价小于下轨
under_line = low * down # 重置上轨
close_new = bar_arr[len(bar_arr) - 1]['Close'] # 获取最新价格(卖价),用于开平仓
if mp == 0 and 930 < can_time(hour_new, minute_new) < 1450: # 如果当前无持仓,并且在规定的交易时间内
if close_new > on_line: # 如果价格大于上轨
exchange.SetDirection("buy") # 设置交易方向和类型
exchange.Buy(close_new, 1) # 开多单
mp = 1 # 设置虚拟持仓的值,即有多单
elif close_new < under_line: # 如果价格小于下轨
exchange.SetDirection("sell") # 设置交易方向和类型
exchange.Sell(close_new - 1, 1) # 开空单
mp = -1 # 设置虚拟持仓的值,即有空单
# 如果持多单,并且价格小于下轨或者非规定的交易时间
if mp > 0 and (close_new < under_line or can_time(hour_new, minute_new) > 1450):
exchange.SetDirection("closebuy") # 设置交易方向和类型
exchange.Sell(close_new - 1, 1) # 平多单
mp = 0 # 设置虚拟持仓的值,即空仓
# 如果持空单,并且价格大于上轨或者非规定的交易时间
if mp < 0 and (high > on_line or can_time(hour_new, minute_new) > 1450):
exchange.SetDirection("closesell") # 设置交易方向和类型
exchange.Buy(close_new, 1) # 平空单
mp = 0 # 设置虚拟持仓的值,即空仓
# 程序入口
def main():
while True:
onTick()
Sleep(1000) #休眠1秒
策略完整代码已经发布在优宽量化策略中心: https://www.youquant.com/strategy/175316 ,无需配置,直接在线回测。
预测今天下午的天气是很容易的,但是要想预测这个月内的天气却很难。日内交易不需要较长的持仓周期,所承受的市场波动风险较低,尽管这种交易方式不符合每个人的风格,但对于那些风险较为敏感的交易者来说,日内交易还是相当值得深入研究。
但需要警惕的是,日内交易策略并步具备反脆弱特性。如果说长线顺势策略的盈利基础是基于人性的话,那么短线交易是基于市场特性,也正是由于市场特性本身不断在进化的同时,导致很多日内策略失效。