├── .project
├── .pydevproject
├── .vscode
└── settings.json
├── README.md
├── __init__.py
├── common
├── __init__.py
├── __pycache__
│ └── __init__.cpython-37.pyc
└── email
│ ├── __init__.py
│ ├── __pycache__
│ ├── __init__.cpython-37.pyc
│ ├── email_handler.cpython-37.pyc
│ └── mail_agent.cpython-37.pyc
│ ├── email_handler.py
│ └── mail_agent.py
├── config
├── __pycache__
│ ├── settings.cpython-36.pyc
│ └── settings.cpython-37.pyc
├── db_properties.py
└── settings.py
├── dao
├── dao.py
└── mysql_connection.py
├── exchangeapi
├── __init__.py
├── __init__.pyc
├── __pycache__
│ ├── __init__.cpython-36.pyc
│ └── __init__.cpython-37.pyc
└── huobi
│ ├── __init__.py
│ ├── __init__.pyc
│ ├── __pycache__
│ ├── __init__.cpython-36.pyc
│ ├── __init__.cpython-37.pyc
│ ├── rest_api.cpython-36.pyc
│ └── rest_api.cpython-37.pyc
│ ├── rest_api.py
│ └── websocket.py
├── requirements.txt
├── run.py
├── runbts.py
├── strategy
├── __init__.py
├── __init__.pyc
├── __pycache__
│ ├── __init__.cpython-37.pyc
│ ├── average_cross.cpython-37.pyc
│ ├── k_line_service.cpython-37.pyc
│ ├── rest_api_service.cpython-37.pyc
│ ├── trend.cpython-37.pyc
│ └── websocket_subscribe_service.cpython-37.pyc
├── big_order_service.py
├── k_line_service.py
├── second_kill_service.py
└── trend.py
├── test.py
├── test
└── fake_run.py
├── test2.py
└── util
├── __pycache__
├── api_key_util.cpython-36.pyc
├── api_key_util.cpython-37.pyc
├── http_request_util.cpython-36.pyc
└── http_request_util.cpython-37.pyc
├── api_key_util.py
└── http_request_util.py
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | TradeRobot
4 |
5 |
6 |
7 |
8 |
9 | org.python.pydev.PyDevBuilder
10 |
11 |
12 |
13 |
14 |
15 | org.python.pydev.pythonNature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.pydevproject:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /${PROJECT_DIR_NAME}
5 |
6 | python 3.6
7 | Default
8 |
9 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.linting.pylintEnabled": false
3 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # **TradeBot**
2 |
3 | ## **火币自动化交易平台机器人**
4 | 依赖ta-lib提供指标支持,需要编译
5 |
6 | ### **一.功能设计**
7 | 1. 大单买卖监测(交易量在10+BTC的买卖监测)
8 | 2. 异常波动监测(一分钟内3%+—,三分钟内5%+-,etc...)
9 | 3. 支持涨跌幅度超过限度时邮件通知
10 | 4. 币安与火币的搬砖功能
11 | 5. 上币监测以及限价买入
12 | 6. 通过计算N分钟内M的货币的平均变化趋势,判断火币网大盘的涨跌
13 | 7. 法币交易识别超低订单,低价秒杀
14 | 8. 设置收益点,止损点,到点自动交易
15 | 9. 添加测试脚本模拟买入/卖出,便于根据结果修改参数
16 |
17 | ### 已经实现部分
18 | 1.均线交叉判断趋势
19 | 2.邮件通知
20 |
21 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/__init__.py
--------------------------------------------------------------------------------
/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/common/__init__.py
--------------------------------------------------------------------------------
/common/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/common/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/common/email/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/common/email/__init__.py
--------------------------------------------------------------------------------
/common/email/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/common/email/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/common/email/__pycache__/email_handler.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/common/email/__pycache__/email_handler.cpython-37.pyc
--------------------------------------------------------------------------------
/common/email/__pycache__/mail_agent.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/common/email/__pycache__/mail_agent.cpython-37.pyc
--------------------------------------------------------------------------------
/common/email/email_handler.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from config import settings
3 | from common.email import mail_agent
4 | from config import settings
5 |
6 |
7 | # 记录上一次邮件发送的时间
8 | last_mail_datetime = None
9 |
10 | def send_mail(title, content):
11 | if not mail_agent:
12 | return
13 | global last_mail_datetime
14 | now = datetime.datetime.now()
15 | # if last_mail_datetime and now - last_mail_datetime < datetime.timedelta(
16 | # minutes=settings.N_MINUTES_STATE):
17 | # return
18 | last_mail_datetime = now
19 |
20 | if settings.MAIL_ACCOUNT and settings.MAIL_AUTH_CODE:
21 | agent = mail_agent.MailAgent(settings.MAIL_ACCOUNT, settings.MAIL_AUTH_CODE)
22 |
23 | with agent.SMTP() as s:
24 | s.send(settings.MAIL_RECEIPIENTS, content, title)
--------------------------------------------------------------------------------
/common/email/mail_agent.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 | import smtplib
5 | import imaplib
6 | from email.encoders import encode_base64
7 | from email.mime.multipart import MIMEMultipart
8 | from email.mime.base import MIMEBase
9 | from email.mime.text import MIMEText
10 | from email.header import decode_header
11 | from email import message_from_bytes
12 |
13 | #找到对应文件的路径,如果不在环境变量则添加到环境变量
14 | # file_path = os.path.dirname(os.path.dirname())
15 | # if file_path not in sys.path:
16 | # sys.path.insert(0,file_path)
17 |
18 | SERVER_LIB = {
19 | 'sample.com':{
20 | 'smtp':'smtp.sample.com',
21 | 'imap':'imap.sample.com',
22 | 'smtp_port':0,
23 | 'imap_port':0,
24 | 'use_ssl':True
25 | }
26 | }
27 |
28 | class MailAgent(object):
29 |
30 | def __init__(self,account,auth_code,name='',**config):
31 |
32 | account_name,server_name = account.split('@')
33 |
34 | self.smtp = 'smtp.' + server_name
35 | self.imap = 'imap.' + server_name
36 | self.smtp_port = 0
37 | self.imap_port = 0
38 | self.use_ssl = True
39 | self.name = '%s<%s>' % (name or account_name,account)
40 | self.account = account
41 | self.auth_code = auth_code
42 |
43 | #将SERVER_LIB和config更新入对象属性
44 | self.__dict__.update(SERVER_LIB.get(server_name,{}))
45 | self.__dict__.update(config)
46 |
47 | #初始化邮箱服务
48 | st_SMTP = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP
49 | st_IMAP = imaplib.IMAP4_SSL if self.use_ssl else imaplib.IMAP4
50 | if self.smtp_port:
51 | self.st_SMTP = lambda: st_SMTP(self.smtp,self.smtp_port)
52 | else:
53 | self.st_SMTP = lambda: st_SMTP(self.smtp)
54 | if self.imap_port:
55 | self.st_IMAP = lambda: st_IMAP(self.imap, self.imap_port)
56 | else:
57 | self.st_IMAP = lambda: st_IMAP(self.imap)
58 | self.SMTP = lambda: SMTP(self)
59 | self.IMAP = lambda: IMAP(self)
60 |
61 | class SMTP(object):
62 |
63 | def __init__(self,mail_agent):
64 | self.name,self.account = mail_agent.name,mail_agent.account
65 | self.server = mail_agent.st_SMTP()
66 | try:
67 | self.server.login(mail_agent.account, mail_agent.auth_code)
68 | except:
69 | self.close()
70 | raise
71 |
72 | def close(self):
73 | try:
74 | return self.server.quit()
75 | except:
76 | pass
77 |
78 | def __enter__(self):
79 | return self
80 |
81 | def __exit__(self, exc_type, exc_value, traceback):
82 | self.close()
83 |
84 | def send(self, to_addr, html='', subject='', to_name='', png_content=''):
85 | subject = subject or 'No subject'
86 | to_name = to_name or "; ".join(to_addr)
87 | html = '
%s' % html
88 | if html:
89 | html = html.replace('{{png}}', '
')
90 |
91 | msg = MIMEMultipart()
92 | msg.attach(MIMEText(html, 'html', 'utf8'))
93 | msg['From'] = self.name
94 | msg['To'] = '%s <%s>' % (to_name, to_addr)
95 | msg['Subject'] = subject
96 |
97 | if png_content:
98 | m = MIMEBase('image', 'png', filename='x.png')
99 | m.add_header('Content-Disposition', 'attachment', filename='x.png')
100 | m.add_header('Content-ID', '<0>')
101 | m.add_header('X-Attachment-Id', '0')
102 | m.set_payload(png_content)
103 | encode_base64(m)
104 | msg.attach(m)
105 |
106 | self.server.sendmail(self.account, to_addr, msg.as_string())
107 |
108 | class IMAP(object):
109 |
110 | def __init__(self, mail_agent):
111 |
112 | self.name, self.account = mail_agent.name, mail_agent.account
113 | self.conn = mail_agent.st_IMAP()
114 | try:
115 | self.conn.login(mail_agent.account, mail_agent.auth_code)
116 | self.conn.select('INBOX')
117 | except:
118 | self.close()
119 | raise
120 |
121 | def __enter__(self):
122 | return self
123 |
124 | def __exit__(self, exc_type, exc_value, traceback):
125 | self.close()
126 |
127 | def close(self):
128 | try:
129 | return self.conn.close()
130 | except:
131 | pass
132 |
133 | def getSubject(self, i):
134 | conn = self.conn
135 | id_list = conn.search(None, '(UNSEEN)')[1][0].split()
136 | try:
137 | email_id = id_list[i]
138 | except IndexError:
139 | return None, -1
140 | data = conn.fetch(email_id, 'BODY.PEEK[HEADER.FIELDS (SUBJECT)]')[1]
141 |
142 | msg = message_from_bytes(data[0][1])
143 | s, encoding = decode_header(msg['Subject'])[0]
144 | subject = s if type(s) is str else s.decode(encoding or 'utf-8')
145 | return subject
146 |
147 | if __name__ == '__main__':
148 | from config import settings
149 |
150 | if settings.MAIL_ACCOUNT and settings.MAIL_AUTH_CODE:
151 | mail_agent = MailAgent(settings.MAIL_ACCOUNT, settings.MAIL_AUTH_CODE)
152 |
153 | with mail_agent.SMTP() as s:
154 | s.send(settings.MAIL_RECEIPIENTS, '测试邮件发送', '测试')
155 | print("发送成功")
156 |
157 | time.sleep(5)
158 |
159 | with mail_agent.IMAP() as i:
160 | subject = i.getSubject(-1)
161 | print('最新邮件: ' + subject)
162 | print('接收成功')
--------------------------------------------------------------------------------
/config/__pycache__/settings.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/config/__pycache__/settings.cpython-36.pyc
--------------------------------------------------------------------------------
/config/__pycache__/settings.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/config/__pycache__/settings.cpython-37.pyc
--------------------------------------------------------------------------------
/config/db_properties.py:
--------------------------------------------------------------------------------
1 | DBHOST = "localhost"
2 | DBPORT = 3306
3 | DBUSER = "root"
4 | DBPWD = ""
5 | DBNAME = "chain"
6 | DBCHAR = "utf8"
--------------------------------------------------------------------------------
/config/settings.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import sys
3 | import logging
4 |
5 | # 日志配置文件
6 | from copy import deepcopy
7 |
8 | # 此处填写APIKEY
9 | ACCESS_KEY = " "
10 | SECRET_KEY = " "
11 |
12 | # 用户ID(火币网个人中心可看)
13 | ACCOUNT_ID = None
14 |
15 | _filename = None
16 | _format = "%(asctime)-15s [%(levelname)s] [%(name)s] %(message)s"
17 | _datefmt = "%Y/%m/%d %H:%M:%S"
18 | _level = logging.INFO
19 |
20 | if _filename:
21 | handlers = [logging.StreamHandler(sys.stdout), logging.FileHandler(_filename)]
22 | else:
23 | handlers = [logging.StreamHandler(sys.stdout)]
24 |
25 | logging.basicConfig(format=_format, datefmt=_datefmt, level=_level, handlers=handlers)
26 |
27 | # 目前查询的$symbol均为:<货币>USTD (可选:{ USDT, BTC })
28 | # 时间间隔均为1min(可选:{ 1min, 5min, 15min, 30min, 60min, 1day, 1mon, 1week, 1year })
29 | SYMBOL = "USDT"
30 | PERIOD = "1min"
31 |
32 | # 计算n分钟内的涨跌幅
33 | N_MINUTES_STATE = 10
34 |
35 | # 当达到以下值,发出警报通知,触发下一步动作
36 | PRICE_ALERT_INCREASE_POINT = 1.25
37 | PRICE_ALERT_DECREASE_POINT = -1.25
38 |
39 | # 买入和卖出策略目前写死在代码里
40 | # 卖出策略 - 达到PRICE_ALERT_DECREASE_POINT时
41 | # 以当时的成交价格卖出,如果这种货币为降幅最小两个之一,则等待不卖出
42 |
43 | # 买入策略 - 达到PRICE_ALERT_INCREASE_POINT时
44 | # 以60%和40%的比例分别买涨价最高/次高的
45 |
46 | # 设定参考货币的类型,权重及初始默认拥有的货币数量
47 | COINS = {
48 | "BTC": {
49 | "WEIGHT": 1,
50 | "AMOUNT": 0
51 | },
52 | "BCH": {
53 | "WEIGHT": 1,
54 | "AMOUNT": 0
55 | },
56 | "ETH": {
57 | "WEIGHT": 1,
58 | "AMOUNT": 0
59 | },
60 | "LTC": {
61 | "WEIGHT": 1,
62 | "AMOUNT": 0
63 | },
64 | "XRP": {
65 | "WEIGHT": 1,
66 | "AMOUNT": 0
67 | },
68 | "DASH": {
69 | "WEIGHT": 1,
70 | "AMOUNT": 0
71 | },
72 | "ETC": {
73 | "WEIGHT": 1,
74 | "AMOUNT": 0
75 | },
76 | "EOS": {
77 | "WEIGHT": 1,
78 | "AMOUNT": 0
79 | },
80 | "OMG": {
81 | "WEIGHT": 1,
82 | "AMOUNT": 0
83 | }
84 | }
85 |
86 | # 用户当前USDT账户余额
87 | USDT_CURRENCY = 0
88 |
89 | # 备份原始COIN/CURRENCY数据,作对比用
90 | ORIGINAL_USDT_CURRENCY = USDT_CURRENCY
91 | ORIGINAL_COINS = deepcopy(COINS)
92 | ORIGINAL_WEALTH = None
93 |
94 | # 将从火币上获取到的交易信息保存到数据库(mongodb)
95 | DATABASE_RECORD = False
96 |
97 | # 配置以下项目以初始化数据库
98 | DATABASE_SERVER_ADDRESS = None
99 | DATABASE_SERVER_PORT = 27017
100 | DATABASE_NAME = "huobi_exchange"
101 |
102 | # 这里的模拟起止仅用于数据库交易模拟分析
103 | SIMULATE_START = datetime.datetime(2017, 12, 1, 0, 0, 0)
104 | SIMULATE_END = datetime.datetime.now()
105 |
106 | # 如果数据库有用户名/密码,则定义如下
107 | DATABASE_SERVER_USERNAME = None
108 | DATABASE_SERVER_PASSWORD = None
109 |
110 | # 邮件通知,配置SMTP,获取其AuthCode即可发送邮件
111 | MAIL_ACCOUNT = "710777350@qq.com"
112 | MAIL_AUTH_CODE = "famqcgmaidkkbdgc"
113 | MAIL_RECEIPIENTS = ["ahbbhywmd@163.com"]
114 |
115 | # API 请求地址
116 | MARKET_URL = "https://api.huobipro.com"
117 | TRADE_URL = "https://api.huobipro.com"
--------------------------------------------------------------------------------
/dao/dao.py:
--------------------------------------------------------------------------------
1 | import ssl
2 | import gzip
3 | import uuid
4 | import json
5 | import time
6 | import StringIO
7 | import threading
8 | from websocket import create_connection
9 | from dao.mysql_connection import Mysql
10 |
11 |
12 | sql = """INSERT INTO {} (id, open, close, low, high, amount, vol, count) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE open=%s, close=%s, low=%s, high=%s, amount=%s, vol=%s, count=%s"""
13 |
14 | def save(item, symbol):
15 | conn = Mysql()
16 | print sql.format(symbol)
17 | conn.insertOne(sql.format(symbol),
18 | [item['id'], item['open'], item['close'], item['low'], item['high'], item['amount'],item['vol'], item['count'],
19 | item['open'], item['close'], item['low'], item['high'], item['amount'], item['vol'], item['count']])
20 |
21 | def createConnection(tradeId, symbol):
22 | print '{}, {} create connection'.format(tradeId, symbol)
23 | try:
24 | ws = create_connection("wss://api.huobipro.com/ws", sslopt={"cert_reqs": ssl.CERT_NONE})
25 | tradeStr = '{{"id": "{}", "sub": "market.{}.kline.1min"}}'
26 | ws.send(tradeStr.format(tradeId, symbol))
27 | return ws
28 | except Exception, e:
29 | time.sleep(10)
30 | return createConnection(tradeId, symbol)
31 |
32 | def main(symbol):
33 | tradeId = uuid.uuid1()
34 | ws = createConnection(tradeId, symbol)
35 | while True:
36 | try:
37 | compressData = ws.recv()
38 | compressedStream = StringIO.StringIO(compressData)
39 | gzipFile = gzip.GzipFile(fileobj=compressedStream)
40 | result = gzipFile.read()
41 | if result[:7] == '{"ping"':
42 | ts = result[8:21]
43 | pong = '{"pong":' + ts + '}'
44 | ws.send(pong)
45 | else:
46 | print(result)
47 | item = json.loads(result)
48 | if 'tick' in item:
49 | item = item['tick']
50 | save(item, symbol)
51 |
52 | except Exception as e:
53 | print('exception: {}'.format(e))
54 | ws = createConnection(tradeId, symbol)
55 |
56 | def history(symbol):
57 | tradeId = uuid.uuid1()
58 | ws = createConnection()
59 | tradeStr = '{{"id": "{}", "req": "market.{}.kline.1min", "from": {}, "to": {}}}'
60 | start = 1511845180
61 | ws.send(tradeStr.format(tradeId, symbol, 1511845180, 1511845180+60*240))
62 | index = 1
63 | while True:
64 | try:
65 | compressData = ws.recv()
66 | compressedStream = StringIO.StringIO(compressData)
67 | gzipFile = gzip.GzipFile(fileobj=compressedStream)
68 | result = gzipFile.read()
69 | if result[:7] == '{"ping"':
70 | ts = result[8:21]
71 | pong = '{"pong":' + ts + '}'
72 | ws.send(pong)
73 | # ws.send(tradeStr.format(tradeId, symbol, start+60*240*index+1, start+60*240*(index+1)))
74 | else:
75 | item = json.loads(result)
76 | conn.insertOne(sql.format(symbol), [item['id'], item['open'], item['close'], item['low'], item['high'], item['amount'], item['vol'], item['count'],
77 | item['open'], item['close'], item['low'], item['high'], item['amount'], item['vol'], item['count']])
78 | # ws.send(tradeStr.format(tradeId, symbol, start+60*240*index+1, start+60*240*(index+1)))
79 | index += 1
80 | time.sleep(.5)
81 | except Exception as e:
82 | print('exception: {}'.format(e))
83 | ws = createConnection()
84 |
85 | if __name__ == '__main__':
86 | thread_list = []
87 | symbols = ['btcusdt','bchusdt','ethusdt','etcusdt','ltcusdt','eosusdt','xrpusdt','omgusdt','dashusdt','zecusdt','adausdt','ctxcusdt','actusdt','btmusdt','btsusdt','ontusdt','iostusdt','htusdt','trxusdt','dtausdt','neousdt','qtumusdt','elausdt','venusdt','thetausdt','sntusdt','zilusdt','xemusdt','smtusdt','nasusdt','ruffusdt','hsrusdt','letusdt','mdsusdt','storjusdt','elfusdt','itcusdt','cvcusdt','gntusdt']
88 |
89 | for symbol in symbols:
90 | thread_list.append(threading.Thread(target=main, args=(symbol,)))
91 | for t in thread_list:
92 | t.start()
93 | for t in thread_list:
94 | t.join()
--------------------------------------------------------------------------------
/dao/mysql_connection.py:
--------------------------------------------------------------------------------
1 | """
2 | @author: Salmon
3 | 1、执行带参数的SQL时,请先用sql语句指定需要输入的条件列表,然后再用tuple/list进行条件批配
4 | 2、在格式SQL中不需要使用引号指定数据类型,系统会根据输入参数自动识别
5 | 3、在输入的值中不需要使用转意函数,系统会自动处理
6 | """
7 |
8 | import MySQLdb
9 | import sys
10 | sys.path.append('../')
11 | from MySQLdb.cursors import DictCursor
12 | from DBUtils.PooledDB import PooledDB
13 | # from PooledDB import PooledDB
14 | import config.db_properties
15 |
16 | """
17 | Config是一些数据库的配置文件
18 | """
19 |
20 | class Mysql(object):
21 | """
22 | MYSQL数据库对象,负责产生数据库连接 , 此类中的连接采用连接池实现获取连接对象:conn = Mysql.getConn()
23 | 释放连接对象;conn.close()或del conn
24 | """
25 | # 连接池对象
26 | __pool = None
27 |
28 | def __init__(self):
29 | # 数据库构造函数,从连接池中取出连接,并生成操作游标
30 | self._conn = Mysql.__getConn()
31 | self._cursor = self._conn.cursor()
32 |
33 | @staticmethod
34 | def __getConn():
35 | """
36 | @summary: 静态方法,从连接池中取出连接
37 | @return MySQLdb.connection
38 | """
39 | if Mysql.__pool is None:
40 | Mysql.__pool = PooledDB(creator=MySQLdb, mincached=1, maxcached=20,
41 | host=Config.DBHOST,
42 | port=Config.DBPORT,
43 | user=Config.DBUSER,
44 | passwd=Config.DBPWD,
45 | charset =Config.DBCHAR,
46 | db=Config.DBNAME,
47 | use_unicode=False,
48 | cursorclass=DictCursor,
49 | setsession=['SET AUTOCOMMIT = 1'])
50 | return Mysql.__pool.connection()
51 |
52 | def getAll(self, sql, param=None):
53 | """
54 | @summary: 执行查询,并取出所有结果集
55 | @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来
56 | @param param: 可选参数,条件列表值(元组/列表)
57 | @return: result list(字典对象)/boolean 查询到的结果集
58 | """
59 | if param is None:
60 | count = self._cursor.execute(sql)
61 | else:
62 | count = self._cursor.execute(sql, param)
63 | if count > 0:
64 | result = self._cursor.fetchall()
65 | else:
66 | result = False
67 | return result
68 |
69 | def getOne(self, sql, param=None):
70 | """
71 | @summary: 执行查询,并取出第一条
72 | @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来
73 | @param param: 可选参数,条件列表值(元组/列表)
74 | @return: result list/boolean 查询到的结果集
75 | """
76 | if param is None:
77 | count = self._cursor.execute(sql)
78 | else:
79 | count = self._cursor.execute(sql, param)
80 | if count > 0:
81 | result = self._cursor.fetchone()
82 | else:
83 | result = False
84 | return result
85 |
86 | def getMany(self, sql, num, param=None):
87 | """
88 | @summary: 执行查询,并取出num条结果
89 | @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来
90 | @param num:取得的结果条数
91 | @param param: 可选参数,条件列表值(元组/列表)
92 | @return: result list/boolean 查询到的结果集
93 | """
94 | if param is None:
95 | count = self._cursor.execute(sql)
96 | else:
97 | count = self._cursor.execute(sql, param)
98 | if count > 0:
99 | result = self._cursor.fetchmany(num)
100 | else:
101 | result = False
102 | return result
103 |
104 | def insertOne(self, sql, value):
105 | """
106 | @summary: 向数据表插入一条记录
107 | @param sql:要插入的SQL格式
108 | @param value:要插入的记录数据tuple/list
109 | @return: insertId 受影响的行数
110 | """
111 | self._cursor.execute(sql, value)
112 | return self.__getInsertId()
113 |
114 | def insertMany(self, sql, values):
115 | """
116 | @summary: 向数据表插入多条记录
117 | @param sql:要插入的SQL格式
118 | @param values:要插入的记录数据tuple(tuple)/list[list]
119 | @return: count 受影响的行数
120 | """
121 | count = self._cursor.executemany(sql, values)
122 | return count
123 |
124 | def __getInsertId(self):
125 | """
126 | 获取当前连接最后一次插入操作生成的id,如果没有则为0
127 | """
128 | self._cursor.execute("SELECT @@IDENTITY AS id")
129 | result = self._cursor.fetchall()
130 | return result[0]['id']
131 |
132 | def __query(self, sql, param=None):
133 | if param is None:
134 | count = self._cursor.execute(sql)
135 | else:
136 | count = self._cursor.execute(sql, param)
137 | return count
138 |
139 | def update(self, sql, param=None):
140 | """
141 | @summary: 更新数据表记录
142 | @param sql: SQL格式及条件,使用(%s,%s)
143 | @param param: 要更新的 值 tuple/list
144 | @return: count 受影响的行数
145 | """
146 | return self.__query(sql, param)
147 |
148 | def delete(self, sql, param=None):
149 | """
150 | @summary: 删除数据表记录
151 | @param sql: SQL格式及条件,使用(%s,%s)
152 | @param param: 要删除的条件 值 tuple/list
153 | @return: count 受影响的行数
154 | """
155 | return self.__query(sql, param)
156 |
157 | def begin(self):
158 | """
159 | @summary: 开启事务
160 | """
161 | self._conn.autocommit(0)
162 |
163 | def end(self, option='commit'):
164 | """
165 | @summary: 结束事务
166 | """
167 | if option == 'commit':
168 | self._conn.commit()
169 | else:
170 | self._conn.rollback()
171 |
172 | def dispose(self, isEnd=1):
173 | """
174 | @summary: 释放连接池资源
175 | """
176 | if isEnd == 1:
177 | self.end('commit')
178 | else:
179 | self.end('rollback');
180 | self._cursor.close()
181 | self._conn.close()
182 |
183 |
184 | def create_table(self, symbol):
185 | s = """
186 | CREATE TABLE `{}` (
187 | `id` int(11) NOT NULL,
188 | `open` varchar(100) DEFAULT NULL,
189 | `close` varchar(100) DEFAULT NULL,
190 | `low` varchar(100) DEFAULT NULL,
191 | `high` varchar(100) DEFAULT NULL,
192 | `amount` varchar(100) DEFAULT NULL,
193 | `vol` varchar(100) DEFAULT NULL,
194 | `count` varchar(100) DEFAULT NULL,
195 | PRIMARY KEY (`id`)
196 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
197 | """
198 | self._cursor.execute(s.format(symbol))
199 |
200 |
201 | if __name__ == "__main__":
202 | symbols = ['btcusdt','bchusdt','ethusdt','etcusdt','ltcusdt','eosusdt','xrpusdt','omgusdt','dashusdt','zecusdt','adausdt','ctxcusdt','actusdt','btmusdt','btsusdt','ontusdt','iostusdt','htusdt','trxusdt','dtausdt','neousdt','qtumusdt','elausdt','venusdt','thetausdt','sntusdt','zilusdt','xemusdt','smtusdt','nasusdt','ruffusdt','hsrusdt','letusdt','mdsusdt','storjusdt','elfusdt','itcusdt','cvcusdt','gntusdt']
203 | db = Mysql()
204 | for tableName in symbols:
205 | db.create_table(tableName)
--------------------------------------------------------------------------------
/exchangeapi/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/__init__.py
--------------------------------------------------------------------------------
/exchangeapi/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/__init__.pyc
--------------------------------------------------------------------------------
/exchangeapi/__pycache__/__init__.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/__pycache__/__init__.cpython-36.pyc
--------------------------------------------------------------------------------
/exchangeapi/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/exchangeapi/huobi/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/huobi/__init__.py
--------------------------------------------------------------------------------
/exchangeapi/huobi/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/huobi/__init__.pyc
--------------------------------------------------------------------------------
/exchangeapi/huobi/__pycache__/__init__.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/huobi/__pycache__/__init__.cpython-36.pyc
--------------------------------------------------------------------------------
/exchangeapi/huobi/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/huobi/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/exchangeapi/huobi/__pycache__/rest_api.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/huobi/__pycache__/rest_api.cpython-36.pyc
--------------------------------------------------------------------------------
/exchangeapi/huobi/__pycache__/rest_api.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/exchangeapi/huobi/__pycache__/rest_api.cpython-37.pyc
--------------------------------------------------------------------------------
/exchangeapi/huobi/rest_api.py:
--------------------------------------------------------------------------------
1 | from util.api_key_util import *
2 | from util.http_request_util import *
3 |
4 | # 获取KLine
5 | def get_kline(symbol, period, size=150):
6 | """
7 | :param symbol
8 | :param period: 可选值:{1min, 5min, 15min, 30min, 60min, 1day, 1mon, 1week, 1year }
9 | :param size: 可选值: [1,2000]
10 | :return:
11 | """
12 | params = {'symbol': symbol,
13 | 'period': period,
14 | 'size': size}
15 |
16 | url = MARKET_URL + '/market/history/kline'
17 | return http_get_request(url, params)
18 |
19 |
20 | # 获取marketdepth
21 | def get_depth(symbol, type):
22 | """
23 | :param symbol
24 | :param type: 可选值:{ percent10, step0, step1, step2, step3, step4, step5 }
25 | :return:
26 | """
27 | params = {'symbol': symbol,
28 | 'type': type}
29 |
30 | url = MARKET_URL + '/market/depth'
31 | return http_get_request(url, params)
32 |
33 |
34 | # 获取tradedetail
35 | def get_trade(symbol):
36 | """
37 | :param symbol
38 | :return:
39 | """
40 | params = {'symbol': symbol}
41 |
42 | url = MARKET_URL + '/market/trade'
43 | return http_get_request(url, params)
44 |
45 |
46 | # 获取merge ticker
47 | def get_ticker(symbol):
48 | """
49 | :param symbol:
50 | :return:
51 | """
52 | params = {'symbol': symbol}
53 |
54 | url = MARKET_URL + '/market/detail/merged'
55 | return http_get_request(url, params)
56 |
57 |
58 | # 获取 Market Detail 24小时成交量数据
59 | def get_detail(symbol):
60 | """
61 | :param symbol
62 | :return:
63 | """
64 | params = {'symbol': symbol}
65 |
66 | url = MARKET_URL + '/market/detail'
67 | return http_get_request(url, params)
68 |
69 | # 获取 支持的交易对
70 | def get_symbols(long_polling=None):
71 | """
72 | """
73 | params = {}
74 | if long_polling:
75 | params['long-polling'] = long_polling
76 | path = '/v1/common/symbols'
77 | return api_key_get(params, path)
78 |
79 | '''
80 | Trade/Account API
81 | '''
82 |
83 |
84 | def get_accounts():
85 | """
86 | :return:
87 | """
88 | path = "/v1/account/accounts"
89 | params = {}
90 | return api_key_get(params, path)
91 |
92 | ACCOUNT_ID = 0
93 | # 获取当前账户资产
94 | def get_balance(acct_id=None):
95 | """
96 | :param acct_id
97 | :return:
98 | """
99 | global ACCOUNT_ID
100 |
101 | if not acct_id:
102 | accounts = get_accounts()
103 | acct_id = accounts['data'][0]['id'];
104 |
105 | url = "/v1/account/accounts/{0}/balance".format(acct_id)
106 | params = {"account-id": acct_id}
107 | return api_key_get(params, url)
108 |
109 |
110 | # 下单
111 |
112 | # 创建并执行订单
113 | def send_order(amount, source, symbol, _type, price=0):
114 | """
115 | :param amount:
116 | :param source: 如果使用借贷资产交易,请在下单接口,请求参数source中填写'margin-api'
117 | :param symbol:
118 | :param _type: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
119 | :param price:
120 | :return:
121 | """
122 | try:
123 | accounts = get_accounts()
124 | acct_id = accounts['data'][0]['id']
125 | except BaseException as e:
126 | print ('get acct_id error.%s' % e)
127 | acct_id = ACCOUNT_ID
128 |
129 | params = {"account-id": acct_id,
130 | "amount": amount,
131 | "symbol": symbol,
132 | "type": _type,
133 | "source": source}
134 | if price:
135 | params["price"] = price
136 |
137 | url = '/v1/order/orders/place'
138 | return api_key_post(params, url)
139 |
140 |
141 | # 撤销订单
142 | def cancel_order(order_id):
143 | """
144 |
145 | :param order_id:
146 | :return:
147 | """
148 | params = {}
149 | url = "/v1/order/orders/{0}/submitcancel".format(order_id)
150 | return api_key_post(params, url)
151 |
152 |
153 | # 查询某个订单
154 | def order_info(order_id):
155 | """
156 |
157 | :param order_id:
158 | :return:
159 | """
160 | params = {}
161 | url = "/v1/order/orders/{0}".format(order_id)
162 | return api_key_get(params, url)
163 |
164 |
165 | # 查询某个订单的成交明细
166 | def order_matchresults(order_id):
167 | """
168 |
169 | :param order_id:
170 | :return:
171 | """
172 | params = {}
173 | url = "/v1/order/orders/{0}/matchresults".format(order_id)
174 | return api_key_get(params, url)
175 |
176 |
177 | # 查询当前委托、历史委托
178 | def orders_list(symbol, states, types=None, start_date=None, end_date=None, _from=None, direct=None, size=None):
179 | """
180 |
181 | :param symbol:
182 | :param states: 可选值 {pre-submitted 准备提交, submitted 已提交, partial-filled 部分成交, partial-canceled 部分成交撤销, filled 完全成交, canceled 已撤销}
183 | :param types: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
184 | :param start_date:
185 | :param end_date:
186 | :param _from:
187 | :param direct: 可选值{prev 向前,next 向后}
188 | :param size:
189 | :return:
190 | """
191 | params = {'symbol': symbol,
192 | 'states': states}
193 |
194 | if types:
195 | params[types] = types
196 | if start_date:
197 | params['start-date'] = start_date
198 | if end_date:
199 | params['end-date'] = end_date
200 | if _from:
201 | params['from'] = _from
202 | if direct:
203 | params['direct'] = direct
204 | if size:
205 | params['size'] = size
206 | url = '/v1/order/orders'
207 | return api_key_get(params, url)
208 |
209 |
210 | # 查询当前成交、历史成交
211 | def orders_matchresults(symbol, types=None, start_date=None, end_date=None, _from=None, direct=None, size=None):
212 | """
213 |
214 | :param symbol:
215 | :param types: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
216 | :param start_date:
217 | :param end_date:
218 | :param _from:
219 | :param direct: 可选值{prev 向前,next 向后}
220 | :param size:
221 | :return:
222 | """
223 | params = {'symbol': symbol}
224 |
225 | if types:
226 | params[types] = types
227 | if start_date:
228 | params['start-date'] = start_date
229 | if end_date:
230 | params['end-date'] = end_date
231 | if _from:
232 | params['from'] = _from
233 | if direct:
234 | params['direct'] = direct
235 | if size:
236 | params['size'] = size
237 | url = '/v1/order/matchresults'
238 | return api_key_get(params, url)
239 |
240 |
241 |
242 | # 申请提现虚拟币
243 | def withdraw(address, amount, currency, fee=0, addr_tag=""):
244 | """
245 | :param address_id:
246 | :param amount:
247 | :param currency:btc, ltc, bcc, eth, etc ...(火币Pro支持的币种)
248 | :param fee:
249 | :param addr-tag:
250 | :return: {
251 | "status": "ok",
252 | "data": 700
253 | }
254 | """
255 | params = {'address': address,
256 | 'amount': amount,
257 | "currency": currency,
258 | "fee": fee,
259 | "addr-tag": addr_tag}
260 | url = '/v1/dw/withdraw/api/create'
261 |
262 | return api_key_post(params, url)
263 |
264 | # 申请取消提现虚拟币
265 | def cancel_withdraw(address_id):
266 | """
267 | :param address_id:
268 | :return: {
269 | "status": "ok",
270 | "data": 700
271 | }
272 | """
273 | params = {}
274 | url = '/v1/dw/withdraw-virtual/{0}/cancel'.format(address_id)
275 |
276 | return api_key_post(params, url)
277 |
278 |
279 | '''
280 | 借贷API
281 | '''
282 |
283 | # 创建并执行借贷订单
284 |
285 |
286 | def send_margin_order(amount, source, symbol, _type, price=0):
287 | """
288 | :param amount:
289 | :param source: 'margin-api'
290 | :param symbol:
291 | :param _type: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
292 | :param price:
293 | :return:
294 | """
295 | try:
296 | accounts = get_accounts()
297 | acct_id = accounts['data'][0]['id']
298 | except BaseException as e:
299 | print ('get acct_id error.%s' % e)
300 | acct_id = ACCOUNT_ID
301 |
302 | params = {"account-id": acct_id,
303 | "amount": amount,
304 | "symbol": symbol,
305 | "type": _type,
306 | "source": 'margin-api'}
307 | if price:
308 | params["price"] = price
309 |
310 | url = '/v1/order/orders/place'
311 | return api_key_post(params, url)
312 |
313 | # 现货账户划入至借贷账户
314 |
315 |
316 | def exchange_to_margin(symbol, currency, amount):
317 | """
318 | :param amount:
319 | :param currency:
320 | :param symbol:
321 | :return:
322 | """
323 | params = {"symbol": symbol,
324 | "currency": currency,
325 | "amount": amount}
326 |
327 | url = "/v1/dw/transfer-in/margin"
328 | return api_key_post(params, url)
329 |
330 | # 借贷账户划出至现货账户
331 |
332 |
333 | def margin_to_exchange(symbol, currency, amount):
334 | """
335 | :param amount:
336 | :param currency:
337 | :param symbol:
338 | :return:
339 | """
340 | params = {"symbol": symbol,
341 | "currency": currency,
342 | "amount": amount}
343 |
344 | url = "/v1/dw/transfer-out/margin"
345 | return api_key_post(params, url)
346 |
347 | # 申请借贷
348 | def get_margin(symbol, currency, amount):
349 | """
350 | :param amount:
351 | :param currency:
352 | :param symbol:
353 | :return:
354 | """
355 | params = {"symbol": symbol,
356 | "currency": currency,
357 | "amount": amount}
358 | url = "/v1/margin/orders"
359 | return api_key_post(params, url)
360 |
361 | # 归还借贷
362 | def repay_margin(order_id, amount):
363 | """
364 | :param order_id:
365 | :param amount:
366 | :return:
367 | """
368 | params = {"order-id": order_id,
369 | "amount": amount}
370 | url = "/v1/margin/orders/{0}/repay".format(order_id)
371 | return api_key_post(params, url)
372 |
373 | # 借贷订单
374 | def loan_orders(symbol, currency, start_date="", end_date="", start="", direct="", size=""):
375 | """
376 | :param symbol:
377 | :param currency:
378 | :param direct: prev 向前,next 向后
379 | :return:
380 | """
381 | params = {"symbol": symbol,
382 | "currency": currency}
383 | if start_date:
384 | params["start-date"] = start_date
385 | if end_date:
386 | params["end-date"] = end_date
387 | if start:
388 | params["from"] = start
389 | if direct and direct in ["prev", "next"]:
390 | params["direct"] = direct
391 | if size:
392 | params["size"] = size
393 | url = "/v1/margin/loan-orders"
394 | return api_key_get(params, url)
395 |
396 |
397 | # 借贷账户详情,支持查询单个币种
398 | def margin_balance(symbol):
399 | """
400 | :param symbol:
401 | :return:
402 | """
403 | params = {}
404 | url = "/v1/margin/accounts/balance"
405 | if symbol:
406 | params['symbol'] = symbol
407 |
408 | return api_key_get(params, url)
--------------------------------------------------------------------------------
/exchangeapi/huobi/websocket.py:
--------------------------------------------------------------------------------
1 | from config import settings
2 | from service import k_line_service
3 | import gzip
4 | import json
5 | import logging
6 | import websocket
7 |
8 | logger = logging.getLogger("service")
9 |
10 | ###
11 | # 通过websocket与火币网实现通信
12 | ###
13 |
14 | def save_data(msg):
15 | if settings.DATABASE_RECORD and mongodb:
16 | try:
17 | collection = mongodb.get_collection(msg['ch'].replace('.', '_'))
18 | collection.insert_one(msg)
19 | except Exception as exp:
20 | logger.error("无法保存到数据库:" + str(exp))
21 |
22 |
23 | def send_message(ws, msg_dict):
24 | data = json.dumps(msg_dict).encode()
25 | logger.debug("发送消息:" + str(msg_dict))
26 | ws.send(data)
27 |
28 |
29 | def on_message(ws, message):
30 | unzipped_data = gzip.decompress(message).decode()
31 | msg_dict = json.loads(unzipped_data)
32 | if 'ping' in msg_dict:
33 | data = {
34 | "pong": msg_dict['ping']
35 | }
36 | logger.debug("收到ping消息: " + str(msg_dict))
37 | send_message(ws, data)
38 | elif 'subbed' in msg_dict:
39 | logger.debug("收到订阅状态消息:" + str(msg_dict))
40 | else:
41 | save_data(msg_dict)
42 | logger.debug("收到消息: " + str(msg_dict))
43 | kline_handler.handle_raw_message(msg_dict)
44 |
45 |
46 | def on_error(ws, error):
47 | error = gzip.decompress(error).decode()
48 | logger.error(str(error))
49 |
50 |
51 | def on_close(ws):
52 | logger.info("已断开连接")
53 |
54 |
55 | def on_open(ws):
56 | # 遍历settings中的货币对象
57 | for currency in settings.COINS.keys():
58 | subscribe = "market.{0}{1}.kline.{2}".format(currency, settings.SYMBOL, settings.PERIOD).lower()
59 | data = {
60 | "sub": subscribe,
61 | "id": currency
62 | }
63 | # 订阅K线图
64 | send_message(ws, data)
65 |
66 |
67 | def start():
68 | ws = websocket.WebSocketApp(
69 | "wss://api.huobipro.com/ws",
70 | on_open=on_open,
71 | on_message=on_message,
72 | on_error=on_error,
73 | on_close=on_close
74 | )
75 | ws.run_forever()
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | panadas
3 | websocket
4 | websocket-client
5 | talib
6 |
7 |
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 | import time
2 | import talib as ta
3 | import numpy as np
4 | import pandas as pd
5 | import json
6 | from strategy import trend
7 | from exchangeapi.huobi import rest_api
8 | from common.email import email_handler
9 |
10 | if __name__ == "__main__":
11 |
12 | while(True):
13 | result = rest_api.get_kline('ethusdt',"60min",100)
14 | # print(result.get('data'))
15 | df = pd.DataFrame(result.get('data'), columns=['id', 'open', 'close', 'low', 'high', 'amount', 'vol', 'count']).sort_values(by = 'id',axis = 0,ascending = True)
16 | # print(df)
17 |
18 | ret = trend.MACross(df, 'close', 10, 25);
19 | print(ret)
20 | if ret :
21 | email_handler.send_mail("huobi 十五分钟均线交叉", "火币十五分钟均线交叉")
22 | time.sleep(600)
--------------------------------------------------------------------------------
/runbts.py:
--------------------------------------------------------------------------------
1 | import time
2 | import threading
3 | import talib as ta
4 | import numpy as np
5 | import pandas as pd
6 | import json
7 | from strategy import trend
8 | from exchangeapi.huobi import rest_api
9 | from common.email import email_handler
10 |
11 | class huobiThread (threading.Thread):
12 | def __init__(self, threadID, symbol,period,lines,shortPeriod,longPeriod,delay=600):
13 | threading.Thread.__init__(self)
14 | self.threadID = threadID
15 | self.symbol = symbol
16 | self.period = period
17 | self.lines = lines
18 | self.shortPeriod = shortPeriod
19 | self.longPeriod = longPeriod
20 | self.delay = delay
21 |
22 | def run(self):
23 | print ("开始线程:" + self.threadID)
24 | monitor(self.threadID, self.symbol,self.period,self.lines,self.shortPeriod,self.longPeriod,self.delay)
25 | print ("退出线程:" + self.threadID)
26 |
27 |
28 | def monitor(name,symbol,period,lines,shortPeriod,longPeriod,delay=600):
29 | # 作为多线程的执行方法,对选定的交易对按照一定的周期进行监控,并且邮件通知,
30 | # 因为火币对频繁调用有一定的限制所以不能一直的去读取
31 | flag = 0; # 1 向上穿越 2下穿越,0 初始value
32 | while(True):
33 | # 检测 btsusdt 交易对,用来为比特股账户的操作提供依据
34 | result = rest_api.get_kline(symbol,period,lines)
35 | # print(result.get('data'))
36 | if result is None:
37 | continue
38 |
39 | df = pd.DataFrame(result.get('data'), columns=['id', 'open', 'close', 'low', 'high', 'amount', 'vol', 'count']).sort_values(by = 'id',axis = 0,ascending = True)
40 |
41 | ret = trend.MACross(df, 'close', shortPeriod, longPeriod);
42 | str = ""
43 | if ret == '向上穿越' and flag != 1:
44 | str="火币交易对:"+symbol+", 周期:"+period+" 向上穿越"
45 | flag = 1
46 |
47 | elif ret == '向下穿越' and flag !=2:
48 | str="火币交易对:"+symbol+", 周期:"+period+" 向下穿越"
49 | flag = 2
50 | else:
51 | str=""
52 | print('火币交易对:%s,flag:%d' % (name, flag))
53 | if not str == "":
54 | email_handler.send_mail(str, str)
55 | time.sleep(delay)
56 |
57 |
58 | if __name__ == "__main__":
59 | # 创建两个线程
60 | thread1 = huobiThread("btsusdt-15min", 'btsusdt', '15min', 200, 10, 30,60)
61 | thread2 = huobiThread("btsusdt-30min", 'btsusdt', '30min', 200, 7, 25,75)
62 | thread3 = huobiThread("btsusdt-60min", 'btsusdt', '60min', 200, 7, 25,85)
63 | thread4 = huobiThread("btsusdt-4h", 'btsusdt', '60min', 100, 7*4, 25*4,85)
64 |
65 | thread1.start()
66 | thread2.start()
67 | thread3.start()
68 | thread4.start()
69 |
70 |
71 |
--------------------------------------------------------------------------------
/strategy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__init__.py
--------------------------------------------------------------------------------
/strategy/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__init__.pyc
--------------------------------------------------------------------------------
/strategy/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/strategy/__pycache__/average_cross.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__pycache__/average_cross.cpython-37.pyc
--------------------------------------------------------------------------------
/strategy/__pycache__/k_line_service.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__pycache__/k_line_service.cpython-37.pyc
--------------------------------------------------------------------------------
/strategy/__pycache__/rest_api_service.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__pycache__/rest_api_service.cpython-37.pyc
--------------------------------------------------------------------------------
/strategy/__pycache__/trend.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__pycache__/trend.cpython-37.pyc
--------------------------------------------------------------------------------
/strategy/__pycache__/websocket_subscribe_service.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/strategy/__pycache__/websocket_subscribe_service.cpython-37.pyc
--------------------------------------------------------------------------------
/strategy/big_order_service.py:
--------------------------------------------------------------------------------
1 | import time
2 | import smtplib
3 | from contextlib import suppress
4 | from selenium import webdriver
5 | from selenium.webdriver.common.by import By
6 | from selenium.webdriver.support.ui import WebDriverWait
7 | from selenium.webdriver.support import expected_conditions as EC
8 |
9 | driver = webdriver.Chrome('C:\\Users\\themi\\Desktop\\chromedriver_win32\\chromedriver.exe')
10 | site=['https://www.huobipro.com/coin_coin/exchange/#eth_btc','https://www.huobipro.com/bch_btc/exchange/','https://www.huobipro.com/ltc_btc/exchange/']
11 | toc=['ETH','BCH','LTC']
12 | large=[] #!!![IMPORTANT] large[] needs to be cleared after one minutes in order to prevent failure of detection
13 | while True:
14 | iterator=0
15 | while iterator<3:
16 | driver.get(site[iterator])
17 | time.sleep(2)
18 | try:
19 | assert "BTC" in driver.title
20 | except:
21 | pass
22 | #find the element containing valuable info
23 | time.sleep(2)
24 | try:
25 | textamt = WebDriverWait(driver, 25).until(EC.presence_of_element_located((By.XPATH,"//dl[@class='market_trades_amount']"))).text.splitlines()
26 | texttyp = WebDriverWait(driver, 25).until(EC.presence_of_element_located((By.XPATH, "//dl[@class='market_trades_type']"))).text.splitlines()
27 | texttim = WebDriverWait(driver, 25).until(EC.presence_of_element_located((By.XPATH, "//dl[@class='market_trades_time']"))).text.splitlines()
28 | textpri = WebDriverWait(driver, 25).until(EC.presence_of_element_located((By.XPATH, "//dl[@class='market_trades_price']"))).text.splitlines()
29 | except:
30 | print("卧槽,又有bug了")
31 | #moniter large amount of transaction
32 | size=len(textamt)
33 | i=1
34 | while i5:
40 | if placeholder not in large:
41 | large.append(placeholder)
42 | print('【大单交易检测到!】 在'+ placeholder2+' 有价值 ' + placeholder+ ' 比特币的' + toc[iterator] + placeholder1+ ' 现价为 '+ placeholder3)
43 | #content='large amount of transaction detected, the amount is ' + str(placeholder)
44 | #sending(content)
45 | i=i+1
46 | iterator=iterator+1
47 | driver.close()
--------------------------------------------------------------------------------
/strategy/k_line_service.py:
--------------------------------------------------------------------------------
1 | import json
2 | import numpy
3 | import logging
4 | import time
5 | import datetime
6 | import http.client
7 | import statistics
8 | from collections import deque
9 |
10 | logger = logging.getLogger("service")
11 |
12 | # 记录每种虚拟货币的每笔交易
13 | transaction_dict = {}
14 | # 记录每种虚拟货币在1分钟内的分析数据,队列长度设置为10,即10分钟内的数据
15 | analyzed_queue_dict = {}
16 | # 记录10分钟内的"价格"变化,这里的"价格"目前直接用close的差计算,以后考虑通过交易量,标准差等计算
17 | price_change_dict = {}
18 |
19 | #获得usdt校验价格
20 | def get_usdt_sell_price():
21 | conn = http.client.HTTPSConnection("api-otc.huobi.pro")
22 | conn.request("GET", "/v1/otc/trade/list/public?coinId=2&tradeType=0¤tPage=1&payWay=&country=")
23 | res = conn.getresponse()
24 | try:
25 | data = json.loads(res.read().decode("utf-8"))['data']
26 | return statistics.mean(list(map(lambda x: x['price'], data)))
27 | except Exception as exp:
28 | logger.error("无法获得USDT交易卖出价:" + str(exp))
29 | return "失败"
30 |
31 | #获得当前财富
32 | def get_current_wealth():
33 | total = 0
34 | for key, value in settings.COINS.items():
35 | total += transaction_dict["market.%susdt.kline.1min" % key.lower()][-1]['tick']['close'] * value['AMOUNT']
36 | total += settings.USDT_CURRENCY
37 | return total
38 |
39 | #监听价格增长
40 | def trigger_price_increase_action(total_price_change):
41 | content = "价格上升:%.4f" % total_price_change
42 | logger.warning(content)
43 |
44 | # 60%购买当前平均10分钟内涨幅 * WEIGHT最高的, 40%购买第2高的
45 | if settings.USDT_CURRENCY > 0:
46 | sorted_prices = sorted(price_change_dict.items(), key=lambda x: x[1] * settings.COINS[x[0].split(".")[1].replace(settings.SYMBOL.lower(), "").upper()]['WEIGHT'], reverse=True)
47 | settings.COINS[sorted_prices[0][0].split(".")[1].replace(settings.SYMBOL.lower(), "").upper()]['AMOUNT'] = 0.998 * 0.6 * settings.USDT_CURRENCY / transaction_dict[sorted_prices[0][0]][-1]['tick']['close']
48 | settings.COINS[sorted_prices[1][0].split(".")[1].replace(settings.SYMBOL.lower(), "").upper()]['AMOUNT'] = 0.998 * 0.4 * settings.USDT_CURRENCY / transaction_dict[sorted_prices[1][0]][-1]['tick']['close']
49 | settings.USDT_CURRENCY = 0
50 | logger.info("总财富USDT: %.2f; 不折腾: %.2f; 时间: %s" % (get_current_wealth(), settings.ORIGINAL_WEALTH, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(transaction_dict['market.etcusdt.kline.1min'][-1]['ts'] / 1000))))
51 | send_mail("火币网价格上升", content)
52 |
53 | #监听价格下降
54 | def trigger_price_decrease_action(total_price_change):
55 | content = "价格下降:%.4f" % total_price_change
56 | logger.warning(content)
57 |
58 | # 查找当前价格涨幅最高的两个,如果我们已购买的货币在这里面,则不卖出
59 | sorted_prices = sorted(price_change_dict.items(), key=lambda x: x[1] / settings.COINS[x[0].split(".")[1].replace(settings.SYMBOL.lower(), "").upper()]['WEIGHT'], reverse=True)
60 | skip_list = [sorted_prices[0][0], sorted_prices[1][0]]
61 | for key, value in price_change_dict.items():
62 | if key in skip_list: continue
63 | coin = key.split(".")[1].replace(settings.SYMBOL.lower(), "").upper()
64 | # 全部卖掉
65 | if settings.COINS[coin]['AMOUNT'] > 0:
66 | settings.USDT_CURRENCY += 0.998 * settings.COINS[coin]['AMOUNT'] * transaction_dict[key][-1]['tick']['close']
67 | settings.COINS[coin]['AMOUNT'] = 0
68 | logger.info("总财富USDT: %.2f; 不折腾: %.2f; 时间: %s" % (get_current_wealth(), settings.ORIGINAL_WEALTH, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(transaction_dict['market.etcusdt.kline.1min'][-1]['ts'] / 1000))))
69 | send_mail("火币网价格下降", content)
70 |
71 | #预测并且监听消息
72 | def predict_and_notify(total_price_change):
73 | if total_price_change >= settings.PRICE_ALERT_INCREASE_POINT:
74 | trigger_price_increase_action(total_price_change)
75 | if total_price_change <= settings.PRICE_ALERT_DECREASE_POINT:
76 | trigger_price_decrease_action(total_price_change)
77 | logger.info("价格变动:%.4f;USDT当前售价:%.2f" % (total_price_change, get_usdt_sell_price()))
78 |
79 | #进行交易计算
80 | def perform_calculation():
81 | total_price = 0
82 | total_price_change = 0
83 | # 当收集满所有货币的10分钟内交易额后,计算才有意义
84 | if not len(price_change_dict) == len(settings.COINS):
85 | return
86 | # 取出price_change_dict中的所有数据,并根据settings中设置的WEIGHT决定是否购买
87 | for channel, price in price_change_dict.items():
88 | currency = channel.split(".")[1].replace(settings.SYMBOL.lower(), "").upper()
89 | weight = settings.COINS[currency]["WEIGHT"]
90 | total_price += settings.COINS[currency].get("AMOUNT", 0) * transaction_dict[channel][-1]['tick']['close']
91 | total_price_change += price * weight
92 | total_price_change /= sum(list(map(lambda x: x["WEIGHT"], settings.COINS.values())))
93 | predict_and_notify(total_price_change)
94 |
95 | #火币的交易量相关信息每60秒重置一次
96 | def update_data(channel):
97 | # 从transaction_dict[channel]中获取
98 | # 1. 1分钟内的涨跌幅
99 | # 2. 价格变化幅度(标准差)
100 | # 3. 1分钟内的交易额
101 | # 4. 1分钟最后一笔交易的价格
102 | if channel not in analyzed_queue_dict:
103 | analyzed_queue_dict[channel] = deque("", settings.N_MINUTES_STATE)
104 | data = {
105 | 'change': transaction_dict[channel][-1]['tick']['close'] - transaction_dict[channel][0]['tick']['close'],
106 | 'vol': transaction_dict[channel][-1]['tick']['vol'],
107 | 'mean': numpy.std(list(map(lambda x: x['tick']['close'], transaction_dict[channel]))),
108 | 'close': transaction_dict[channel][-1]['tick']['close'],
109 | }
110 | analyzed_queue_dict[channel].append(data)
111 | logger.debug("updated: " + channel)
112 | if len(analyzed_queue_dict[channel]) == settings.N_MINUTES_STATE:
113 | # 10分钟以内的"价格"变化
114 | price_change_dict[channel] = (analyzed_queue_dict[channel][-1]['close'] - analyzed_queue_dict[channel][0][
115 | 'close']) * 100 / analyzed_queue_dict[channel][0]['close']
116 | perform_calculation()
117 |
118 | #定期处理信息
119 | def handle_raw_message(msg_dict):
120 | channel = msg_dict['ch']
121 | if len(transaction_dict) == len(settings.COINS):
122 | total = 0
123 | for key, value in settings.ORIGINAL_COINS.items():
124 | total += transaction_dict["market.%susdt.kline.1min" % key.lower()][-1]['tick']['close'] * value['AMOUNT']
125 | settings.ORIGINAL_WEALTH = settings.ORIGINAL_USDT_CURRENCY + total
126 | if channel not in transaction_dict:
127 | transaction_dict[channel] = [msg_dict]
128 | else:
129 | if transaction_dict[channel][-1]['tick']['count'] > msg_dict['tick']['count']:
130 | # 每60秒计算一次已有数据,然后重置该channel
131 | update_data(channel)
132 | transaction_dict[channel] = [msg_dict]
133 | else:
134 | transaction_dict[channel].append(msg_dict)
--------------------------------------------------------------------------------
/strategy/second_kill_service.py:
--------------------------------------------------------------------------------
1 | import urllib
2 | import urllib2
3 | import json
4 | import time
5 | import sys
6 | import json
7 | import requests
8 | from service.rest_api_service import get_balance, send_order, cancel_order, order_matchresults
9 |
10 | #用户配置信息
11 | url_buy = 'https://api-otc.huobi.pro/v1/otc/trade/list/public?coinId=1&tradeType=1¤tPage=1&payWay=&country='
12 | url_sell = 'https://api-otc.huobi.pro/v1/otc/trade/list/public?coinId=1&tradeType=0¤tPage=1&payWay=&country='
13 | url_order = 'https://api-otc.huobi.pro/v1/otc/order/submit'
14 | url_get_order = 'https://api-otc.huobi.pro/v1/otc/order/confirm?order_ticket='
15 | agent = 'User-Agent,Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7'
16 | avg_data = []
17 | #---------------------#
18 | #防波动收购差价配置
19 | d_price = 1200
20 | #防做空收购数量境界
21 | max_buy_num = 0.18
22 | #爬虫定时/秒
23 | t = 5
24 | #输入用户cookie和token头的值
25 | cookie = ''
26 | token = ''
27 | #---------------------#
28 |
29 | #获取收售比特币的数据
30 | def get_buy(url,headers):
31 | req = urllib2.Request(url)
32 | req.add_header("User-Agent",headers)
33 | req.add_header("Host","api-otc.huobi.pro")
34 | content = urllib2.urlopen(req)
35 | buy_data = content.read()
36 | content.close()
37 | return buy_data
38 |
39 |
40 | #将json格式化,得出最低价格
41 | def json_data(jsondata):
42 | json_to_python = json.loads(jsondata)
43 | mindata_json = json_to_python['data'][0]
44 | new_min_json = json.dumps(mindata_json,ensure_ascii=False)
45 | new_min_data = json.loads(new_min_json)
46 | min_price = new_min_data['price']
47 | min_number = new_min_data['tradeCount']
48 | min_id = new_min_data['id']
49 | min_tradeNo = new_min_data['tradeNo']
50 | return min_price,min_number,min_id
51 |
52 |
53 | #取出售商家参考平均值
54 | def avg_sell(jsondata_sell):
55 | json_to_python = json.loads(jsondata_sell)
56 | for i in range(5):
57 | maxdata_json = json_to_python['data'][i]
58 | new_max_json = json.dumps(maxdata_json,ensure_ascii=False)
59 | new_max_data = json.loads(new_max_json)
60 | avg_data.append(new_max_data['price'])
61 | return (avg_data[0]+avg_data[1]+avg_data[2]+avg_data[3]+avg_data[4])//5
62 |
63 |
64 | #get_ticket脚本
65 | def get_ticket(buyNum,minBuy,tradeId):
66 | maxTradeLimit = buyNum * minBuy
67 | data = {'tradeId':tradeId,'amount':maxTradeLimit,'remark':''}
68 | req = urllib2.Request(url_order)
69 | req.add_header("User-Agent",agent)
70 | req.add_header("Host","api-otc.huobi.pro")
71 | req.add_header("token",token)
72 | req.add_header("Referer","https://otc.huobipro.com")
73 | req.add_header("Cookie",cookie)
74 | req.add_data(urllib.urlencode(data))
75 | content = urllib2.urlopen(req)
76 | order_data = content.read()
77 | content.close()
78 | json_to_python = json.loads(order_data)
79 | ticket_json = json_to_python['data']
80 | new_ticket_json = json.dumps(ticket_json,ensure_ascii=False)
81 | new_ticket = json.loads(new_ticket_json)
82 | try:
83 | ticket = new_ticket['ticket']
84 | except TypeError:
85 | ticket = 0
86 | return ticket
87 |
88 |
89 | #下单脚本
90 | def get_order(order_ticket):
91 | data = ''
92 | req = urllib2.Request(url_get_order+order_ticket)
93 | req.add_header("User-Agent",agent)
94 | req.add_header("Host","api-otc.huobi.pro")
95 | req.add_header("token",token)
96 | req.add_header("Referer","https://otc.huobipro.com")
97 | req.add_header("Cookie",cookie)
98 | req.add_data(urllib.urlencode(data))
99 | content = urllib2.urlopen(req)
100 | result_data = content.read()
101 | content.close()
102 | return result_data
103 |
104 |
105 | #主逻辑与下单脚本
106 | def btc_scan():
107 | buydata = get_buy(url_buy,agent)
108 | selldata = get_buy(url_sell,agent)
109 | minBuy,buyNum,tradeId = json_data(buydata)
110 | avgSell = avg_sell(selldata)
111 | if avgSell - minBuy > d_price:
112 | if buyNum < max_buy_num:
113 | ticket = get_ticket(buyNum,minBuy,tradeId)
114 | if ticket != 0:
115 | order_result = get_order(ticket)
116 | else:
117 | order_result = 'error order'
118 | return '1','price differences:'+str(avgSell - minBuy),ticket,order_result
119 | else:
120 | return '2','count to much!',buyNum
121 | else:
122 | return 0,'waitting','min buy price:'+str(minBuy),'sell avg price:'+str(avgSell)
123 |
124 |
125 | #定时执行
126 | def timer(n):
127 | while True:
128 | print time.strftime('%X',time.localtime()),
129 | print btc_scan()
130 | time.sleep(n)
131 | timer(t)
132 |
133 | ###
134 | # 等待买入的币种(小写)
135 | COIN1 = 'ela'
136 | # 你想买进的COIN1的数量
137 | COIN1_AMOUNT = 10
138 | # 当COIN1的价格小于这个价位,程序允许买入,单位为COIN2
139 | COIN1_PRICE = 0.00153
140 | # 用来支付的币种,在USDT/BTC/ETH中间选择
141 | COIN2 = 'btc'
142 |
143 | def get_tote(coin):
144 | balance_dict = get_balance()
145 | token = {coin: 0}
146 | token_list = balance_dict['data']['list']
147 | for item in token_list:
148 | if item['currency'] == coin and item['type'] == 'trade':
149 | token[coin] = item['balance']
150 | break
151 | return token[coin]
152 |
153 |
154 | def get_price(symbol):
155 | addr = 'https://api.huobi.pro/market/depth?symbol={symbol}&type=step0'
156 | resp_json = requests.get(addr.format(symbol=symbol)).text
157 | if '[]' not in resp_json:
158 | resp_dict = json.loads(resp_json)
159 | sell_price = resp_dict['tick']['asks'][0][0]
160 | return '%f' % sell_price
161 | else:
162 | return '0'
163 |
164 |
165 | def buy_limit(coin1, coin2, coin1_max_price, coin1_amount):
166 | coin1_price = get_price(coin1 + coin2)
167 | if 0 < float(coin1_price) < coin1_max_price:
168 | resp_dict = send_order(amount=str(coin1_amount),
169 | source='',
170 | symbol=coin1 + coin2,
171 | _type='buy-limit',
172 | price=coin1_price)
173 | return resp_dict
174 | else:
175 | return {'status': 'err'}
176 |
177 |
178 | def task():
179 | print('任务说明>> 我想用 {} 来买 {} '.format(COIN2, COIN1))
180 | print('钱包状况>> 我有 {} 个 {} '.format(get_tote(COIN2), COIN2))
181 | while True:
182 | resp_dict = buy_limit(COIN1, COIN2, COIN1_PRICE, COIN1_AMOUNT)
183 | if 'data' in resp_dict:
184 | order_id = resp_dict['data']
185 | print('当前进度>> 已下买单,订单ID为 {}'.format(order_id))
186 | if order_matchresults(order_id)['status'] != 'ok':
187 | print('订单追踪>> 未买到,取消订单...')
188 | cancel_order(order_id)
189 | else:
190 | print('订单追踪>> 已经买入,任务完成')
191 | break
192 | else:
193 | print('当前进度>> 暂未上币,或价格不符...')
194 |
195 |
196 | if __name__ == '__main__':
197 | if len(sys.argv) < 2:
198 | task()
--------------------------------------------------------------------------------
/strategy/trend.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 |
4 | import talib as ta
5 | from pandas.io.json import json_normalize
6 | import json
7 | import time
8 |
9 |
10 | # 使用talib库作为基础的指标库,只用获取对应的数据即可
11 | def SMA(df, period=5, type='close'):
12 | '''均线
13 | '''
14 |
15 | # df = df.sort_values(by = 'id',axis = 0,ascending = True)
16 | df['ma']= ta.SMA(df[type],period)
17 | return df
18 |
19 | def MACross(df, type='close', shortPeriod=5, longPeriod=20):
20 | '''均线交叉
21 | '''
22 | df['short'] = ta.SMA(df[type],shortPeriod)
23 | df['long'] = ta.SMA(df[type],longPeriod)
24 | shortMA = df['short']
25 | longMA = df['long']
26 | #print(df)
27 | if len(df) <= 2 :
28 | return '数据不足'
29 | if shortMA[0] > longMA[0] and shortMA[1] < longMA[1] :
30 | return '向上穿越';
31 | elif shortMA[0] < longMA[0] and shortMA[1] > longMA[1] :
32 | return '向下穿越'
33 | return '非穿越'
34 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | import talib as ta
2 | import numpy as np
3 |
4 | p = np.array([1.0,2.0,3.0,4.0,5.0,6.0])
5 | s = ta.MA(p, 5)
6 | print(s)
--------------------------------------------------------------------------------
/test/fake_run.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from config import settings
3 | from service import k_line_service
4 | from service import mongodb
5 |
6 | logger = Logging.getLogger(__name__)
7 |
8 | # 不通过websocket,直接通过读取到的数据库的值调用kline_handler
9 | if __name__ == "__main__":
10 | index_dict = {}
11 | currency_dict = {}
12 | if mongodb:
13 | for currency in settings.COINS.keys():
14 | index_dict["market_%susdt_kline_1min" % currency.lower()] = 0
15 |
16 | for collection_name in index_dict.keys():
17 | collection = mongodb.get_collection(collection_name)
18 | currency_dict[collection_name] = collection.find_one({}, skip=index_dict[collection_name])
19 |
20 | while True:
21 | # 从currency_dict中找到ts最小的key,更新该key
22 | min_ts_document_key = min(currency_dict.items(), key=lambda x: x[1]['_id'])[0]
23 | collection = mongodb.get_collection(min_ts_document_key)
24 | index_dict[min_ts_document_key] += 1
25 | kline_handler.handle_raw_message(currency_dict[min_ts_document_key])
26 | currency_dict[min_ts_document_key] = collection.find_one(
27 | {'ts': {'$gt': 1000 * int(settings.SIMULATE_START.timestamp()),
28 | '$lt': 1000 * int(settings.SIMULATE_END.timestamp())}}, skip=index_dict[min_ts_document_key])
29 | if currency_dict[min_ts_document_key] is None:
30 | break
31 | print("已完成处理")
--------------------------------------------------------------------------------
/test2.py:
--------------------------------------------------------------------------------
1 | from exchangeapi.huobi import rest_api
2 | import talib as ta
3 | import numpy as np
4 | import pandas as pd
5 | import json
6 |
7 | if __name__ == "__main__":
8 | #websocket.start()
9 | print(rest_api.get_kline('ethusdt',"1min",10))
10 |
11 | result = rest_api.get_kline('ethusdt',"1min",50)
12 |
13 | print(result.get('data'))
14 |
15 | df = pd.DataFrame(result.get('data'), columns=['id', 'open', 'close', 'low', 'high', 'amount', 'vol', 'count'])
16 | print(df)
17 | dm = df.sort_values(by = 'id',axis = 0,ascending = True)
18 |
19 | print(dm)
20 |
21 |
22 | #print(ta.EMA(df['close'],5))
23 | dm['ema10']= ta.EMA(dm['close'],10)
24 | dm['sma10']= ta.SMA(dm['close'],10)
25 | print(dm)
--------------------------------------------------------------------------------
/util/__pycache__/api_key_util.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/util/__pycache__/api_key_util.cpython-36.pyc
--------------------------------------------------------------------------------
/util/__pycache__/api_key_util.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/util/__pycache__/api_key_util.cpython-37.pyc
--------------------------------------------------------------------------------
/util/__pycache__/http_request_util.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/util/__pycache__/http_request_util.cpython-36.pyc
--------------------------------------------------------------------------------
/util/__pycache__/http_request_util.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Salmon-Bot/TradeRobot/860483cb8febe659bb9596d44136daa9732c2db1/util/__pycache__/http_request_util.cpython-37.pyc
--------------------------------------------------------------------------------
/util/api_key_util.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import datetime
3 | import hashlib
4 | import hmac
5 | import json
6 | import urllib
7 | import urllib.parse
8 | import urllib.request
9 | import requests
10 |
11 | from config.settings import ACCESS_KEY
12 | from config.settings import SECRET_KEY
13 | from config.settings import ACCOUNT_ID
14 | from config.settings import MARKET_URL
15 | from config.settings import TRADE_URL
16 |
17 | def api_key_get(params, request_path):
18 | method = 'GET'
19 | timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
20 | params.update({'AccessKeyId': ACCESS_KEY,
21 | 'SignatureMethod': 'HmacSHA256',
22 | 'SignatureVersion': '2',
23 | 'Timestamp': timestamp})
24 |
25 | host_url = TRADE_URL
26 | host_name = urllib.parse.urlparse(host_url).hostname
27 | host_name = host_name.lower()
28 | params['Signature'] = createSign(params, method, host_name, request_path, SECRET_KEY)
29 |
30 | url = host_url + request_path
31 | return http_get_request(url, params)
32 |
33 |
34 | def api_key_post(params, request_path):
35 | method = 'POST'
36 | timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
37 | params_to_sign = {'AccessKeyId': ACCESS_KEY,
38 | 'SignatureMethod': 'HmacSHA256',
39 | 'SignatureVersion': '2',
40 | 'Timestamp': timestamp}
41 |
42 | host_url = TRADE_URL
43 | host_name = urllib.parse.urlparse(host_url).hostname
44 | host_name = host_name.lower()
45 | params_to_sign['Signature'] = createSign(params_to_sign, method, host_name, request_path, SECRET_KEY)
46 | url = host_url + request_path + '?' + urllib.parse.urlencode(params_to_sign)
47 | return http_post_request(url, params)
48 |
49 |
50 | def createSign(pParams, method, host_url, request_path, secret_key):
51 | sorted_params = sorted(pParams.items(), key=lambda d: d[0], reverse=False)
52 | encode_params = urllib.parse.urlencode(sorted_params)
53 | payload = [method, host_url, request_path, encode_params]
54 | payload = '\n'.join(payload)
55 | payload = payload.encode(encoding='UTF8')
56 | secret_key = secret_key.encode(encoding='UTF8')
57 |
58 | digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest()
59 | signature = base64.b64encode(digest)
60 | signature = signature.decode()
61 | return signature
--------------------------------------------------------------------------------
/util/http_request_util.py:
--------------------------------------------------------------------------------
1 | import json
2 | import urllib
3 | import urllib.parse
4 | import urllib.request
5 | import requests
6 | import os
7 |
8 | #找到对应文件的路径,如果不在环境变量则添加到环境变量
9 | # file_path = os.path.dirname(os.path.dirname())
10 | # if file_path not in sys.path:
11 | # sys.path.insert(0,p)
12 |
13 | def http_get_request(url, params, add_to_headers=None):
14 | headers = {
15 | "Content-type": "application/x-www-form-urlencoded",
16 | 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
17 | }
18 | if add_to_headers:
19 | headers.update(add_to_headers)
20 | postdata = urllib.parse.urlencode(params)
21 | response = requests.get(url, postdata, headers=headers, timeout=5)
22 | try:
23 | if response.status_code == 200:
24 | return response.json()
25 | else:
26 | return
27 | except BaseException as e:
28 | print("httpGet failed, detail is:%s,%s" %(response.text,e))
29 | return
30 |
31 | def http_post_request(url, params, add_to_headers=None):
32 | headers = {
33 | "Accept": "application/json",
34 | 'Content-Type': 'application/json'
35 | }
36 | if add_to_headers:
37 | headers.update(add_to_headers)
38 | postdata = json.dumps(params)
39 | response = requests.post(url, postdata, headers=headers, timeout=10)
40 | try:
41 |
42 | if response.status_code == 200:
43 | return response.json()
44 | else:
45 | return
46 | except BaseException as e:
47 | print("httpPost failed, detail is:%s,%s" %(response.text,e))
48 | return
49 |
50 |
--------------------------------------------------------------------------------