{"action":"created","issue":{"url":"https://api.github.com/repos/vnpy/vnpy/issues/601","repository_url":"https://api.github.com/repos/vnpy/vnpy","labels_url":"https://api.github.com/repos/vnpy/vnpy/issues/601/labels{/name}","comments_url":"https://api.github.com/repos/vnpy/vnpy/issues/601/comments","events_url":"https://api.github.com/repos/vnpy/vnpy/issues/601/events","html_url":"https://github.com/vnpy/vnpy/issues/601","id":274077958,"number":601,"title":"完善TargetPosTemplate模板","user":{"login":"miyihuxi","id":30117230,"avatar_url":"https://avatars3.githubusercontent.com/u/30117230?v=4","gravatar_id":"","url":"https://api.github.com/users/miyihuxi","html_url":"https://github.com/miyihuxi","followers_url":"https://api.github.com/users/miyihuxi/followers","following_url":"https://api.github.com/users/miyihuxi/following{/other_user}","gists_url":"https://api.github.com/users/miyihuxi/gists{/gist_id}","starred_url":"https://api.github.com/users/miyihuxi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miyihuxi/subscriptions","organizations_url":"https://api.github.com/users/miyihuxi/orgs","repos_url":"https://api.github.com/users/miyihuxi/repos","events_url":"https://api.github.com/users/miyihuxi/events{/privacy}","received_events_url":"https://api.github.com/users/miyihuxi/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2017-11-15T08:51:50Z","updated_at":"2017-11-15T09:38:52Z","closed_at":null,"author_association":"NONE","body":"## 环境\r\n\r\n* 操作系统: 如Windows 7或者Ubuntu 16.04\r\n* Anaconda版本: 如Anaconda 4.0.0 Python 2.7 32位\r\n* vn.py版本: 如v1.7发行版或者dev branch 20170920（下载日期）\r\n\r\n## Issue类型\r\n三选一：Bug\r\n\r\n## 预期程序行为\r\n量衍投资编写的　TargetPosTemplate　思路是非常好的，而且代码也很简洁，值得学习。有一些细节我完善了一下，希望大家指正，如下请大家参考测试。\r\n```\r\n########################################################################\r\nclass TargetPosTemplate(CtaTemplate):\r\n    \"\"\"\r\n    允许直接通过修改目标持仓来实现交易的策略模板\r\n    \r\n    开发策略时，无需再调用buy/sell/cover/short这些具体的委托指令，\r\n    只需在策略逻辑运行完成后调用setTargetPos设置目标持仓，底层算法\r\n    会自动完成相关交易，适合不擅长管理交易挂撤单细节的用户。    \r\n    \r\n    使用该模板开发策略时，请在以下回调方法中先调用母类的方法：\r\n    onTick\r\n    onBar\r\n    onOrder\r\n    \r\n    假设策略名为TestStrategy，请在onTick回调中加上：\r\n    super(TestStrategy, self).onTick(tick)\r\n    \r\n    其他方法类同。\r\n    \"\"\"\r\n    \r\n    className = 'TargetPosTemplate'\r\n    author = u'量衍投资编写　胡曦完善'\r\n    \r\n    # 目标持仓模板的基本变量\r\n    tickAdd = 1             # 委托时相对基准价格的超价\r\n    lastTick = None         # 最新tick数据\r\n    lastBar = None          # 最新bar数据\r\n    targetPos = EMPTY_INT   # 目标持仓\r\n    orderDict = {}          # 委托号字典\r\n    \r\n    cancelOrderList = []    # 已发出撤单指令的委托号列表\r\n    lastDatetime = None     # 最新时间\r\n\r\n    # 变量列表，保存了变量的名称\r\n    varList = ['inited',\r\n               'trading',\r\n               'pos',\r\n               'targetPos']\r\n\r\n    #----------------------------------------------------------------------\r\n    def __init__(self, ctaEngine, setting):\r\n        \"\"\"Constructor\"\"\"\r\n        super(TargetPosTemplate, self).__init__(ctaEngine, setting)\r\n        \r\n    #----------------------------------------------------------------------\r\n    def onTick(self, tick):\r\n        \"\"\"收到行情推送\"\"\"\r\n        self.lastTick = tick\r\n        #一般来讲，由tick维护最新时间\r\n        self.lastDatetime = tick.datetime\r\n        \r\n        # 实盘模式下，启动交易后，需要根据tick的实时推送执行自动开平仓操作\r\n        if self.trading:\r\n            self.trade()\r\n        \r\n    #----------------------------------------------------------------------\r\n    def onBar(self, bar):\r\n        \"\"\"收到K线推送\"\"\"\r\n        self.lastBar = bar\r\n        #如果是bar模式，则由onBar来维护最新时间\r\n        if not self.lastDatetime: \r\n            self.lastDatetime = bar.datetime\r\n        elif (bar.datetime - self.lastDatetime).total_seconds() > 0:\r\n            self.lastDatetime = bar.datetime\r\n\r\n        # 实盘模式下，启动交易后，需要根据bar的实时推送执行自动开平仓操作\r\n        if self.trading:\r\n            self.trade()\r\n\r\n    #----------------------------------------------------------------------\r\n    def onOrder(self, order):\r\n        \"\"\"收到委托推送\"\"\"\r\n        if order.status == STATUS_ALLTRADED or order.status == STATUS_REJECTED:\r\n            del self.orderDict[order.vtOrderID]\r\n        elif order.status == STATUS_CANCELLED:\r\n            del self.orderDict[order.vtOrderID]\r\n            self.cancelOrderList.remove(vtOrderID)\r\n            \r\n    #----------------------------------------------------------------------\r\n    def setTargetPos(self, targetPos):\r\n        \"\"\"设置目标仓位\"\"\"\r\n        self.targetPos = targetPos\r\n        \r\n        self.trade()\r\n        \r\n    #----------------------------------------------------------------------\r\n    def trade(self):\r\n        \"\"\"执行交易\"\"\"\r\n        # 如果目标仓位和实际仓位一致，则不进行任何操作\r\n        posChange = self.targetPos - self.pos\r\n        if not posChange:\r\n            return\r\n        else:\r\n            # 如果目标仓位与实际仓位有差异，则先撤销之前的委托\r\n            if len(self.orderDict) > 0:\r\n                for vtOrderID in self.orderDict.keys():\r\n                    #如果该委托正在被撤消，则忽略\r\n                    if vtOrderID in self.cancelOrderList:\r\n                        pass\r\n                    else:\r\n                        #如果委托时间超过4秒，则撤单\r\n                        if abs((self.lastDatetime - self.orderDict[vtOrderID]).total_seconds()) > 4:\r\n                            self.cancelOrderList.append(vtOrderID)\r\n                            self.cancelOrder(vtOrderID)\r\n                return\r\n                    \r\n                    \r\n        # 检查之前委托都已结束\r\n        #if self.orderDict:\r\n        #    return\r\n        \r\n        \r\n        # 确定委托基准价格，有tick数据时优先使用，否则使用bar\r\n        longPrice = 0\r\n        shortPrice = 0\r\n        \r\n        if self.lastTick:\r\n            if posChange > 0:\r\n                longPrice = self.lastTick.askPrice1 + self.tickAdd\r\n            else:\r\n                shortPrice = self.lastTick.bidPrice1 - self.tickAdd\r\n        else:\r\n            if posChange > 0:\r\n                longPrice = self.lastBar.close + self.tickAdd\r\n            else:\r\n                shortPrice = self.lastBar.close - self.tickAdd\r\n        \r\n        # 回测模式下，采用合并平仓和反向开仓委托的方式\r\n        if self.getEngineType() == ENGINETYPE_BACKTESTING:\r\n            if posChange > 0:\r\n                l = self.buy(longPrice, abs(posChange))\r\n            else:\r\n                l = self.short(shortPrice, abs(posChange))\r\n            #过滤掉空字符串\r\n            for s in l:\r\n                if not s:\r\n                    pass\r\n                else:\r\n                    self.orderDict[s] = self.lastDatetime\r\n        \r\n        # 实盘模式下，首先确保之前的委托都已经结束（全成、撤销）\r\n        # 然后先发平仓委托，等待成交后，再发送新的开仓委托\r\n        else:\r\n            \r\n            # 买入\r\n            if posChange > 0:\r\n                if self.pos < 0:\r\n                    l = self.cover(longPrice, abs(self.pos))\r\n                else:\r\n                    l = self.buy(longPrice, abs(posChange))\r\n            # 卖出\r\n            else:\r\n                if self.pos > 0:\r\n                    l = self.sell(shortPrice, abs(self.pos))\r\n                else:\r\n                    l = self.short(shortPrice, abs(posChange))\r\n            #过滤掉空字符串\r\n            for s in l:\r\n                if not s:\r\n                    pass\r\n                else:\r\n                    self.orderDict[s] = self.lastDatetime\r\n    \r\n    \r\n########################################################################\r\n```\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/vnpy/vnpy/issues/comments/344537958","html_url":"https://github.com/vnpy/vnpy/issues/601#issuecomment-344537958","issue_url":"https://api.github.com/repos/vnpy/vnpy/issues/601","id":344537958,"user":{"login":"miyihuxi","id":30117230,"avatar_url":"https://avatars3.githubusercontent.com/u/30117230?v=4","gravatar_id":"","url":"https://api.github.com/users/miyihuxi","html_url":"https://github.com/miyihuxi","followers_url":"https://api.github.com/users/miyihuxi/followers","following_url":"https://api.github.com/users/miyihuxi/following{/other_user}","gists_url":"https://api.github.com/users/miyihuxi/gists{/gist_id}","starred_url":"https://api.github.com/users/miyihuxi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miyihuxi/subscriptions","organizations_url":"https://api.github.com/users/miyihuxi/orgs","repos_url":"https://api.github.com/users/miyihuxi/repos","events_url":"https://api.github.com/users/miyihuxi/events{/privacy}","received_events_url":"https://api.github.com/users/miyihuxi/received_events","type":"User","site_admin":false},"created_at":"2017-11-15T09:38:52Z","updated_at":"2017-11-15T09:38:52Z","author_association":"NONE","body":"```\r\n        if self.lastTick:\r\n            if posChange > 0:\r\n                longPrice = self.lastTick.askPrice1 + self.tickAdd\r\n            else:\r\n                shortPrice = self.lastTick.bidPrice1 - self.tickAdd\r\n```\r\n这段代码有个隐患，如果遇上涨跌停板而策略又发出了改变仓位的信号时，会出问题，要仔细想一想再做修改。"}}
{"actor":{"display_login":"miyihuxi"}}