Loading ...

给商品期货策略加上一个闹钟--策略中的定时设计

Author: 小小梦, Created: 2020-07-28 09:24:54, Updated: 2020-08-03 09:18:12

给商品期货策略加上一个闹钟–策略中的定时设计

img

经常有设计策略的朋友问我,如何给策略设计定时功能,让策略在指定的时间去处理某些任务。例如,一些日内策略,需要在下午收盘前平仓。类似这样的需求在策略中要如何设计才好。一个策略里面可能要用到很多时间控制,这样来说我们把时间控制功能封装起来最好,最大程度降低时间控制代码与策略的耦合性,让这个时间控制模块可以复用,并且在使用方面简洁易懂。

设计一个“闹钟”

// triggerTime : 14:58:00
function CreateAlarmClock(triggerHour, triggerMinute) { 
    var self = {}                           // 构造的对象
    // 以下给构造的对象设置成员、函数
    
    self.isTrigger = false                  // 当天是否触发过
    self.triggerHour = triggerHour          // 计划触发的小时
    self.triggerMinute = triggerMinute      // 计划触发的分钟
    self.nowDay = new Date().getDay()       // 当前时间是哪日
    
    self.Check = function() {               // 检查函数,检查触发,触发返回true,未触发返回false    
        var t = new Date()                  // 获取当前时间对象
        var hour = t.getHours()             // 获取当前小数:0~23
        var minute = t.getMinutes()         // 获取当前分钟:0~59
        var day = t.getDay()                // 获取当前天数

        if (day != self.nowDay) {           // 判断,如果当前天,不等于记录的当天,重置触发标记为未触发,更新记录的天数
            self.isTrigger = false
            self.nowDay = day
        }

        if (self.isTrigger == false && hour == self.triggerHour && minute >= self.triggerMinute) {  
            // 判断时间是否触发,如果符合条件,设置标记isTrigger为true表示已经触发过
            self.isTrigger = true
            return true
        }

        return false    // 不符合触发条件,即为未触发
    }

    return self         // 返回构造完成的对象
}

我们设计并实现了一个创建闹钟对象的函数(可以理解为构造函数),其它语言直接可以设计一个闹钟类(例如使用Python,后续我们用Python实现一个)。

设计好构造“闹钟”对象的函数,在使用时只需一行代码即可创建一个“闹钟”对象。

var t = CreateAlarmClock(14, 58)

例如,创建一个对象t,并且定时每天14:58触发。 可以再创建一个对象t1,定时每天9:00触发。

var t1 = CreateAlarmClock(9, 0)

测试策略

我们写一个测试用的策略,策略使用最简单的均线系统,策略只是用来测试而已不用在意收益情况。 策略计划在每天9:00开盘时,根据日均线金叉、死叉判定开仓(做多、做空、不交易),并且在下午14:58时平仓(15:00收盘)。

function CreateAlarmClock(triggerHour, triggerMinute) { 
    var self = {}                           // 构造的对象
    // 以下给构造的对象设置成员、函数
    
    self.isTrigger = false                  // 当天是否触发过
    self.triggerHour = triggerHour          // 计划触发的小时
    self.triggerMinute = triggerMinute      // 计划触发的分钟
    self.nowDay = new Date().getDay()       // 当前时间是哪日
    
    self.Check = function() {               // 检查函数,检查触发,触发返回true,未触发返回false    
        var t = new Date()                  // 获取当前时间对象
        var hour = t.getHours()             // 获取当前小数:0~23
        var minute = t.getMinutes()         // 获取当前分钟:0~59
        var day = t.getDay()                // 获取当前天数

        if (day != self.nowDay) {           // 判断,如果当前天,不等于记录的当天,重置触发标记为未触发,更新记录的天数
            self.isTrigger = false
            self.nowDay = day
        }

        if (self.isTrigger == false && hour == self.triggerHour && minute >= self.triggerMinute) {  
            // 判断时间是否触发,如果符合条件,设置标记isTrigger为true表示已经触发过
            self.isTrigger = true
            return true
        }

        return false    // 不符合触发条件,即为未触发
    }

    return self         // 返回构造完成的对象
}

function main() {
    var q = $.NewTaskQueue()
    var p = $.NewPositionManager()
    
    // 可以写: var t = CreateAlarmClock(14, 58)
    // 可以写: var t1 = CreateAlarmClock(9, 0)
    
    var symbol = "i2009"  
    while (true) {
        if (exchange.IO("status")) {
            exchange.SetContractType(symbol)
            var r = exchange.GetRecords()
            if(!r || r.length < 20) {
                Sleep(500)
                continue   
            }
            if (/*判断9:00开仓的条件*/) {     // 可以写: t1.Check()
                var fast = TA.MA(r, 2)
                var slow = TA.MA(r, 5)
                
                var direction = ""
                if (_Cross(fast, slow) == 1) {
                    direction = "buy"
                } else if(_Cross(fast, slow) == -1) {
                    direction = "sell"
                }
                if(direction != "") {
                    q.pushTask(exchange, symbol, direction, 1, function(task, ret) {
                        Log(task.desc, ret)
                    })
                }
            }

            if (/*判断14:58临近收盘平仓的条件*/) {   // 可以写: t.Check()
                p.CoverAll()
            }

            q.poll()
            LogStatus(_D())
        } else {
            LogStatus(_D())
        }

        Sleep(500)
    }
}

在策略中放入我们已经实现的CreateAlarmClock函数,并且在main函数开始部分构造两个“闹钟”对象。在策略判断开仓、平仓的位置,加上“闹钟”对象调用Check函数的代码,如代码中注释掉的部分。

回测运行

img

可以看到回测,早上9点之后开仓,下午14:58开始平仓。

也可以用于多品种策略,在多品种策略中可以创建多个这样的“闹钟”对象,用于多品种的时间控制,互不影响。

Python语言实现闹钟类

实现以及测试代码:

import time
class AlarmClock:
    def __init__(self, triggerHour, triggerMinute):
        self.isTrigger = False 
        self.triggerHour = triggerHour
        self.triggerMinute = triggerMinute
        self.nowDay = time.localtime(time.time()).tm_wday

    def Check(self):
        t = time.localtime(time.time())
        hour = t.tm_hour
        minute = t.tm_min
        day = t.tm_wday
        
        if day != self.nowDay:
            self.isTrigger = False
            self.nowDay = day
            
        if self.isTrigger == False and hour == self.triggerHour and minute >= self.triggerMinute:
            self.isTrigger = True
            return True
        
        return False 

def main():
    t1 = AlarmClock(14,58)
    t2 = AlarmClock(9, 0)
    while True:
        if exchange.IO("status"):
            LogStatus(_D(), "已经连接!")
            exchange.SetContractType("rb2010")
            ticker = exchange.GetTicker()
            if t1.Check():
                Log("收盘", "#FF0000")
                
            if t2.Check():
                Log("开盘", "#CD32CD")
        else :
            LogStatus(_D(), "未连接!")
        Sleep(500)

回测测试运行: img

需要注意的是,回测测试运行,底层K线周期不能设置过大,否则可能直接跳过时间检测的点导致没有触发。

策略代码仅仅抛砖引玉提供思路,感谢阅读。


Related

More