Loading ...

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

Author: 扁豆子, Created: 2020-03-31 22:25:30, Updated: 2020-04-01 10:42:43

还在为TV看到了好策略却无法自动化下单而苦恼么!! 扁豆带你排忧解难~直接打通FMZ Bot~

今天在这里要讲什么呢~ 大家看标题就对了!

1. 背景介绍

TradingView是很好的行情画图工具~ pine脚本也是各种神仙操作, 强大威武! 回测, 报警, 各种对接, 是非常完善的一款金融工具了~ 但是有两点一直在困扰着胖友们… 一是昂贵的会员制度, 二是信号直接可交易的交易所非常之少, 貌似就两三个. 今天我们这篇文章就是带大家搞定交易所对接的问题~

2. 实现方法

整体的思路呢, 是这样事儿的. TV pine脚本 -> 信号报警webhook -> 本地webhook server转发请求 -> FMZ bot接收请求进行操作 那咱呢, 就一步一步的来呗~ … 首先, 你先有个TV呗, https://www.tradingview.com/ 接下来, 我们先建立个Alert, 详情见下图喽, img 图中的几点需要注意, 生成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的接口文档最下面~ img 我们可以通过api传给自己的小Bot一些Command! 具体请求例子在这里, 红框部分就是我们需要的请求了~ img 这里也同样需要一些准备工作, FMZ API(头像->账号设置->API接口), 一个已经启动的Bot(我们要拿到它的ID, 不管怎么样先新建一个搞个ID), 一般机器人的url里面数字就是ID啦~ img 好嘞!! 到这里!~ 我们改造一下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


More

Peppa 官方已经支持直接生成url对接tradingview的webhook来给机器人发指令 https://www.fmz.com/api#%E7%9B%B4%E6%8E%A5%E9%AA%8C%E8%AF%81

skyfffire 要哭了,哈哈哈,我发两个机器人有个机器人死活收不到命令,最后检查发现楼主的command写成了conmand,哈哈哈哈草

homily 已经用FastAPI迭代过了。感谢分享。

skyfffire 厉害,已经移植在跑测试了

张超 妈妈再也不用担心我TV的策略不能移植了

homily 好像TV要买高级会员才行

扁豆子 嗯, 交易对是问题, 一般都是比较主流得, 不过我都比较混用, 相关性比较强得其实可以混用, 不是特别小周期得趋势都没太大查别得亚子...

viper 这个我也考虑过,但是不想充会员…最后还是选择移植策略了。其实还可以用tv的邮件警报触发,网上可以搜到类似的教程。

轻轻的云 好是好,不过好像只能支持TV支持的交易对,而不能添加交易对吧? 比如OK的合约只能支持币本位的,USDT本位的还不支持。。。。

John 赞一个!

小草 这样就不用改写了,不错

小小梦 赞一个!

张百九

古力十段 顶一下

扁豆子 官方爸爸流批~

扁豆子 (^U^)ノ~YO

skyfffire 楼主厉害呀,这个思路不错,谢谢楼主。

扁豆子 555555555555555555......(>人<;) sorry

扁豆子 流批~ 期待分享哈~

轻轻的云 双十一 双十二买划算 直接半价。我就是趁着那个时候买的。

扁豆子 ( •̀ ω •́ )y

扁豆子 嗯嗯... 最好赶上一些促销什么的买一年最划算了, 平时还是挺贵的, 有大佬分享哈~ 刚开始免费会员, 过段时间就会推送折扣很大的价格了~

扁豆子 感觉他们最全的还是股市和期货, 数字货币的很少了算是...

轻轻的云 嗯,我也问过TV的客服,回复说正在准备增加OKEX的所有的币对合约,但是什么时候能增加还不知道。。。。也是醉了。。。。难不成歪果仁的效率这么低下了。。。。

扁豆子 邮件那种我用过, 延时会稍微大一些, 这个在10s之内一般都能完成交易来的

扁豆子 嗯, 交易对是问题, 一般都是比较主流得, 不过我都比较混用, 相关性比较强得其实可以混用, 不是特别小周期得趋势都没太大查别得亚子...