还在为TV看到了好策略却无法自动化下单而苦恼么!! 扁豆带你排忧解难~直接打通FMZ Bot~
今天在这里要讲什么呢~
大家看标题就对了!
1. 背景介绍
TradingView是很好的行情画图工具~
pine脚本也是各种神仙操作, 强大威武!
回测, 报警, 各种对接, 是非常完善的一款金融工具了~
但是有两点一直在困扰着胖友们...
一是昂贵的会员制度,
二是信号直接可交易的交易所非常之少, 貌似就两三个.
今天我们这篇文章就是带大家搞定交易所对接的问题~
2. 实现方法
整体的思路呢, 是这样事儿的.
TV pine脚本 -> 信号报警webhook -> 本地webhook server转发请求 -> FMZ bot接收请求进行操作
那咱呢, 就一步一步的来呗~
...
首先, 你先有个TV呗,
https://www.tradingview.com/
接下来, 我们先建立个Alert, 详情见下图喽,
图中的几点需要注意, 生成Alert的时候,
有效期, webhook地址, Message内容, 一定要搞好.
有效期, 这个一看就知道, 到期了就无效了...
webhook地址, 这里我们先放下, 等本地的webhook service搞好了再回来填写.
Message这里, 最好有个设计, 为了bot好区分是哪个Alert传来的信息,
扁豆这里一般是这些信息 -> XXX策略, 下单量, 方向
好啦, 到这里, TV部分基本搞定了!
...
接下来我们搞定本地的webhook service!
这种东西呢, Google一下遍地都是这框架那框架,
扁豆就不再推荐了, 只说自己的那种.
是个python的简单框架,
GitHub: https://github.com/shawn-sterling/gitlab-webhook-receiver
安全无忧, 简单方便, 当然...也是有坑的,
这个小框架, 它会!! 自杀!! 这点请务必注意~
所以呢, 又写了个脚本再server上面,
当log里面出现die啊, offline啊, 就给他重启下, 后来不保险, 又定时重启了, 每个小时找个不碍事儿的时间...给他重启下, 目前有两个月左右了吧, 没有再出现过丢信号的情况了~
另外还有一点, TV只认80端口哦~ service不要搞错端口了~
搞到这里,
我们已经搞定了从TV拿到了Alert的Message,
那么我们怎么搞给Bot呢?
不知道大家有没有注意过FMZ的接口文档最下面~
我们可以通过api传给自己的小Bot一些Command!
具体请求例子在这里, 红框部分就是我们需要的请求了~
这里也同样需要一些准备工作,
FMZ API(头像->账号设置->API接口),
一个已经启动的Bot(我们要拿到它的ID, 不管怎么样先新建一个搞个ID), 一般机器人的url里面数字就是ID啦~
好嘞!!
到这里!~ 我们改造一下webhook service, 让他在接收了消息之后, 自动转发给我们可爱的FMZ Bot~!
最后别忘了把搞好的webhook地址回填到TV的Alert中哦~
(格式: http://xx.xx.xx.xx:80)
下面是渣渣扁豆改动的service代码, 大家可以参考
#!/usr/bin/python -tt
# -*- coding: UTF-8 -*-
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import json
import logging
import logging.handlers
import os
import re
import shutil
import subprocess
import time
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
try:
import md5
import urllib2
from urllib import urlencode
except:
import hashlib as md5
import urllib.request as urllib2
from urllib.parse import urlencode
############################################################
##### You will likely need to change some of the below #####
# log file for this script
log_file = '/root/webhook/VMA/webhook.log'
# Bot api licence
accessKey = ''
secretKey = ''
# HTTP config
log_max_size = 25165824 # 24 MB
log_level = logging.INFO
#log_level = logging.DEBUG # DEBUG is quite verbose
listen_port = 80
##### You should stop changing things unless you know what you are doing #####
##############################################################################
log = logging.getLogger('log')
log.setLevel(log_level)
log_handler = logging.handlers.RotatingFileHandler(log_file,
maxBytes=log_max_size,
backupCount=4)
f = logging.Formatter("%(asctime)s %(filename)s %(levelname)s %(message)s",
"%B %d %H:%M:%S")
log_handler.setFormatter(f)
log.addHandler(log_handler)
class webhookReceiver(BaseHTTPRequestHandler):
def run_it(self, cmd):
"""
runs a command
"""
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
log.debug('running:%s' % cmd)
p.wait()
if p.returncode != 0:
log.critical("Non zero exit code:%s executing: %s" % (p.returncode,
cmd))
return p.stdout
def bot_conmand(self, method, *args):
"""
send conmand request to bot api
"""
d = {
'version': '1.0',
'access_key': accessKey,
'method': method,
'args': json.dumps(list(args)),
'nonce': int(time.time() * 1000),
}
d['sign'] = md5.md5(('%s|%s|%s|%d|%s' % (d['version'], d['method'], d['args'], d['nonce'], secretKey)).encode('utf-8')).hexdigest()
return json.loads(urllib2.urlopen('https://www.fmz.com/api/v1', urlencode(d).encode('utf-8')).read().decode('utf-8'))
def do_POST(self):
"""
receives post, handles it
"""
log.debug('got post')
message = 'OK'
self.rfile._sock.settimeout(5)
data_string = self.rfile.read(int(self.headers['Content-Length']))
log.info(data_string)
self.send_response(200)
self.send_header("Content-type", "text")
self.send_header("Content-length", str(len(message)))
self.end_headers()
self.wfile.write(message)
log.debug('TV connection should be closed now.')
#log.info(self.bot_conmand('GetRobotList', -1, -1, -1)) # GetRobotList(offset, length, robotStatus int)传-1代表获取全部
log.info(self.bot_conmand('CommandRobot', 169788, data_string)) # CommandRobot(robotId int64, cmd string)向机器人发送命令
def log_message(self, formate, *args):
"""
disable printing to stdout/stderr for every post
"""
return
def main():
"""
the main event.
"""
try:
server = HTTPServer(('', listen_port), webhookReceiver)
log.info('started web server...')
server.serve_forever()
except KeyboardInterrupt:
log.info('ctrl-c pressed, shutting down.')
server.socket.close()
if __name__ == '__main__':
main()
3. FMZ策略内实现
上面讲了通信实现,
那么其实我们的Bot策略中也要做相应的处理,
来搞定我们的接收信号过程.
比如一开始设计的Alert Message,
自己可以按照喜好和具体设计来做一些玩儿法~ 这就看大家的脑洞啦~
代码如下, 拿到信息, 筛选, 做操作, 结束~
function get_Command() { //负责交互的函数,交互及时更新 相关数值 ,熟悉的用户可以自行扩展
var way = null; //路由
var cmd = GetCommand(); //获取 交互命令API
var cmd_arr = cmd.split(",");
if (cmd) {
// 定义路由
if (cmd.indexOf("BUY,1") != -1) {
way = 1;
}
if (cmd.indexOf("SELL,1") != -1) {
way = 2;
}
if (cmd.indexOf("BUY,2") != -1) {
way = 3;
}
if (cmd.indexOf("SELL,2") != -1) {
way = 4;
}
// 分支选择 操作
switch (way) {
case 1:
xxx
break;
case 2:
xxx
break;
case 3:
xxx
break;
case 4:
xxx
break;
default:
break;
}
}
}
好啦~ 这次的科普就告一段落啦~
希望有给大家带来帮助吧!
顺便打个广告~
公众号: 扁豆子的量化日志
欢迎大佬们前来玩儿哈~
(^U^)ノ~YO
网友评论