开始写策略
一个简单的策略
#!/usr/bin/env python
# coding:utf-8
from PoboAPI import *
def OnStart(context) :
print "I\'m starting..."
g.code = "rb1901.SHFE"
SubscribeQuote( g.code)
if context.accounts.has_key("回测期货") :
print "登录交易账号 回测期货"
if context.accounts["回测期货"].Login() :
context.myacc = context.accounts["回测期货"]
def OnQuote(context,code) :
print '调用到OnQuote事件'
dyndata = GetQuote(g.code)
now1 = dyndata.now
option = PBObj()
klinedata = GetHisData(g.code, BarType.Day, option)
print klinedata[-1].close
if klinedata[len(klinedata)-1].close < klinedata[len(klinedata)-2].close :
# 当日收盘价小于昨收则买入
context.myacc.InsertOrder(g.code, BSType.BuyOpen, dyndata.now, 1)
elif klinedata[len(klinedata)-1].close > klinedata[len(klinedata)-2].close:
# 当日收盘价大于昨收则卖出
context.myacc.InsertOrder(g.code, BSType.SellClose, dyndata.now, 1)
一个完整的策略只需要两步:
- 设置开始事件OnStart,初始化一些信息,在上面例子中我们定义了全局合约代码“rb1901.SHFE”,rb1901是合约,SHFE是上期所的交易所代码,除此以外INE 上海国际能源交易中心、DCE 大连商品交易所、CZCE 郑州商品交易所、CFFEX 中金所。根据我们的需求可以把g.code改成当时的期货主力合约,并订阅了他的行情,登录了期货交易账号
- 实现一个事件,例子中实时行情事件,驱动交易下单,在上例中,当日收盘价小于昨收则买入;反之,则卖出平仓。
全局变量
全局对象g
全局对象g,用来存储用户的各类全局数据
示例
def OnStart(context) :
g.code = "rb1901.SHFE"
def OnBar(context,code,bartype):
print g.code
context
context对象,里面包含上下文信息,也可以用来存储用户的全局数据,与g用法上的区别,主要是context要在函数的参数中包含时才能调用;
另外context对象,内置的方法有账号集合,点击查看详情、backtest是否回测,回测时返回True
示例
def OnStart(context) :
context.counter = 0
def OnBar(context,code,bartype):
print context.counter
用户要用的事件
开始事件-OnStart
开始事件OnStart(context),在回测、模拟实盘中开始的时候执行,用于初始化一些变量。
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
返回
None
示例
def OnStart(context) :
print "I\'m starting..."
g.code = "rb1901.SHFE"
context.counter = 0
SubscribeQuote(g.code)
SubscribeBar(g.code,BarType.Day)
if context.accounts.has_key("回测期货") :
print "登录交易账号 回测期货"
context.accounts["回测期货"].Login()
例中,context.counter = 0是定义的在接下来其他事件中可能用到的变量; context.accounts是所有设置好的交易账号; g.code是全局变量。
回测取消事件-OnStop
回测取消事件OnStop(context),只在回测点取消时调用,回测正常结束或失败时不会调用。
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
返回
None
示例
def OnStop(context) :
print "I\'m exiting..."
定时器-OnTimer
定时器OnTimer(context, timerid),在时间达到设置的定时时间时调用;
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
timerid:时间ID
示例
#!/usr/bin/env python
# coding:utf-8
from PoboAPI import *
def OnStart(context) :
print "I\'m starting..."
context.upcounter = 0
TimerID = SetTimer(2)
def OnTimer(context, timerid) :
context.counter += 1
print "I\'ve been hitted by " + str(timerid) + " and Counter is " + str(context.counter) + " at " + str(GetCurrentTime())
if context.counter >= 5 :
ret = KillTimer(TimerID)
if ret :
print "KillTimer " + str(timerid)
定义TimerID = SetTimer(2),SetTimer用法加下每两秒调用一次Ontimer事件
实时行情事件-OnQuote
实时行情事件OnQuote(context, code),当有品种行情过来时,调用该事件。需要提前使用SubscribeQuote订阅行情才能触发,如在OnStart事件或行情初始化事件里SubscribeQuote订阅行情。
回测时,一般活跃品种的,如果按天回测,每天只有一笔行情,所以每天触发一次;按分钟回测时,每分钟一笔行情,所以每分钟触发一次;tick回测时,每tick触发一次;
实跑时,根据SubscribeQuote所订阅的品种行情情况,有一笔行情就触发一次;
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
code:合约
返回
None
示例
def OnStart(context) :
print "I\'m starting..."
g.code = "rb1901.SHFE"
SubscribeQuote( g.code)
def OnQuote(context,code) :
print '调用到OnQuote事件'
dyndata = GetQuote(g.code)
now1 = dyndata.now
K线更新事件-OnBar
K线更新事件OnBar(context,code,type),当有新的一根K线出现时,调用该事件。需要提前使用SubscribeBar订阅K线才能触发,在OnStart事件或行情初始化事件里SubscribeBar订阅K线。
根据订阅K线的频率触发,比如订阅的是5分钟K线,则每根5分钟K线刚出现的时候,调用到该事件;
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
code:合约
type :K线周期
示例
def OnStart(context) :
print "I\'m starting..."
g.code = "rb1901.SHFE"
SubscribeBar(g.code, BarType.Day)
def OnBar(context,code,bartype):
print '调用到OnBar事件'
闹钟事件—SetAlarm
闹钟事件SetAlarm(datetime,RepeatType),设置闹钟,当时间达到调用到该事件,SetAlarm的第二个参数是可选的,不提供则表示不需要重复。
参数
datetime : 闹钟要触发的时间,若传入datetime.datetime对象,则日期部分会被忽略,如SetAlarm(datetime.datetime(2018, 8, 31, 11, 20)),则2018年8月31号这个日期会被忽略,只有11点20这个时间点是有效的。
RepeatType :重复方式,RepeatType常量提供了四个值可供选择:Daily/Weekly/Hourly/Minutely,可不填,不填代表不重复,只调用闹钟一次。
示例
def OnStart(context) :
context.MyAlarm1 = SetAlarm(datetime.time(11, 10), RepeatType.Daily)
context.MyAlarm2 = SetAlarm(datetime.datetime(2018, 8, 31, 11, 20))
def OnAlarm(context, alarmid) :
print "调用到闹钟" + str(GetCurrentTime())
# 当日期超过设定值时,停止MyAlarm1
if hasattr(context, "MyAlarm1") and alarmid == context.MyAlarm1 and GetCurrentTime() >= datetime.datetime(2017, 1, 16) :
print "Kill MyAlarm1"
KillAlarm(context.MyAlarm1)
delattr(context, "MyAlarm1")
日程事件—SetSchedule
日程事件—SetSchedule(datetime,RepeatType),与闹钟事件Alarm不同的是,日程Schedule必须指定日期,只在日期也符合条件时触发,并且“过时不候”——如果设置的日期当时已经“过了”,那么不会再触发,即使设置了RepeatType也不会触发。
参数
datetime : 闹钟要触发的时间,datetime.datetime格式,如SetSchedule(datetime.datetime(2017, 1, 5, 11, 40), RepeatType.Daily)
RepeatType :重复方式,RepeatType常量提供了四个值可供选择:Daily/Weekly/Hourly/Minutely,可不填,不填代表不重复,只调用闹钟一次。
示例
def OnStart(context) :
# 设置日程,第二个参数的意义与SetAlarm相同
context.MySchedule1 = SetSchedule(datetime.datetime(2017, 1, 5, 11, 40), RepeatType.Daily)
context.MySchedule2 = SetSchedule(datetime.datetime(2017, 1, 5, 11, 50))
def OnSchedule(context, scheduleid) :
print "调用到日程事件 " + str(GetCurrentTime()) + str(scheduleid)
# 当日期超过设定值时,停止MySchedule1
if hasattr(context, "MySchedule1") and scheduleid == context.MySchedule1 and GetCurrentTime() >= datetime.datetime(2017, 1, 15) :
print "Kill MySchedule1"
KillSchedule(context.MySchedule1)
delattr(context, "MySchedule1")
委托回报事件-OnOrderChange
委托回报事件OnOrderChange(context, AccountName, order),当有委托回报推送过来时调用,如已委托回报,部分成交回报。
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
AccountName:账号名称
order:订单对象,点击查看具体属性
示例
def OnOrderChange(context, AccountName, order) :
print "OrderChanged on " + order.id + " on " + AccountName
成交回报事件-OnTradeDeal
成交回报事件OnTradeDeal(context, AccountName, trade),当有成交单时调用。
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
AccountName:账号名称
trade:订单对象,与委托回报事件中的order类似,除了:
- id:成交编号(TradeID);
- orderid:对应的委托编号(OrderID);
- volume:成交数量;
- price:成交价格。
示例
def OnTradeDeal(context, AccountName, trade) :
print "TradeDealed on " + trade.id + " on " + AccountName
智能单状态变化事件—OnSmartOrderChange
智能单状态变化事件—OnSmartOrderChange(context, AccountName, status),用户下智能单以后,智能单状态发生变化时,会调用该事件;点击查看智能单详情
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
AccountName:账号名称
status:智能单状态,一个对象,对象成员有:
- .UID 智能单ID
- .OrderNo 委托编号(普通委托时为效),当智能单只有最简单参数,没有追单和拆单参数时,会当做普通委托处理,可以看委托编号;
- .State 智能执行状态字,返回是int型,2代表执行中,3是首单已请求,6是已有成交还没完全完成,32是智能单已完成;
- .ErrorCode 出错码
- .ErrorInfo 出错信息
- .VolumeTotal 委托总数量
- .VolumeRemain 未拆单剩余数量(拆单时为效)
- .VolumeTraded 已成交数量
- .ReorderTotal 追单总次数(=0只有首次下单,没有追单)
- .OrderNbr 已下单次数, 包括下首单(追单时为效)
- .SubOrderIDs子单ID列表[uid1,uid2,…]
示例
def OnSmartOrderChange(context, AccountName, status):
print("========智能单回报推送 账号名称:" + AccountName )
DumpSmartOrderStatus(status)
def DumpSmartOrderStatus(status):
if not status:
print("智能单状态对象为空。")
return
print("====智能单状态:")
print("UID:"+str(status.UID)\
+ " 委托类型:"+str(status.OrderType)\
+ " 状态字:"+str(status.State)\
+ " 出错码:"+str(status.ErrorCode)\
+ " 出错信息:"+str(status.ErrorInfo)\
+ " 委托总数:"+str(status.VolumeTotal)\
+ " 已成交数:"+str(status.VolumeTraded)\
+ " 未拆数量"+str(status.VolumeRemain)\
+ " 追价总次数:"+str(status.ReorderTotal)\
+ " 已下单次数:"+str(status.OrderNbr))
断线事件-OnTradeAccountDisconnected
断线事件OnTradeAccountDisconnected(context, accountname),当交易断线时调用。
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
accountname:账号名称
示例
收到断线通知后,彻底断开账号,然后设置定时期,每60秒重连一次,这个时间间隔最少要大于45秒,重连成功后,结束掉定时器。
def OnTradeAccountDisconnected(context, accountname):
print '断线啦'
context.accounts["期货测试"].Logout()
g.timer = SetTimer(60)
print '开启定时器'
def OnTimer(context, timerid) :
if timerid ==g.timer:
print '定时期开始'
if context.accounts["期货测试"].Login():
print '重连成功啦'
KillTimer(g.timer)
print '结束定时器'
行情初始化事件-OnMarketQuotationInitial
行情初始化事件—OnMarketQuotationInitial(context, marketid),行情初始化时调用,一般夜盘交易所在晚上20点40分调用,白盘交易所在早上8点40调用。
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
marketed 行情市场代码:
上交所SHSE、深交所SZSE、上交所期权SHSEQQ、深交所期权SZSEQQ、上期所SHFE、上期所夜盘SHFEYP、上期所期权SHFEQQ、大商所DLCE、大商所夜盘DLCEYP、大商所期权DLCEQQ、郑商所CZCE、郑商所夜盘CZCEYP、郑商所期权CZCEQQ、中金所ZJS、上海能源中心SHINE
注意:该行情市场代码仅限于行情交易所开盘事件使用,指定某一期货合约时,需要用交易所代码。比如大商所的合约要做交易必须写成m1901.DCE,而不是.DLCE;
示例
夜盘行情初始化时,登出登入一遍交易账号,解决实盘交易中,每天收盘后交易账号被断开,下一交易日开盘后因为没有登录无法交易的问题。
def OnMarketQuotationInitial(context, marketid):
if marketid == 'SHFEYP':
print '上期所夜盘行情开盘时重登交易账号'
context.myacc.Logout()
context.myacc.Login()
开盘事件-OnExchangeOpen
开盘事件OnExchangeOpen(context, accountname, exchangecode, productcode),市场开盘时调用
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
accountname:账号名称
exchangecode:交易所,有’SHFE’、’DCE’、’CZCE’、’CFFEX’,分别对应上期所,大商所、郑商所、中金所
productcode:品种,郑商所字母要大写,比如’TA’;
注:有的柜台开收盘信号是按品种的,这时候如果只想触发一次,可以对交易所和品种同时过滤;而有的柜台开收盘信号只按交易所,这个时候只对交易所过滤就可以了;
收盘事件-OnExchangeClose
收盘事件OnExchangeClose(context, accountname, exchangecode, productcode),市场收盘时调用
参数
context:上下文信息,用于存储编写之前用户设置好的交易账号或者存储在其他事件中要用的变量。
accountname:账号名称
exchangecode:交易所,有’SHFE’、’DCE’、’CZCE’、’CFFEX’,分别对应上期所,大商所、郑商所、中金所
productcode:品种,郑商所字母要大写,比如’TA’;
注:有的柜台开收盘信号是按品种的,这时候如果只想触发一次,可以对交易所和品种同时过滤;而有的柜台开收盘信号只按交易所,这个时候只对交易所过滤就可以了;
示例
每天收盘时打印资金情况
def OnExchangeClose(context, accountname, exchangecode, productcode):
if exchangecode == 'DCE':
print '收盘啦++++++++++++++++++++++'
bal = context.myacc.AccountBalance
print '资金余额: ' + str(bal.CashBalance)
print '昨日资金余额: ' + str(bal.CashBalanceYesterday)
print '当日手续费: ' + str(bal.DailyFee)
print '当日盈亏: ' + str(bal.CashBalance - bal.CashBalanceYesterday)
print '浮动盈亏: ' + str(bal.FloatingProfit)
print '可用保证金: ' + str(bal.AvailableMargin)
print '持仓占用保证金: ' + str(bal.FrozenMargin)
print '净资产: ' + str(bal.NetAssets)
print '动态权益: ' + str(bal.DynamicNetAssets)
print '风险度: ' + str(bal.RiskDegree)