基于VNPY实现网格策略实盘(币圈)
基于VNPY实现网格策略实盘(币圈)
目录
- 基于VNPY实现网格策略实盘(币圈)
- vnpy事件驱动框架
- 交易所gateway
- vnpy算法引擎
- vnpy数据格式
- algo类和算法模板template
- 网格交易策略逻辑
- 程序入口
- 策略实战
在回测程序中摸爬滚打了几个月,现在发现vnpy作为实盘系统,非常方便。
vnpy事件驱动框架
首先我们要利用到vnpy的事件驱动框架,是一个消息队列。其中,交易所gateway就是事件的生产者,算法模块AlgotradingApp是队列的消费者。为了能够获取行情,我们要让网格策略算法监听委托、成交回报、订单回报、行情tick这几种事件。
- tick、trade、order事件
在交易所gateway中,on_tick方法可以将tick数据推送到事件引擎中,其他事件以此类推:
def on_tick(self, tick: TickData) -> None:"""Tick event push.Tick event of a specific vt_symbol is also pushed."""self.on_event(EVENT_TICK, tick)self.on_event(EVENT_TICK + tick.vt_symbol, tick)
在事件的消费者算法引擎中,需要注册此事件即可获得tick的数据,并给此事件准备好handler函数:
def register_event(self):""""""self.event_engine.register(EVENT_TICK, self.process_tick_event)self.event_engine.register(EVENT_TIMER, self.process_timer_event)self.event_engine.register(EVENT_ORDER, self.process_order_event)self.event_engine.register(EVENT_TRADE, self.process_trade_event)
tick事件的handler函数如下:
def process_tick_event(self, event: Event):""""""tick = event.dataalgos = self.symbol_algo_map.get(tick.vt_symbol, None)if algos:for algo in algos:algo.update_tick(tick)
首先它会取出data,然后将此数据映射到相应的算法中。
交易所gateway
交易所的gateway就是程序与交易数据收发的入口,里面集成了rest、websocket、行情、交易的接口。本文使用的okex交易所的gateway。交易所的各种数据就是通过各种on开头的回调函数传递到事件引擎中,再被算法引擎监听获得。
def __init__(self, event_engine: EventEngine, gateway_name: str = "OKEX") -> None:"""构造函数"""super().__init__(event_engine, gateway_name)self.rest_api: "OkexRestApi" = OkexRestApi(self)self.ws_public_api: "OkexWebsocketPublicApi" = OkexWebsocketPublicApi(self)self.ws_private_api: "OkexWebsocketPrivateApi" = OkexWebsocketPrivateApi(self)
vnpy算法引擎
算法引擎负责处理各种事件的数据并映射到对应的算法中,首先来看看其初始化函数:
class AlgoEngine(BaseEngine):""""""setting_filename = "algo_trading_setting.json"def __init__(self, main_engine: MainEngine, event_engine: EventEngine):"""Constructor"""super().__init__(main_engine, event_engine, APP_NAME)self.algos = {}self.symbol_algo_map = {}self.orderid_algo_map = {}self.algo_templates = {}self.algo_settings = {}self.load_algo_template()self.register_event()
其中有两个比较重要的字典,用来映射订单、行情到对应的algo类中,每个algo类就是不同的算法。
vnpy数据格式
vnpy的所有事件数据都有它自己的dataclass,比如交易数据TradeData:
@dataclass
class TradeData(BaseData):"""Trade data contains information of a fill of an order. One ordercan have several trade fills."""symbol: strexchange: Exchangeorderid: strtradeid: strdirection: Direction = Noneoffset: Offset = Offset.NONEprice: float = 0volume: float = 0datetime: datetime = Nonedef __post_init__(self):""""""self.vt_symbol = f"{self.symbol}.{self.exchange.value}"self.vt_orderid = f"{self.gateway_name}.{self.orderid}"self.vt_tradeid = f"{self.gateway_name}.{self.tradeid}"
可以看到我们可以通过trade数据得知这个交易明细的交易所,订单号,价格,数量,时间,这些都是算法类需要利用到的数据
algo类和算法模板template
为了实现自己编写的算法,我们需要写一个算法类,所有编写的算法都需要继承算法模板AlgoTemplate类,从而与算法引擎进行交互。
class GridAlgo(AlgoTemplate):""""""display_name = "Grid 网格交易"default_setting = {"vt_symbol": "","upper_limit": "","lower_limit": "","investment": "","grid_step": 0.0,"grid_levels":0,"interval": 10,"stop_loss": 0,"trailing_up":False,}
算法类再继承AlgoTemplate之后,就可以开始编写策略逻辑。在算法引擎中收到的Trade、Order事件数据都可以在算法类中利用on_trade,on_order进行处理。
更新算法的仓位数据时,利用on_trade方法:
def on_trade(self, trade: TradeData):""""""if trade.direction == Direction.LONG:self.pos += trade.volumeelse:self.pos -= trade.volume
多单LONG仓位增加,空单SHORT仓位减小。至于策略逻辑可以写在on_timer函数中,定时根据行情检测买卖条件。
网格交易策略逻辑
创建好了algo类接下来就可以编写策略逻辑,为了实现网格交易首先我们需要下限价单,根据网格上界与下界,以及制定好的间隔下单:
def send_all_limit_orders(self, tick):# 先购买足够多的币用来下网格卖单grid_price = []for price in np.arange(self.lower_limit, self.upper_limit,(self.upper_limit - self.lower_limit) / self.grid_levels):grid_price.append(price)# sell_order_count = 0sell_order_amount = 0for price in grid_price:if price > tick.last_price:sell_order_amount += self.grid_usdt_amounts# 为了能够刚好下完所有限价,多买入2%的币buy_amount = sell_order_amount*1self.buy(self.vt_symbol,price=tick.ask_price_1,volume=round(buy_amount, 5),order_type=OrderType.MARKET)time.sleep(3)print(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'):}买入初始仓位完成")# 开始下限价单if len(self.active_orders) < self.grid_levels:for price in np.arange(self.lower_limit, self.upper_limit, (self.upper_limit-self.lower_limit)/self.grid_levels):if price < tick.last_price:volume = self.grid_usdt_amountsorderid = self.buy(volume=round(volume/price, 5),price=round(price,5),vt_symbol=self.vt_symbol)else:volume = self.grid_usdt_amountsorderid = self.sell(volume=round(volume/price, 5),price=round(price,5),vt_symbol=self.vt_symbol)self.order_book[orderid] = round(price,5)if tick.gateway_name == 'BINANCE':# 币安10秒内最多50个委托time.sleep(0.3)if tick.gateway_name == 'OKEX':# 币安10秒内最多50个委托time.sleep(0.1)return True
成功下单之后我们就需要利用on_timer函数定时检测是否有网格成交并且填充网格:
def on_timer(self):""""""tick = self.get_tick(self.vt_symbol)if not tick:print(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}:暂未收到行情")returnif not self.is_init:if self.is_sbot:self.send_all_limit_orders_sbot(tick)else:self.send_all_limit_orders(tick)self.is_init = True# 最多容忍2个订单差if not self.is_all_sent:if len(self.order_book) - len(self.active_orders) >= 2:print(f"本地订单簿长度{len(self.order_book)},在线订单{len(self.active_orders)}")print(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}:所有限价单订单回报未完全更新")returnelse:print(f"本地订单簿长度{len(self.order_book)},在线订单{len(self.active_orders)}")self.is_all_sent = Trueif tick.last_price <= self.stop_loss:# 到达止损价,即使止损print(f"达到止损价{self.stop_loss}, 撤销所有限价单,卖出现有仓位")self.cancel_all()self.sell(volume=self.pos,price=tick.ask_price_5,vt_symbol=self.vt_symbol)if not self.is_sbot:if self.is_all_sent:self.check_grid_count(tick)else:if self.is_all_sent:self.check_grid_count_sbot(tick)
如果符合条件就填充网格
def check_grid_count_sbot(self, tick):print(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}:现在活跃订单簿数量:{len(self.active_orders)},本地订单数量{len(self.order_book)}")# 每五分钟检测一次网格是否未填充if self.stuck:returnself.stuck = Truediff = self.order_book.keys() - self.active_orders.keys()# 找出离lasttick最近的网格的index,对其暂不填充price_diff = []for order in diff:price = self.order_book[order]price_diff.append(abs(price-tick.last_price))if len(price_diff) == 0:returnindex_min = price_diff.index(min(price_diff))index_count = 0if len(self.order_book.keys()) > len(self.active_orders):for order in diff:if index_count == index_min:print(f"{self.order_book[order]}此价格网格离lastprice过近,不进行委托")index_count += 1continueindex_count += 1price = self.order_book[order]del self.order_book[order]if tick.last_price > price:# print(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'):}触碰了价格为{price}委托单")volume = self.grid_usdt_amountsorderid = self.buy(volume=round(volume/price, 5),price=price,vt_symbol=self.vt_symbol)else:# print(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'):}触碰了价格为{price}委托单")volume = self.grid_usdt_amountsvolume = min(self.pos, round(volume/price, 5))orderid = self.sell(volume=volume,price=price,vt_symbol=self.vt_symbol)self.order_book[orderid] = price
程序入口
首先实例化事件引擎以及主引擎:
event_engine = EventEngine()main_engine = MainEngine(event_engine)
首先我们需要往主引擎中添加gateway。
main_engine.add_gateway(OkexGateway)main_engine.add_gateway(HuobiGateway)main_engine.add_gateway(BinanceGateway)
为了实时获取行情我们需要订阅symbol,这里我们选择XRP/USDT币对,oms引擎会储存一个gateway中所有的contract,于是我们遍历这个contract列表,找出xrp现货。
contracts = main_engine.engines["oms"].contractsspotsymbol = []for contract_name in contracts:contract = \contracts[contract_name]vt_symbol = contract.vt_symbolif contract.product == Product.SPOT:if (arbsymbol in vt_symbol and 'usdt' in vt_symbol) or (arbsymbol.upper() in vt_symbol and 'USDT' in vt_symbol) :spotsymbol.append(vt_symbol)
手动设置好算法的参数:
up = 0.85down = 0.71grid_levels = 180grid_step = (up-down)/down/grid_levelsgrid_amounts = 45grid_usdt_amount = 14investment = grid_levels*grid_usdt_amountis_sbot = Truedefault_setting = {"vt_symbol": vt_symbol,"upper_limit": up,"lower_limit": down,"investment": investment,"grid_step": grid_step,"grid_levels":grid_levels,"grid_amounts":grid_amounts,"grid_usdt_amounts":grid_usdt_amount,"is_sbot":is_sbot,"interval": 0,"stop_loss": 0.20,"trailing_up":False,}
最后添加算法引擎并初始化运行。
algoengine = main_engine.add_app(AlgoTradingApp)algoengine.init_engine()algoengine.start_algo(default_setting)
策略实战
参数选择:
选择了btc现货,底部31000顶部40000,180个网格的参数,每个限价单价值11usdt。
vt_symbol = 'BTC-USDT.OKEX'up = 40000down = 31000grid_levels = 180grid_step = (up-down)/down/grid_levelsgrid_amounts = 45grid_usdt_amount = 11investment = grid_levels*grid_usdt_amount
下所有限价单:
策略输出
2021-06-21 15:09:26:现在活跃订单簿数量:178,本地订单数量180
2021-06-21 15:09:27:现在活跃订单簿数量:178,本地订单数量180
多单成交,数量:0.00033,价格:33000.0
有不同的网格成交了,将stuck取消
2021-06-21 15:09:28:现在活跃订单簿数量:177,本地订单数量180
GridAlgo_1:委托卖出BTC-USDT.OKEX:0.00033@33050.0
33000.0此价格网格离lastprice过近,不进行委托
GridAlgo_1:委托卖出BTC-USDT.OKEX:0.00033@33100.0
2021-06-21 15:09:29:现在活跃订单簿数量:179,本地订单数量180
2021-06-21 15:09:30:现在活跃订单簿数量:179,本地订单数量180
......
将所有成交连点成线:
可以看出策略实现了逢低买入逢高卖出。
基于VNPY实现网格策略实盘(币圈)相关推荐
- 【一篇文章告诉你网格策略从理论到实盘的所有内容(python实现)】
一篇文章告诉你网格策略从理论到实盘的所有内容 名词定义 什么是网格策略 现货网格的基本参数 等差网格以及等比网格 什么是网格的价格中枢以及目标仓位 无常损失的与业绩计算 需要"市价补仓&qu ...
- 我的币圈量化学习之路-初衷
初衷 我自己擅长C.C++编码,对区块链非常感兴趣.近年来接触到一些股票量化投资的概念,一直想深入研究,但是各种原因吧,一直没有实际行动.近期呢觉得股票量化的人很多,竞争对手更多,那与其努力打好手里的 ...
- AMM引入无限网格策略,变无常损失为阿尔法收益
当前AMM的痛点 自动化做市商(AMM)是Defi领域的一大创新,AMM 从根本上改变了用户交易加密货币的方式,与传统的订单簿交易模式不同,AMM 的交易双方都是和链上流动性资产池在进行交互.流动性池 ...
- 羊毛大军杀入币圈,有人月入过万,有人惨遭反薅,沦为韭菜
文 | 棘轮 比萨 空投糖果."月入过万"."躺赚"--羊毛党大军,早已杀入币圈. 羊毛党圈流传的"史上最贵羊毛"--ONT空投,就出自币圈 ...
- 【币圈小试牛刀】期现套利(上)——期限套利原理
大家好!我是币圈玖哥,匆匆忙忙进场,匆匆忙忙进厂-提醒本文不构成任何投资建议,只是作为学习分享,本期给大家带来的策略是:期限套利,套取永续合约的资金费率,由于风险偏低,适合新手使用! 文章目录 1.期 ...
- 混迹币圈想超越他人,你不得不了解的CCR智能炒币机器人
现货交易时代,高额的利益流动吸引了不少人来加入这场逐鹿之战.但是面对复杂的交易规则,许多交易新手纷纷下马.我们不得不想到,如果能有一个"机器人",自动追踪牛市.分析币种.通过完备的 ...
- 陀螺问答TOP 10 周榜单(9.23-9.29):币圈暴跌是否与谷歌量子霸权有关?
近日,陀螺财经App推出了全新功能"陀螺问答",为用户首创向KOL单独提问的机会,一对一问答,满足用户个性化深度交互的需求. 截止目前,"陀螺问答"已接收到70 ...
- 【vn.py】 策略实盘自动交易
跑完了历史数据回测和优化,得到了一个不错的回测资金曲线,最后就可以准备开始实盘交易了.在教程2-5中我们已经接触了真实账户和仿真账户的概念,这里强调一个原则: 所有量化策略在开始真金白银交易之前,都应 ...
- 电影奥斯卡,学术诺贝尔,体育奥运会,币圈……
低调做市场,高调做产品.在"少说多做"上,Bybit 开了个好头. 文 | 秦晓峰 运营 | 盖遥 编辑 | 郝方舟 出品 | Odaily星球日报(ID:o-daily) 每 ...
最新文章
- 【蓝桥java】递归基础之输出连续数字
- 文件目录管理及vi编辑器的使用
- hdu2457 Trie图+dp
- 字节跳动被爆商业化部门大量裁员
- 谷歌浏览器安卓版_谷歌Chrome Canary 82安卓版现可复制图片到剪贴板
- tensorflow MySQL_tensorflow从入门到放弃....
- 无法访问hadoop yarn8088端口的解决方法
- java银行收费系统界面程序_基于jsp的物业收费管理系统-JavaEE实现物业收费管理系统 - java项目源码...
- Android连续点击事件的实现
- BAT 老兵的经验之谈,成长路上这个道理越早知道越好
- [UE4][C++]简单超人小游戏(游戏接受键盘事件)
- IE8补充前后缀快捷键
- PyCharm 的调试功能
- 关于windows下的System32与SysWOW64两个文件夹
- html5 竖线的实现,border 实现竖线
- skywalking plugin 开发初探 ONS plugin 实践
- 小学阅读方法六种_小学写作手法六种
- 对于雷诺数,你了解多少?
- 浅谈C语言嵌入式系统编程注意事项
- 微弱直流电压/电流信号的采样电路 --滤波跟随放大
热门文章
- c语言less函数,LESS使用方法
- Embedded Linux S3C2440 - QEMU and Graphic
- Acer S3 拆机换固态硬盘!【我的Acer S3小三,时尚时尚最时尚!】
- NDK-r25交叉编译qemu-7.0.0 第66步报错
- 圣诞节快来了~用python做一个粒子烟花震撼众人赚个女孩回来吧~
- SPS PDSCH的HARQ反馈
- 惠普笔记本U盘重装系统教程(转载)
- 电脑怎么系统重装,重装电脑系统怎么装
- VR中的9轴传感器(重力加速度/陀螺仪/磁力计)
- laravel 模型局部不更新updated_at字段