利用 tradingview 指标对接发明者实盘机器人

还在为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传给自己的**ot一些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

免责声明:信息仅供参考,不构成投资及交易建议。投资者据此操作,风险自担。
如果觉得文章对你有用,请随意赞赏收藏
相关推荐
相关下载
登录后评论
Copyright © 2019 宽客在线