├── __init__.py ├── exchange ├── __init__.py ├── fake.py ├── bitfinex.py ├── okex.py ├── poloniex.py └── huobi.py ├── .gitignore ├── .DS_Store ├── compare.png ├── test ├── test.py ├── email_test.py ├── asyncio_test.py ├── http_test.py ├── email.py ├── lock.py └── exchange.py ├── README.md ├── analyse ├── analyse3.py ├── orderBook.py ├── analyse.py └── analyse2.py ├── calculate.py ├── trades.py ├── shannon.py ├── deal2.py ├── deal.py └── grid.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /exchange/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.log 3 | *.pyc 4 | data/* 5 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exquisite2007/DigitalCurrency/HEAD/.DS_Store -------------------------------------------------------------------------------- /compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exquisite2007/DigitalCurrency/HEAD/compare.png -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import requests 3 | 4 | class test1: 5 | def __init__(self): 6 | pass 7 | def haha(self,param2): 8 | print('param2:{}'.format(param2)) 9 | async def main(self,parma1): 10 | print(parma1) 11 | self.haha(567) 12 | loop = asyncio.get_event_loop() 13 | future1 = loop.run_in_executor(None, requests.get, 'http://www.baidu.com') 14 | future2 = loop.run_in_executor(None, requests.get, 'http://www.126.com') 15 | response1 = await future1 16 | response2 = await future2 17 | print('Fiish:{}'.format(test1.__name__)) 18 | test=test1() 19 | loop = asyncio.get_event_loop() 20 | loop.run_until_complete(test.main(123)) -------------------------------------------------------------------------------- /test/email_test.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | from email.mime.text import MIMEText 3 | SMTP_SERVER = "smtp.mail.yahoo.com" 4 | SMTP_PORT = 587 5 | SMTP_USERNAME = "xiagao1234@yahoo.com" 6 | SMTP_PASSWORD = "88161732@yahoo" 7 | async def send_email(title,message): 8 | msg = MIMEText(message) 9 | msg['Subject'] = title 10 | msg['From'] = "xiagao1234@yahoo.com" 11 | msg['To'] = "lhongwei_2005@126.com" 12 | debuglevel = True 13 | mail = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) 14 | # mail.set_debuglevel(debuglevel) 15 | mail.starttls() 16 | mail.login(SMTP_USERNAME, SMTP_PASSWORD) 17 | mail.sendmail(EMAIL_FROM, EMAIL_TO, msg.as_string()) 18 | mail.quit() 19 | -------------------------------------------------------------------------------- /test/asyncio_test.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def phase1(): 5 | print('in phase1') 6 | await asyncio.sleep(2) 7 | print('done with phase1') 8 | return 'phase1 result' 9 | 10 | 11 | async def phase2(): 12 | print('in phase2') 13 | await asyncio.sleep(1) 14 | print('done with phase2') 15 | return 'phase2 result' 16 | 17 | 18 | async def main(): 19 | print('starting main') 20 | print('waiting for phases to complete') 21 | results = await asyncio.gather( 22 | phase1(), 23 | phase2(), 24 | ) 25 | print('results: {!r}'.format(results)) 26 | 27 | 28 | event_loop = asyncio.get_event_loop() 29 | try: 30 | event_loop.run_until_complete(main()) 31 | finally: 32 | event_loop.close() -------------------------------------------------------------------------------- /test/http_test.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | import asyncio 3 | import json 4 | async def handle(request): 5 | name = request.match_info.get('name', "Anonymous") 6 | text = "Hello, " + name 7 | return web.Response(text=text) 8 | async def test(app): 9 | while True: 10 | await asyncio.sleep(5) 11 | print('Test') 12 | async def test_handler(app): 13 | app.loop.create_task(test(app)) 14 | 15 | async def get_wallet(request): 16 | return web.json_response({}) 17 | async def change_threshold(request): 18 | # ok_buy=request.match_info['okbuy'] 19 | # poloniex_buy=request.match_info['poloniexbuy'] 20 | body = await request.json() 21 | print(request.transport.get_extra_info('peername')) 22 | print('{!r}'.format(body)) 23 | return web.json_response({}) 24 | 25 | app = web.Application() 26 | app.router.add_get('/wallet', get_wallet) 27 | app.router.add_post('/update', change_threshold) 28 | app.on_startup.append(test_handler) 29 | # print('here') 30 | web.run_app(app) 31 | -------------------------------------------------------------------------------- /test/email.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | from email.mime.text import MIMEText 3 | SMTP_SERVER = "smtp.mail.yahoo.com" 4 | SMTP_PORT = 587 5 | SMTP_USERNAME = "xiagao1234" 6 | SMTP_PASSWORD = "88161732@yahoo" 7 | EMAIL_FROM = "xiagao1234@yahoo.com" 8 | EMAIL_TO = "lhongwei_2005@126.com" 9 | EMAIL_SUBJECT = "REMINDER:" 10 | co_msg = """ 11 | Hello, [username]! Just wanted to send a friendly appointment 12 | reminder for your appointment: 13 | [Company] 14 | Where: [companyAddress] 15 | Time: [appointmentTime] 16 | Company URL: [companyUrl] 17 | Change appointment?? Add Service?? 18 | change notification preference (text msg/email) 19 | """ 20 | def send_email(): 21 | msg = MIMEText(co_msg) 22 | msg['Subject'] = EMAIL_SUBJECT + "Company - Service at appointmentTime" 23 | msg['From'] = EMAIL_FROM 24 | msg['To'] = EMAIL_TO 25 | debuglevel = True 26 | mail = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) 27 | mail.set_debuglevel(debuglevel) 28 | mail.starttls() 29 | mail.login(SMTP_USERNAME, SMTP_PASSWORD) 30 | mail.sendmail(EMAIL_FROM, EMAIL_TO, msg.as_string()) 31 | mail.quit() 32 | 33 | if __name__=='__main__': 34 | send_email() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DigitalCurrency 2 | 3 | 有一段时间,区块链特别火爆,而有的人通过虚拟货币交易实现了财务自由。仔细研究后,发现他们用的都是机器人(量化交易)。身为程序员,这个本应该是我的强项啊,于是乎写了几个策略。但当我兴冲冲的把脚本写好,发现浪潮已经过去。这个发财的路已经关闭了。感叹自己后知后觉。但也没有亏什么,起码对量化交易还有个初步的了解。也许有一天,可以用在股票交易上。 4 | 5 | ## 数据分析 6 | ----- 7 | trades.py 8 | 1. 爬取数据,通过交易所的开放API可以取到价格,交易量等信息 9 | 2. 存入sqlite,以此可以用matplotlib进行画图展示进行分析 10 | 11 | 为节省开支,每天的信息生成文件,可通过免费的七牛文件服务(10GB)上传。 12 | ![compare](compare.png) 13 | 14 | ## 网格交易法 15 | ---- 16 | grid.py 17 | 18 | 网格交易有很多变种,简单的举个例子。 19 | 假设你手里有100 个X币,每个币当前价值100块,且手里还有10000块钱。这时你的仓位是平衡的,50%的X币,50%的法币,资产总额20000块。 20 | 21 | 当X币上升到110块,仓位就不平衡了,资产总额21000。这个时候以110卖掉500块的币,X币数就成了100-500/110=95.45,法币数额是10500块。 22 | 23 | 当X币跌回100块,仓位又不平衡了,资产总额(95.45*100)+10500=20045块。这个时候要花掉(10500-20045/2)=477.5元去以100块价格买X币。币的总数变成了95.45+4.775=100.225个X币。 24 | 25 | 看到没,同样的100块的X币,来回一算计就多了22.5块和0.225个X币。当然,这个还没有扣除交易所手续费。要知道,早期交易所作单是没有手续费的,所以如果这个脚本在17年或更早的时候跑,是非常有利可图的。 26 | 脚本中会更复杂一些,以钱包为中心设计了5个状态: 27 | - 深绿(DG,X币价值多于法币,执行卖掉) 28 | - 浅绿(LG,X币价值多于法币,准备卖掉部分X币) 29 | - 白(W,平衡) 30 | - 浅红(LR,X币价值多于法币,准备买入X币) 31 | - 深红(DR,X币价值多于法币,执行买入) 32 | 33 | 其中LG、LR两个状态起到缓冲的作用,可以提前做单,等待其它人买入或卖出。 34 | 35 | ## 套利 36 | ---- 37 | deal.py 38 | 39 | 简单的来说,就是在不同交易所之前低买高卖。但是一定要效率高,甚至要求毫秒级的处理速度,这样才可以抢单。该策略是以吃单为主。 40 | - 使用websocket,这样以最快的速度,响应式的处理。 41 | - 使用异步IO, async。这个在python3 之后才有的功能。 42 | - 同时监听两个交易所的ask与bid,发现满足条件的价差就尝试去抢。 43 | 44 | ## 其它 45 | ---- 46 | 为什么没有继续下去? 47 | 48 | 也不一定后面不继续。 49 | 50 | 每种策略都有自身的局限,最关键的是要判断好行情的走势。这个就很难了。 51 | 比如网格交易法,这个是有些利润,但是这个最大的问题是在大幅波动的情况下,可能单边挂起,要么手里面全是X币,没有法币。要么就是全是法币,没有X币。这样算法就停止了。 52 | 53 | 套利的问题是,当时交易所的接口不是很稳定,因为监控行情的接口与下单是两条通路,如果出现滞后就会出现单边交易的情况。 54 | 55 | 上面这些还是比较初级的。有一段时间还想上增强学习来做交易,不过学习成本比较高。哪天看明白了再补充。 56 | 57 | 58 | support python3.5 above -------------------------------------------------------------------------------- /test/lock.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import functools 3 | 4 | 5 | def unlock(lock): 6 | print('callback releasing lock') 7 | lock.release() 8 | 9 | 10 | async def coro1(lock): 11 | # lock.locked() 12 | print('coro1 waiting for the lock') 13 | await lock 14 | try: 15 | await asyncio.sleep(3) 16 | print('coro1 acquired lock') 17 | finally: 18 | print('coro1 released lock') 19 | lock.release() 20 | 21 | 22 | async def coro2(lock): 23 | # lock.locked() 24 | print('coro2 waiting for the lock') 25 | await lock 26 | try: 27 | await asyncio.sleep(3) 28 | print('coro2 acquired lock') 29 | finally: 30 | print('coro2 released lock') 31 | lock.release() 32 | async def coro3(lock): 33 | # lock.locked() 34 | print('coro3 waiting for the lock') 35 | await lock 36 | try: 37 | await asyncio.sleep(3) 38 | print('coro3 acquired lock') 39 | finally: 40 | print('coro3 released lock') 41 | lock.release() 42 | 43 | async def main(loop): 44 | # Create and acquire a shared lock. 45 | lock = asyncio.Lock() 46 | print('acquiring the lock before starting coroutines') 47 | # await lock.acquire() 48 | # print('lock acquired: {}'.format(lock.locked())) 49 | 50 | # # Schedule a callback to unlock the lock. 51 | # loop.call_later(10, functools.partial(unlock, lock)) 52 | 53 | # Run the coroutines that want to use the lock. 54 | print('waiting for coroutines') 55 | await asyncio.wait([coro2(lock),coro1(lock),coro3(lock)]), 56 | 57 | 58 | event_loop = asyncio.get_event_loop() 59 | try: 60 | event_loop.run_until_complete(main(event_loop)) 61 | finally: 62 | event_loop.close() -------------------------------------------------------------------------------- /analyse/analyse3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | ''' 4 | base.analyze -- shortdesc 5 | 6 | base.analyze is a description 7 | 8 | It defines classes_and_methods 9 | 10 | @author: johnny 11 | 12 | @copyright: 2018 organization_name. All rights reserved. 13 | 14 | @license: license 15 | 16 | @contact: lihongwei.bupt@gmail.com 17 | @deffield updated: 2018-01-06 18 | ''' 19 | 20 | import sys 21 | import os 22 | import pandas as pd 23 | import sqlite3 24 | import matplotlib.pyplot as plt 25 | 26 | from optparse import OptionParser 27 | 28 | __all__ = [] 29 | __version__ = 0.1 30 | __date__ = '2018-01-06' 31 | __updated__ = '2018-01-06' 32 | 33 | SELECT_SQL='select diversion,timestamp from bookOrder where type=? and diversion>-10' 34 | 35 | 36 | def parse(pair,date): 37 | conn = sqlite3.connect('../data/orderbook_'+pair+'_'+date+'.db') 38 | freq='5S' 39 | cursor = conn.cursor() 40 | ex1_buy = pd.read_sql_query(SELECT_SQL, conn,params=(0,),index_col='timestamp') 41 | ex1_buy.index=pd.to_datetime(ex1_buy.index,unit='s') 42 | ex2_buy=pd.read_sql_query(SELECT_SQL, conn,params=(1,),index_col='timestamp') 43 | ex2_buy.index = pd.to_datetime(ex2_buy.index,unit='s') 44 | conn.close() 45 | span=20 46 | freq='600S' 47 | ewma=pd.ewma(ex1_buy, span=span, freq=freq,adjust=True) 48 | ewma.name='ex1 buy ewma1' 49 | res_df=pd.concat([ewma],axis=1) 50 | # print(res_df) 51 | 52 | ex1_buy.plot() 53 | plt.show() 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | def main(argv=None): 64 | parser = OptionParser() 65 | parser.add_option("-d", "--date", dest="date", help="select date") 66 | parser.add_option("-p", "--pair", dest="pair", help="set pair") 67 | # set defaults 68 | parser.set_defaults(pair='ETC_USDT',date="2018-03-06",) 69 | 70 | 71 | 72 | # process options 73 | (opts, args) = parser.parse_args(argv) 74 | parse(opts.pair,opts.date) 75 | 76 | 77 | 78 | 79 | 80 | 81 | if __name__ == "__main__": 82 | sys.exit(main()) -------------------------------------------------------------------------------- /analyse/orderBook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | from apscheduler.scheduler import Scheduler 4 | import sqlite3 5 | import time 6 | from datetime import datetime 7 | from os.path import isfile 8 | import requests 9 | import json 10 | import re 11 | import os 12 | import logging 13 | from logging.handlers import TimedRotatingFileHandler 14 | logger = logging.getLogger("apscheduler.scheduler") 15 | logger.setLevel(logging.DEBUG) 16 | ch = TimedRotatingFileHandler('orderbook.log', when='D', interval=1, backupCount=2) 17 | ch.setLevel(logging.DEBUG) 18 | 19 | # create formatter 20 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 21 | 22 | # add formatter to ch 23 | ch.setFormatter(formatter) 24 | 25 | # add ch to logger 26 | logger.addHandler(ch) 27 | 28 | #BTC_ETH 29 | #BTC_LTC 30 | #BTC_BCH 31 | #BTC_USDT 32 | #ETH_LTC 33 | # COIN_PAIR_LIST=['BTC_ETH','BTC_LTC','BTC_USDT','ETH_LTC'] 34 | COIN_PAIR_LIST=['BTC_ETH','BTC_LTC','ETH_LTC','BTC_USDT'] 35 | INSERT_SQL='insert into bookOrder (pair,ask1,bid1,timestamp,exchange) values(?,?,?,?,?)' 36 | CREATE_SQL='CREATE TABLE IF NOT EXISTS bookOrder (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,pair text,ask1 real,bid1 real,timestamp INTEGER,exchange text)' 37 | 38 | def OKEXTask(pair): 39 | try: 40 | exchange = 'okex' 41 | coinMap={'BTC_ETH':'eth_btc','BTC_LTC':'ltc_btc','BTC_USDT':'btc_usdt','ETH_LTC':'ltc_eth'} 42 | coinMap={'BTC_ETH':'eth_btc','BTC_LTC':'ltc_btc','BTC_USDT':'btc_usdt','ETH_LTC':'ltc_eth','ETC_USDT':'etc_usdt','ETC_USDT':'etc_usdt'} 43 | ok_url='https://www.okex.com/api/v1/depth.do?size=5&symbol='+coinMap[pair] 44 | ok_res = json.loads(requests.get(ok_url).text) 45 | return ok_res['asks'][0][0],ok_res['bids'][0][0] 46 | except Exception as e: 47 | logger.error(e) 48 | return None,None 49 | 50 | def poloniex(pair): 51 | try: 52 | coinMap={'BTC_ETH':'BTC_ETH','BTC_LTC':'BTC_LTC','BTC_USDT':'USDT_BTC','ETC_USDT':'USDT_ETC'} 53 | url='https://poloniex.com/public?command=returnOrderBook&depth=5¤cyPair='+coinMap[pair] 54 | res_text=requests.get(url).text 55 | poloniex_res=json.loads(res_text) 56 | 57 | return poloniex_res['asks'][0][0],poloniex_res['bids'][0][0] 58 | except Exception as e: 59 | logger.error(e) 60 | return None,None 61 | 62 | 63 | 64 | 65 | def cronTask(): 66 | logger.info('enter task') 67 | dbFile = 'orderbook_'+datetime.now().strftime("%Y-%m-%d")+'.db' 68 | conn = sqlite3.connect(dbFile) 69 | cursor = conn.cursor() 70 | cursor.execute(CREATE_SQL) 71 | pair='ETC_USDT' 72 | askBook1,bidBook1=OKEXTask(pair) 73 | askBook2,bidBook2=poloniex(pair) 74 | lst=[] 75 | ts= int(time.time()*1000) 76 | lst.append((pair,askBook1,bidBook1,ts,'okex')) 77 | lst.append((pair,askBook2,bidBook2,ts,'poloniex')) 78 | cursor.executemany(INSERT_SQL,lst) 79 | cursor.connection.commit() 80 | conn.close() 81 | logger.info('end task') 82 | 83 | 84 | 85 | if __name__ == '__main__': 86 | sched = Scheduler(standalone=True,misfire_grace_time=5) 87 | 88 | sched.add_interval_job(cronTask , seconds=5,coalesce=True) 89 | try: 90 | sched.start() 91 | except (KeyboardInterrupt, SystemExit): 92 | pass 93 | 94 | -------------------------------------------------------------------------------- /test/exchange.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | from apscheduler.scheduler import Scheduler 4 | import time 5 | from datetime import datetime 6 | import requests 7 | import sqlite3 8 | import json 9 | import logging 10 | from logging.handlers import TimedRotatingFileHandler 11 | logger = logging.getLogger("apscheduler.scheduler") 12 | logger.setLevel(logging.DEBUG) 13 | ch = TimedRotatingFileHandler('data.log', when='D', interval=1, backupCount=5) 14 | ch.setLevel(logging.DEBUG) 15 | 16 | # create formatter 17 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 18 | 19 | # add formatter to ch 20 | ch.setFormatter(formatter) 21 | 22 | # add ch to logger 23 | logger.addHandler(ch) 24 | # 返回买一价 ask,卖一价 bid 25 | def getOKEXLatestPrice(pair): 26 | try: 27 | coinMap={'BTC_ETH':'eth_btc','BTC_LTC':'ltc_btc','BTC_USDT':'btc_usdt','ETH_LTC':'ltc_eth','ETC_USDT':'etc_usdt'} 28 | ok_url='https://www.okex.com/api/v1/depth.do?size=5&symbol='+coinMap[pair] 29 | ok_res = json.loads(requests.get(ok_url).text) 30 | print(requests.get(ok_url).text) 31 | return ok_res['asks'],ok_res['bids'] 32 | except Exception as e: 33 | logger.error(e) 34 | return None,None 35 | 36 | def getPoloniexLatestPrice(pair): 37 | try: 38 | coinMap={'BTC_ETH':'BTC_ETH','BTC_LTC':'BTC_LTC','BTC_USDT':'USDT_BTC','ETC_USDT':'USDT_ETC'} 39 | url='https://poloniex.com/public?command=returnOrderBook&depth=5¤cyPair='+coinMap[pair] 40 | res_text=requests.get(url).text 41 | print(res_text) 42 | poloniex_res=json.loads(res_text) 43 | 44 | return poloniex_res['asks'],poloniex_res['bids'] 45 | except Exception as e: 46 | logger.error(e) 47 | return None,None 48 | 49 | def getWallet(cursor): 50 | cursor.execute("select exchange,ETC,USDT from wallet") 51 | res = cursor.fetchall() 52 | mapRes={} 53 | for item in res: 54 | mapRes[item[0]]={'ETC':item[1],'USDT':item[2]} 55 | return mapRes 56 | def ETCTask(): 57 | try: 58 | 59 | # conn = sqlite3.connect(dbFile) 60 | # cursor = conn.cursor() 61 | # WALLET=getWallet(cursor) 62 | pair='ETC_USDT' 63 | askBook1,bidBook1=getOKEXLatestPrice(pair) 64 | askBook2,bidBook2=getPoloniexLatestPrice(pair) 65 | # print(askBook1,askBook2,bidBook1,bidBook2) 66 | if askBook1 is None or askBook2 is None or bidBook1 is None or bidBook2 is None: 67 | logger.error("exchange Failed to get info") 68 | return 69 | if float(bidBook2[0][0])-askBook1[0][0]> 1.9*(askBook1[0][0]*0.001+float(bidBook2[0][0])*0.0015): 70 | logger.info("exchange happy okex buy:"+str(askBook1[0][0]) +" poloniex sell:"+str(float(bidBook2[0][0]))) 71 | elif bidBook1[0][0]-float(askBook2[0][0])> 1.9*(float(askBook2[0][0])*0.001+bidBook1[0][0]*0.0015): 72 | logger.info("exchange happy sell:"+str(bidBook1[0][0]) +" poloniex buy:"+str(float(askBook2[0][0]))) 73 | else: 74 | logger.info("exchange no change okex:"+str(askBook1[0][0])+' '+str(bidBook1[0][0])+' poloniex:'+askBook2[0][0]+' '+bidBook2[0][0]) 75 | 76 | 77 | except Exception as e: 78 | logger.error('etc '+str(e)) 79 | 80 | if __name__ == '__main__': 81 | sched = Scheduler(standalone=True,misfire_grace_time=5) 82 | 83 | sched.add_interval_job(ETCTask , seconds=6,coalesce=True) 84 | try: 85 | sched.start() 86 | except (KeyboardInterrupt, SystemExit): 87 | pass -------------------------------------------------------------------------------- /exchange/fake.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import hashlib 4 | import sys 5 | import os 6 | from optparse import OptionParser 7 | import logging 8 | import asyncio 9 | import websockets 10 | import random 11 | from exchange.okex import okexUtil 12 | logger = logging.getLogger("deal") 13 | 14 | class fakeUtil: 15 | def __init__(self,pair): 16 | self.name='FAKE' 17 | self.PAIR_MAP={'BTC_ETH':'eth_btc','BTC_LTC':'ltc_btc','BTC_USDT':'btc_usdt','ETH_LTC':'ltc_eth','ETC_USDT':'etc_usdt','LTC_USDT':'ltc_usdt'} 18 | self.CURRENT_PAIR=self.PAIR_MAP[pair] 19 | self.CURRENCY=self.CURRENT_PAIR.split('_') 20 | self.WALLET={self.CURRENCY[0]:{'free':1000},self.CURRENCY[1]:{'free':100000}} 21 | self.ORDER_BOOK={} 22 | self.TAKER_FEE=0.002 23 | # 补偿,买一个币,只能得到(1-self.TAKER_FEE)个币,为了保证两边币的数量一致,增加一个补偿量 24 | self.BUY_PATCH=(1+self.TAKER_FEE)*self.TAKER_FEE 25 | self.ask_head_all=None 26 | self.bid_head_all=None 27 | self.ticker_value=None 28 | self.interval=1 29 | self.order=None 30 | self.otherUtil=okexUtil(pair) 31 | access_key=None 32 | secret_key=None 33 | 34 | 35 | 36 | async def buy(self,rate,amount,is_market=False): 37 | await asyncio.sleep(self.interval) 38 | self.order={'type':'buy','price':rate,'amount':amount} 39 | return random.randrange(1000000,20000000) 40 | 41 | 42 | 43 | async def sell(self,rate,amount,is_market=False): 44 | await asyncio.sleep(self.interval) 45 | self.order={'type':'sell','price':rate,'amount':amount} 46 | return random.randrange(1000000,20000000) 47 | async def unfinish_order(self): 48 | await asyncio.sleep(self.interval) 49 | 50 | async def cancel_order(self,orderId): 51 | await asyncio.sleep(self.interval) 52 | self.order=None 53 | 54 | async def init_wallet(self): 55 | await asyncio.sleep(self.interval) 56 | logger.info('current wallet info {!r}'.format(self.WALLET)) 57 | async def order_info(self,orderId): 58 | return [{'status':2}] 59 | 60 | 61 | 62 | 63 | def get_orderbook_head(self): 64 | if self.ask_head_all is None or self.bid_head_all is None: 65 | raise Exception(self.name,'Error in get_orderbook_head') 66 | else: 67 | ask_heads=self.ask_head_all.split(':') 68 | bid_heads=self.bid_head_all.split(':') 69 | return (float(ask_heads[0]),float(ask_heads[1]),float(bid_heads[0]),float(bid_heads[1])) 70 | def get_sell_info(self,rate): 71 | if len(self.WALLET)<=0: 72 | raise Exception(self.name,'Error in get_sell_info') 73 | else: 74 | avaliable_amount=self.WALLET[self.CURRENCY[0]]['free'] 75 | cost=self.TAKER_FEE*rate 76 | return(avaliable_amount,cost) 77 | def get_buy_info(self,rate): 78 | if len(self.WALLET)<=0: 79 | raise Exception(self.name,'Error in get_buy_info') 80 | else: 81 | avaliable_amount=self.WALLET[self.CURRENCY[1]]['free']/rate/(1+self.BUY_PATCH) 82 | cost=self.TAKER_FEE*rate*(1+self.BUY_PATCH) 83 | return(avaliable_amount,cost) 84 | 85 | async def unfinish_order_handler(self): 86 | res = await self.unfinish_order() 87 | # if res is not None and len(res)>0: 88 | # for item in res: 89 | # head_res=self.get_orderbook_head() 90 | # if head_res is not None and item['type']=='sell' and head_res[2]-item['price']*1.001>0: 91 | # cancel_res= await self.cancel_order(item['order_id'],item['symbol']) 92 | # if cancel_res is not None and cancel_res['result']==True: 93 | # await self.sell(head_res[2],item['amount']) 94 | 95 | # if head_res is not None and item['type']=='buy' and item['price']*0.-head_res[0]*1.001>0: 96 | # cancel_res= await self.cancel_order(item['order_id'],item['symbol']) 97 | # if cancel_res is not None and cancel_res['result']==True: 98 | # await self.buy(head_res[0],item['amount']) 99 | async def trade_handler_wrapper(self): 100 | if self.otherUtil.ticker_value is None: 101 | return 102 | self.ticker_value=self.otherUtil.ticker_value 103 | (ask1,bid1,last) = self.otherUtil.ticker_value 104 | if self.order is not None and self.order['type']=='buy' and lastself.order['price']: 108 | self.WALLET[self.CURRENCY[0]]['free']-=self.order['amount'] 109 | self.WALLET[self.CURRENCY[1]]['free']+=self.order['amount']*self.order['price'] *0.998 110 | await self.trade_handler() 111 | async def ticker(self,trade_handler): 112 | self.trade_handler=trade_handler 113 | await self.otherUtil.ticker(self.trade_handler_wrapper) 114 | 115 | async def health_check(self): 116 | await self.otherUtil.health_check() 117 | async def refresh_wallet(self): 118 | while True: 119 | await asyncio.sleep(300) 120 | await self.init_wallet() 121 | 122 | -------------------------------------------------------------------------------- /calculate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import websockets 5 | import json 6 | import requests 7 | import logging 8 | from logging.handlers import TimedRotatingFileHandler 9 | import time 10 | import numpy as np 11 | import hmac 12 | import hashlib 13 | import os 14 | import sys 15 | import random 16 | from exchange.poloniex import poloniexUtil 17 | from exchange.okex import okexUtil 18 | from exchange.bitfinex import bitfinexUtil 19 | from exchange.huobi import huobiUtil 20 | from datetime import datetime 21 | import sqlite3 22 | import math 23 | SUPPORT_PAIR='ETC_USDT' 24 | if 'pair' in os.environ: 25 | SUPPORT_PAIR=os.environ['pair'] 26 | logger = logging.getLogger("deal") 27 | logger.setLevel(logging.DEBUG) 28 | ch = TimedRotatingFileHandler('cal_'+SUPPORT_PAIR+'.log', when='D', interval=1, backupCount=3) 29 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 30 | ch.setFormatter(formatter) 31 | logger.addHandler(ch) 32 | 33 | logger.info('BEGIN monitor {}'.format(SUPPORT_PAIR)) 34 | okexUtil=okexUtil(SUPPORT_PAIR) 35 | poloniexUtil=poloniexUtil(SUPPORT_PAIR) 36 | bitfinexUtil=bitfinexUtil(SUPPORT_PAIR) 37 | huobiUtil = huobiUtil(SUPPORT_PAIR) 38 | exchanges=[okexUtil,poloniexUtil,bitfinexUtil,huobiUtil] 39 | MINIST_VALUE=-999999 40 | exch1_exch2_max=MINIST_VALUE 41 | exch2_exch1_max=MINIST_VALUE 42 | exch1_exch2_lst=[] 43 | exch2_exch1_lst=[] 44 | SAMPLE_INTERVAL=1 45 | PERIORD=3*60*60 46 | REPORT_INTERVAL=60 47 | INSERT_SQL='insert into ticker_diff (diversion,timestamp,ex_buy,ex_sell) values(?,?,?,?)' 48 | CREATE_SQL='CREATE TABLE IF NOT EXISTS ticker_diff (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,diversion real,timestamp INTEGER,ex_buy text,ex_sell text)' 49 | COMBINATION=[(0,1),(1,0),(0,2),(1,2),(2,0),(2,1),(0,3),(1,3),(2,3),(3,0),(3,1),(3,2)] 50 | COMBINATION_INDEX=int(math.factorial(len(exchanges))/math.factorial(len(exchanges)-2)) 51 | ENABLE_TRADE_MODIFY=0 52 | if 'enable_trade_modify' in os.environ: 53 | ENABLE_TRADE_MODIFY=1 54 | 55 | async def trade_handler(): 56 | try: 57 | global COMBINATION 58 | global COMBINATION_INDEX 59 | global exchanges 60 | local_diff_max=-99999 61 | local_exchange_pair=None 62 | for item in COMBINATION[:COMBINATION_INDEX]: 63 | if exchanges[item[0]].ticker_value is None or exchanges[item[1]].ticker_value is None: 64 | continue 65 | diff = exchanges[item[0]].ticker_value[1]- exchanges[item[1]].ticker_value[0]-exchanges[item[0]].ticker_value[1]*exchanges[item[0]].TAKER_FEE-exchanges[item[1]].ticker_value[0]*exchanges[item[1]].TAKER_FEE 66 | 67 | if diff>local_diff_max: 68 | local_exchange_pair = item 69 | local_diff_max=diff 70 | 71 | if local_exchange_pair is not None: 72 | logger.info('buy from {} and sell from {}, difference is {}'.format(exchanges[local_exchange_pair[1]].name,exchanges[local_exchange_pair[0]].name,local_diff_max)) 73 | 74 | # if local_exchange_pair is not None and local_diff_max >0.02: 75 | if local_exchange_pair is not None and local_diff_max>0.02: 76 | dbFile = 'orderbook_'+SUPPORT_PAIR+'_'+datetime.now().strftime("%Y-%m-%d")+'.db' 77 | conn = sqlite3.connect(dbFile) 78 | cursor = conn.cursor() 79 | cursor.execute(CREATE_SQL) 80 | lst=[] 81 | ts= int(time.time()) 82 | lst.append((local_diff_max,ts,exchanges[local_exchange_pair[1]].name,exchanges[local_exchange_pair[0]].name)) 83 | cursor.executemany(INSERT_SQL,lst) 84 | cursor.connection.commit() 85 | conn.close() 86 | 87 | except Exception as e: 88 | logger.error("Trade_handler_error:{}".format(e)) 89 | 90 | async def percentile(): 91 | while True: 92 | global exch1_exch2_lst 93 | global exch2_exch1_lst 94 | await asyncio.sleep(REPORT_INTERVAL) 95 | logger.debug('percentile length:{},{}'.format(len(exch1_exch2_lst),len(exch2_exch1_lst))) 96 | enable=len(exch1_exch2_lst)>PERIORD 97 | if len(exch1_exch2_lst)> PERIORD: 98 | exch1_exch2_lst=exch1_exch2_lst[-PERIORD:] 99 | if len(exch2_exch1_lst) > PERIORD: 100 | exch2_exch1_lst=exch2_exch1_lst[-PERIORD:] 101 | logger.debug('percentile after length:{},{}'.format(len(exch1_exch2_lst),len(exch2_exch1_lst))) 102 | rg=[99.9,99.8,99.7,99.6,99.5,99.5,99.4,99.3,99.2,99.1,99,98,97,96,95,90,80] 103 | for item in rg: 104 | logger.info('REPORT RES {} exch1_buy:{}, exch2_buy:{}'.format(item,np.percentile(exch1_exch2_lst,item),np.percentile(exch2_exch1_lst,item))) 105 | global ENABLE_TRADE_MODIFY 106 | if ENABLE_TRADE_MODIFY==1 and enable: 107 | params={} 108 | ok_buy_thres=np.percentile(exch1_exch2_lst,99.9) 109 | poloniex_buy_thres=np.percentile(exch2_exch1_lst,99.9) 110 | if ok_buy_thres+poloniex_buy_thres >0.05: 111 | params['ok_buy_thres']=ok_buy_thres 112 | params['poloniex_buy_thres']=poloniex_buy_thres 113 | params['rand']=str(random.randint(1000000,2000000)) 114 | randStr='I am really poor'+params['rand'] 115 | params['sign']=hmac.new(randStr.encode(),digestmod=hashlib.sha256).hexdigest() 116 | r = requests.post("http://45.62.107.169:20183/threshold", data=json.dumps(params)) 117 | logger.info('FINISH update:{}'.format(r.text)) 118 | 119 | 120 | async def deal_handler(): 121 | return await asyncio.wait([item.ticker(trade_handler) for item in exchanges],return_when=asyncio.FIRST_COMPLETED,) 122 | 123 | loop=asyncio.get_event_loop() 124 | loop.run_until_complete(deal_handler()) 125 | -------------------------------------------------------------------------------- /analyse/analyse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | ''' 4 | base.analyze -- shortdesc 5 | 6 | base.analyze is a description 7 | 8 | It defines classes_and_methods 9 | 10 | @author: johnny 11 | 12 | @copyright: 2018 organization_name. All rights reserved. 13 | 14 | @license: license 15 | 16 | @contact: lihongwei.bupt@gmail.com 17 | @deffield updated: 2018-01-06 18 | ''' 19 | 20 | import sys 21 | import os 22 | import pandas as pd 23 | import sqlite3 24 | import matplotlib.pyplot as plt 25 | 26 | from optparse import OptionParser 27 | 28 | __all__ = [] 29 | __version__ = 0.1 30 | __date__ = '2018-01-06' 31 | __updated__ = '2018-01-06' 32 | 33 | SELECT_SQL='select timestamp,price,amount from trades where exchange=? and pair=? and type=\'sell\'' 34 | # [coin1,coin2] 35 | wallet=[[1.0,0.0],[0.0,20000.0]] 36 | def exchange(serial_item): 37 | wallet0=wallet[0] 38 | wallet1=wallet[1] 39 | base_amount=max(wallet0[0],wallet1[0]) 40 | 41 | if serial_item[0] - serial_item[1]-(0.002*serial_item[0]+0.002*serial_item[1])> 0 and wallet[0][0]>wallet[1][0]: 42 | #交易所0大于交易所1的价钱 且交易所0的钱包币种0的数量大于交易所1币种0的数量, 交易所0卖, 交易所1买 43 | 44 | # wallet[0]=[wallet0[0]-base_amount,base_amount*serial_item[0]*0.998+wallet0[1]] 45 | # wallet[1]=[wallet1[0]+base_amount*0.998,wallet1[1]-base_amount*serial_item[1]] 46 | wallet[0]=[0,wallet0[0]*serial_item[0]*0.998+wallet0[1]] 47 | wallet[1]=[wallet1[0]+wallet1[1]/serial_item[1]*0.998,0] 48 | print('exchange0 sell'+str(wallet)+str(serial_item.name)) 49 | return 50 | 51 | if serial_item[1]-serial_item[0] -(0.002*serial_item[0]+0.002*serial_item[1])>0 and wallet[0][0]0.24809: 41 | if ok_buy_profit >-0.02: 42 | x['ok_c']+=1 43 | if x['direction']==0: 44 | x['ok']+=1 45 | x['profit']+=ok_buy_profit 46 | x['direction']=1 47 | print('buy:'+str(ok_buy_profit)) 48 | return 49 | ok_sell_profit=serial_item[3]-serial_item[0]-(serial_item[3]*0.002+serial_item[0]*0.0025) 50 | if ok_sell_profit > -0.02: 51 | x['polo_c']+=1 52 | if x['direction']==1 : 53 | x['poloniex']+=1 54 | x['profit']+=ok_sell_profit 55 | x['direction']=0 56 | print('sell:'+str(ok_sell_profit)) 57 | return 58 | 59 | 60 | def parse(date,mode): 61 | conn = sqlite3.connect('data/orderbook_'+date+'.db') 62 | freq='5S' 63 | cursor = conn.cursor() 64 | # resLst=cursor.execute(SELECT_SQL, (exchange,pair)) 65 | okex_df = pd.read_sql_query(SELECT_SQL, conn,params=('okex',),index_col='timestamp') 66 | # df = pd.read_sql_table('trades',conn,) 67 | okex_df.index=pd.to_datetime(okex_df.index/1000,unit='s') 68 | # print(df['price'].resample('1H').ohlc().tail()) 69 | # print(df['amount'].resample('1H').sum().tail()) 70 | # print(df['price'].resample('1H').mean().tail()) 71 | # okex_mean_serial= okex_df['price'].resample(freq).mean() 72 | # okex_mean_serial.name='okex' 73 | 74 | 75 | 76 | poloniex_df=pd.read_sql_query(SELECT_SQL, conn,params=('poloniex',),index_col='timestamp') 77 | poloniex_df.index = pd.to_datetime(poloniex_df.index/1000,unit='s') 78 | conn.close() 79 | if mode==1: 80 | res_df = pd.concat([okex_df,poloniex_df],axis=1) 81 | res_df.apply(exchange,axis=1) 82 | print(x) 83 | elif mode==2: 84 | 85 | profit_ok_sell=2*(okex_df['bid1']-poloniex_df['ask1']-(okex_df['bid1']*0.002+poloniex_df['ask1']*0.0025))/(okex_df['bid1']+poloniex_df['ask1']) 86 | # first=okex_df['bid1']-poloniex_df['ask1'] 87 | profit_ok_sell.name='ok sell and poloniex buy' 88 | profit_ok_buy=2*(poloniex_df['bid1']-okex_df['ask1']-(okex_df['ask1']*0.002+poloniex_df['bid1']*0.0025))/(poloniex_df['bid1']+okex_df['ask1']) 89 | # second=poloniex_df['bid1']-okex_df['ask1'] 90 | profit_ok_buy.name='poloniex sell and ok buy' 91 | res_df=pd.concat([profit_ok_sell,profit_ok_buy],axis=1) 92 | # res_df.plot() 93 | # plt.figure(); 94 | res_df.plot.hist(bins=20,alpha=0.5) 95 | # cost_ok_sell=okex_df['bid1']*0.001+poloniex_df['ask1']*0.0015 96 | # cost_ok_sell.name='cost_ok_sell' 97 | # cost_ok_buy=okex_df['ask1']*0.001+poloniex_df['bid1']*0.0015 98 | # cost_ok_buy='cost_ok_buy' 99 | # # res_df=pd.concat([cosk_ok_sell,cost_ok_buy],axis=1) 100 | # res_df=cost_ok_sell/profit_ok_sell 101 | # res_df.plot() 102 | 103 | plt.show() 104 | elif mode==3: 105 | profit_ok_sell=okex_df['bid1']-poloniex_df['ask1']-(okex_df['bid1']*0.002+poloniex_df['ask1']*0.0025) 106 | profit_ok_sell.name='ok sell and poloniex buy' 107 | profit_ok_buy=poloniex_df['bid1']-okex_df['ask1']-(okex_df['ask1']*0.002+poloniex_df['bid1']*0.0025) 108 | # second=poloniex_df['bid1']-okex_df['ask1'] 109 | profit_ok_buy.name='poloniex sell and ok buy' 110 | res_df=pd.concat([profit_ok_sell,profit_ok_buy],axis=1) 111 | res_df.plot() 112 | plt.show() 113 | # plt.figure(); 114 | # res_df.plot.hist(bins=20,alpha=0.5) 115 | elif mode==4: #EWMA 116 | profit_ok_sell=okex_df['bid1']-poloniex_df['ask1'] 117 | profit_ok_sell.name='ok sell and poloniex buy' 118 | profit_ok_sell.name='ok sell and poloniex buy' 119 | profit_ok_buy=poloniex_df['bid1']-okex_df['ask1'] 120 | # second=poloniex_df['bid1']-okex_df['ask1'] 121 | profit_ok_buy.name='poloniex sell and ok buy' 122 | 123 | span=20 124 | freq='1H' 125 | ewma=pd.ewma(profit_ok_sell, span=span, freq=freq,adjust=True) 126 | ewma.name='oloniex buy ewma1' 127 | ewmstd=pd.ewmstd(profit_ok_sell, span=span, freq=freq) 128 | ewmstd.name='oloniex buy ewmstd1' 129 | 130 | ewma2=pd.ewma(profit_ok_buy, span=span, freq=freq,adjust=True) 131 | ewma2.name='ok buy ewma2' 132 | ewmstd2=pd.ewmstd(profit_ok_buy, span=span, freq=freq) 133 | ewmstd2.name='ok buy ewmstd2' 134 | upper=ewma+1.5*ewmstd 135 | upper.name='poloniex buy' 136 | lower=ewma2+1.5*ewmstd2 137 | lower.name='ok buy thres' 138 | res_df=pd.concat([ewma,upper,ewma2,lower],axis=1) 139 | res_df.plot() 140 | plt.show() 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | def main(argv=None): 151 | parser = OptionParser() 152 | parser.add_option("-d", "--date", dest="date", help="select date") 153 | parser.add_option("-t", "--toward", dest="toward", help="set direction") 154 | parser.add_option("-m", "--mode", dest="mode", help="set direction") 155 | # set defaults 156 | parser.set_defaults(toward=0,date="2018-01-25",mode=1) 157 | 158 | 159 | 160 | # process options 161 | (opts, args) = parser.parse_args(argv) 162 | x['direction']=int(opts.toward) 163 | parse(opts.date,int(opts.mode)) 164 | 165 | 166 | 167 | 168 | 169 | 170 | if __name__ == "__main__": 171 | sys.exit(main()) -------------------------------------------------------------------------------- /exchange/bitfinex.py: -------------------------------------------------------------------------------- 1 | 2 | import requests 3 | import json 4 | import hashlib 5 | import hmac 6 | import asyncio 7 | import websockets 8 | from time import time 9 | import base64 10 | import sys 11 | from optparse import OptionParser 12 | try: 13 | #python2 14 | from urllib import urlencode 15 | except ImportError: 16 | #python3 17 | from urllib.parse import urlencode 18 | import logging 19 | logger = logging.getLogger("deal") 20 | BOOK_LIMIT=10 21 | class bitfinexUtil: 22 | def __init__(self,pair): 23 | self.name='bitfinex' 24 | self.PAIR_MAP={'BTC_ETH':'ETHBTC','BTC_LTC':'LTCBTC','BTC_USDT':'BTCUSD','ETC_USDT':'ETCUSD'} 25 | self.CURRENT_PAIR=self.PAIR_MAP[pair] 26 | self.CURRENCY=[self.CURRENT_PAIR[:3],self.CURRENT_PAIR[-3:]] 27 | self.WALLET={} 28 | self.ORDER_BOOK={} 29 | self.TAKER_FEE=0.002 30 | self.ask_head_all=None 31 | self.bid_head_all=None 32 | self.ticker_value=None 33 | access_key=None 34 | secret_key=None 35 | @property 36 | def _nonce(self): 37 | """ 38 | Returns a nonce 39 | Used in authentication 40 | """ 41 | return str(time.time() * 1000000) 42 | 43 | def _sign_payload(self, payload): 44 | j = json.dumps(payload) 45 | data = base64.standard_b64encode(j.encode('utf8')) 46 | 47 | h = hmac.new(self.secret_key.encode('utf8'), data, hashlib.sha384) 48 | signature = h.hexdigest() 49 | return { 50 | "X-BFX-APIKEY": self.access_key, 51 | "X-BFX-SIGNATURE": signature, 52 | "X-BFX-PAYLOAD": data 53 | } 54 | async def order_book(self,trade_handler): 55 | while True: 56 | async with websockets.connect('wss://api.bitfinex.com/ws/2') as websocket: 57 | try: 58 | param={'event':'subscribe','channel':'book','symbol':self.CURRENT_PAIR,'prec':'P0','freq':'F0','len': '25'} 59 | await websocket.send(json.dumps(param)) 60 | while True: 61 | message = await websocket.recv() 62 | res=json.loads(message) 63 | if type(res) is not list: 64 | continue 65 | data=res[1] 66 | if len(data) >3: 67 | self.ORDER_BOOK['ask']={} 68 | self.ORDER_BOOK['bid']={} 69 | for item in data[25:25+BOOK_LIMIT]:#snapshot 70 | self.ORDER_BOOK['ask'][item[0]]=-item[1] 71 | for item in data[:BOOK_LIMIT]:#snapshot 72 | self.ORDER_BOOK['bid'][item[0]]=item[1] 73 | elif len(data)==3: 74 | if data[2]>0:#bid 75 | if data[1]==0 and data[0] in self.ORDER_BOOK['bid']: 76 | del self.ORDER_BOOK['bid'][data[0]] 77 | else: 78 | self.ORDER_BOOK['bid'][data[0]]=data[2] 79 | else: 80 | if data[1]==0 and data[0] in self.ORDER_BOOK['ask']: 81 | del self.ORDER_BOOK['ask'][data[0]] 82 | else: 83 | self.ORDER_BOOK['ask'][data[0]]=-data[2] 84 | ask_head=min(self.ORDER_BOOK['ask'],key=lambda subItem:float(subItem)) 85 | ask_head_volume=self.ORDER_BOOK['ask'][ask_head] 86 | bid_head=max(self.ORDER_BOOK['bid'],key=lambda subItem:float(subItem)) 87 | bid_head_volume=self.ORDER_BOOK['bid'][bid_head] 88 | ask_head_all=ask_head+':'+str(ask_head_volume) 89 | bid_head_all=bid_head+':'+str(bid_head_volume) 90 | if ask_head_all != self.ask_head_all or bid_head_all != self.bid_head_all: 91 | self.ask_head_all=ask_head_all 92 | self.bid_head_all=bid_head_all 93 | await trade_handler() 94 | except Exception as e: 95 | self.ORDER_BOOK={} 96 | logger.error('ERROR happen in bitfinex connection:{}'.format(e)) 97 | websocket.close() 98 | def get_orderbook_head(self): 99 | if self.ask_head_all is None or self.bid_head_all is None: 100 | raise Exception(self.name,'Error in get_orderbook_head') 101 | else: 102 | ask_heads=self.ask_head_all.split(':') 103 | bid_heads=self.bid_head_all.split(':') 104 | return (float(ask_heads[0]),float(ask_heads[1]),float(bid_heads[0]),float(bid_heads[1])) 105 | async def ticker(self,trade_handler): 106 | while True: 107 | try: 108 | async with websockets.connect('wss://api.bitfinex.com/ws/2') as websocket: 109 | 110 | param={'event':'subscribe','channel':'ticker','symbol':self.CURRENT_PAIR} 111 | await websocket.send(json.dumps(param)) 112 | while True: 113 | message = await websocket.recv() 114 | res=json.loads(message) 115 | if type(res) is not list: 116 | continue 117 | data=res[1] 118 | if type(data) is not list: 119 | continue 120 | ask1=float(data[0]) 121 | bid1=float(data[2]) 122 | last=float(data[6]) 123 | self.ticker_value=(ask1,bid1,last) 124 | await trade_handler() 125 | except Exception as e: 126 | self.ticker_value = None 127 | logger.error('ERROR happen in bitfinex connection:{}'.format(e)) 128 | async def buy(self,rate,amount,is_market=False): 129 | ord_type='limit' 130 | if is_market: 131 | ord_type='market' 132 | params={ 133 | "request": "/v1/order/new", 134 | "nonce": self._nonce, 135 | "symbol": self.CURRENT_PAIR, 136 | "amount": amount, 137 | "price": rate, 138 | "exchange": 'bitfinex', 139 | "side": 'buy', 140 | "type": ord_type 141 | } 142 | async def sell(self,rate,amount,is_market=False): 143 | ord_type='limit' 144 | if is_market: 145 | ord_type='market' 146 | params={ 147 | "request": "/v1/order/new", 148 | "nonce": self._nonce, 149 | "symbol": self.CURRENT_PAIR, 150 | "amount": amount, 151 | "price": rate, 152 | "exchange": 'bitfinex', 153 | "side": 'sell', 154 | "type": ord_type 155 | } 156 | async def cancel_order(self,orderId): 157 | params={ 158 | "request": "/v1/order/cancel", 159 | "nonce": self._nonce, 160 | "order_id": order_id 161 | } 162 | async def unfinish_order(self): 163 | params = { 164 | "request": "/v1/orders", 165 | "nonce": self._nonce 166 | } 167 | async def test(ask1,bid1,last): 168 | print('test:{},{},{}'.format(ask1,bid1,last)) 169 | def main(argv=None): 170 | parser = OptionParser() 171 | parser.add_option("-m", "--mode", dest="mode", help="0-wallet,1-buy,2-sell") 172 | parser.add_option("-r", "--rate", dest="rate", help="rate") 173 | parser.add_option("-a", "--amount", dest="amount", help="amount") 174 | parser.add_option("-p", "--pair", dest="pair", help="pair") 175 | parser.set_defaults(mode=0,pair='ETC_USDT') 176 | 177 | util = bitfinexUtil('ETC_USDT') 178 | 179 | # if 'bitfinex_access_key' not in os.environ: 180 | # return 181 | # util.access_key=os.environ['bitfinex_access_key'] 182 | # util.secret_key=os.environ['bitfinex_secret_key'] 183 | (opts, args) = parser.parse_args(argv) 184 | loop=asyncio.get_event_loop() 185 | loop.run_until_complete(util.ticker(test)) 186 | if __name__ == "__main__": 187 | sys.exit(main()) 188 | 189 | -------------------------------------------------------------------------------- /trades.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | from apscheduler.scheduler import Scheduler 4 | import sqlite3 5 | import time 6 | from datetime import datetime 7 | from os.path import isfile 8 | import requests 9 | import json 10 | import re 11 | import os 12 | from qiniu import Auth, put_file, etag 13 | import logging 14 | from logging.handlers import TimedRotatingFileHandler 15 | logger = logging.getLogger("apscheduler.scheduler") 16 | logger.setLevel(logging.DEBUG) 17 | ch = TimedRotatingFileHandler('data.log', when='D', interval=1, backupCount=5) 18 | ch.setLevel(logging.DEBUG) 19 | 20 | # create formatter 21 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 22 | 23 | # add formatter to ch 24 | ch.setFormatter(formatter) 25 | 26 | # add ch to logger 27 | logger.addHandler(ch) 28 | 29 | #BTC_ETH 30 | #BTC_LTC 31 | #BTC_BCH 32 | #BTC_USDT 33 | #ETH_LTC 34 | # COIN_PAIR_LIST=['BTC_ETH','BTC_LTC','BTC_USDT','ETH_LTC'] 35 | COIN_PAIR_LIST=['BTC_ETH','BTC_LTC','ETH_LTC','BTC_USDT'] 36 | INSERT_SQL='insert into trades values(?,?,?,?,?,?,?)' 37 | CREATE_SQL='CREATE TABLE IF NOT EXISTS trades (tid INTEGER,pair text, amount real, price real,type text,timestamp INTEGER,exchange text)' 38 | SELECT_SQL='select tid from trades where exchange=? and pair=? order by tid desc limit 1' 39 | 40 | def OKEXTask(cursor): 41 | try: 42 | exchange = 'okex' 43 | coinMap={'BTC_ETH':'eth_btc','BTC_LTC':'ltc_btc','BTC_USDT':'btc_usdt','ETH_LTC':'ltc_eth'} 44 | lst =[] 45 | 46 | for pair in COIN_PAIR_LIST: 47 | cursor.execute(SELECT_SQL, (exchange,pair)) 48 | last = cursor.fetchone() 49 | url='https://www.okex.com/api/v1/trades.do?symbol='+coinMap[pair] 50 | if last is not None: 51 | url+='&since='+str(last[0]) 52 | res = requests.get(url).text 53 | if res is not None: 54 | for item in json.loads(res): 55 | lst.append((int(item['tid']),pair,item['amount'],item['price'],item['type'],int(item['date_ms']),exchange)) 56 | logger.debug(lst[-1]) 57 | if len(lst)>0: 58 | cursor.executemany(INSERT_SQL,lst) 59 | cursor.connection.commit() 60 | logger.info( 'finish okex:'+str(len(lst))) 61 | except Exception as e: 62 | logger.error(e) 63 | 64 | def bittrexTask(cursor): 65 | try: 66 | exchange='bittrex' 67 | coinMap={'BTC_ETH':'BTC-ETH','BTC_LTC':'BTC-LTC','BTC_USDT':'USDT-BTC','ETH_LTC':'ETH-LTC'} 68 | 69 | lst=[] 70 | for pair in COIN_PAIR_LIST: 71 | cursor.execute(SELECT_SQL, (exchange,pair)) 72 | last = cursor.fetchone() 73 | url='https://bittrex.com/api/v1.1/public/getmarkethistory?market='+coinMap[pair] 74 | res = requests.get(url).text 75 | epoch = datetime.fromtimestamp(0) 76 | if res is not None: 77 | for item in json.loads(res)['result']: 78 | if last is None or int(item['Id']) > int(last[0]): 79 | ts=item['TimeStamp'] 80 | if ts.find('.')<0: 81 | ts+='.00' 82 | timestamp=int(((datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S.%f")-epoch).total_seconds()-3600*5)*1000) 83 | lst.append((int(item['Id']),pair,item['Quantity'],item['Price'],item['OrderType'].lower(),timestamp,exchange)) 84 | logger.debug(lst[-1]) 85 | if len(lst)>0: 86 | cursor.executemany(INSERT_SQL,lst) 87 | cursor.connection.commit() 88 | logger.info('finish bittrex:'+str(len(lst))) 89 | except Exception as e: 90 | logger.error(e) 91 | 92 | 93 | def bitfinexTask(cursor): 94 | try: 95 | exchange = 'bitfinex' 96 | coinMap={'BTC_ETH':'ethbtc','BTC_LTC':'ltcbtc','BTC_USDT':'btcusd'} 97 | 98 | lst=[] 99 | for pair in COIN_PAIR_LIST: 100 | if coinMap.has_key(pair): 101 | cursor.execute(SELECT_SQL, (exchange,pair)) 102 | last = cursor.fetchone() 103 | url='https://api.bitfinex.com/v1/trades/'+coinMap[pair]+'?limit_trades=1000' 104 | res = json.loads(requests.get(url).text) 105 | if type(res)==type([]): 106 | for item in res: 107 | if last is None or int(item['tid']) > int(last[0]): 108 | ts=item['timestamp']*1000 109 | lst.append((item['tid'],pair,float(item['amount']),float(item['price']),item['type'],ts,exchange)) 110 | logger.debug(lst[-1]) 111 | if len(lst)>0: 112 | cursor.executemany(INSERT_SQL,lst) 113 | cursor.connection.commit() 114 | logger.info('finish bitfinex:'+str(len(lst))) 115 | except Exception as e: 116 | logger.error(e) 117 | 118 | def poloniex(cursor): 119 | try: 120 | exchange = 'poloniex' 121 | coinMap={'BTC_ETH':'BTC_ETH','BTC_LTC':'BTC_LTC','BTC_USDT':'USDT_BTC'} 122 | 123 | lst=[] 124 | epoch = datetime.fromtimestamp(0) 125 | for pair in COIN_PAIR_LIST: 126 | if coinMap.has_key(pair): 127 | cursor.execute(SELECT_SQL, (exchange,pair)) 128 | last = cursor.fetchone() 129 | now = int(time.time()) 130 | fromP = str(now-100) 131 | toP = str(now) 132 | url='https://poloniex.com/public?command=returnTradeHistory¤cyPair='+coinMap[pair]+'&start='+fromP+'&end='+toP 133 | res = requests.get(url).text 134 | if res is not None: 135 | for item in json.loads(res): 136 | if last is None or item['globalTradeID'] > int(last[0]): 137 | ts=int(((datetime.strptime(item['date'], "%Y-%m-%d %H:%M:%S")-epoch).total_seconds()-5*3600)*1000) 138 | lst.append((item['globalTradeID'],pair,float(item['amount']),float(item['rate']),item['type'],ts,exchange)) 139 | logger.debug(lst[-1]) 140 | if len(lst)>0: 141 | cursor.executemany(INSERT_SQL,lst) 142 | cursor.connection.commit() 143 | logger.info('finish poloniex:'+str(len(lst))) 144 | except Exception as e: 145 | logger.error(e) 146 | 147 | 148 | 149 | 150 | def cronTask(): 151 | logger.info('enter task') 152 | dbFile = 'trades_'+datetime.now().strftime("%Y-%m-%d")+'.db' 153 | conn = sqlite3.connect(dbFile) 154 | cursor = conn.cursor() 155 | cursor.execute(CREATE_SQL) 156 | OKEXTask(cursor) 157 | bittrexTask(cursor) 158 | bitfinexTask(cursor) 159 | poloniex(cursor) 160 | conn.close() 161 | regex = re.compile('trades_.*.db') 162 | files = sorted(filter(regex.match,os.listdir('.')),reverse=True) 163 | 164 | if len(files) >1 and os.environ.has_key('access_key'): 165 | access_key = os.environ['access_key'] 166 | secret_key = os.environ['secret_key'] 167 | q = Auth(access_key, secret_key) 168 | bucket_name = 'stock' 169 | for item in files[1:]: 170 | token = q.upload_token(bucket_name, item, 60) 171 | ret, info =put_file(token, item, item) 172 | print info 173 | if ret['hash'] == etag(item): 174 | os.remove(item) 175 | logger.info('leave task') 176 | cronTask() 177 | if __name__ == '__main__': 178 | sched = Scheduler(standalone=True,misfire_grace_time=5) 179 | 180 | sched.add_interval_job(cronTask , seconds=5,coalesce=True) 181 | try: 182 | sched.start() 183 | except (KeyboardInterrupt, SystemExit): 184 | pass 185 | 186 | -------------------------------------------------------------------------------- /shannon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import websockets 5 | import json 6 | import requests 7 | from aiohttp import web 8 | import hmac 9 | import hashlib 10 | import random 11 | import logging 12 | from logging.handlers import TimedRotatingFileHandler 13 | logger = logging.getLogger("deal") 14 | logger.setLevel(logging.DEBUG) 15 | ch = TimedRotatingFileHandler('shannon.log', when='D', interval=1, backupCount=3) 16 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 17 | ch.setFormatter(formatter) 18 | logger.addHandler(ch) 19 | import sqlite3 20 | import os 21 | import sys 22 | from exchange.fake import fakeUtil 23 | from exchange.okex import okexUtil 24 | import time 25 | 26 | SUPPOR_PAIR='ETC_USDT' 27 | okexUtil=okexUtil(SUPPOR_PAIR) 28 | util=fakeUtil(SUPPOR_PAIR) 29 | 30 | util.WALLET[util.CURRENCY[0]]={'free':0} 31 | util.WALLET[util.CURRENCY[1]]={'free':1200} 32 | 33 | CREATE_SYSTEM_SQL='CREATE TABLE IF NOT EXISTS `system` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `key` TEXT NOT NULL, `value` TEXT NOT NULL )' 34 | SELECT_SYSTEM_SQL='SELECT * from system' 35 | UPDATE_SYSTEM_SQL='update system set value=? where key=?' 36 | INSERT_SYSTEM_SQL='insert into system (key,value) values(?,?)' 37 | conn = sqlite3.connect('trade.db') 38 | DIGITAL_COIN_NUM=None 39 | FIAT_COIN_NUM=None 40 | 41 | TRADE_LOCK=False 42 | 43 | 44 | BUY_RATE_THRESHOLD=0.0196 45 | SELL_RATE_THRESHOLD=0.02 46 | # BUY_RATE_THRESHOLD=0.04761904762 47 | # SELL_RATE_THRESHOLD=0.05 48 | # BUY_RATE_THRESHOLD=0.04761904762 49 | # SELL_RATE_THRESHOLD=0.05 50 | # BUY_RATE_THRESHOLD=0.0909 51 | # SELL_RATE_THRESHOLD=0.1 52 | # BUY_RATE_THRESHOLD=0.16668 53 | # SELL_RATE_THRESHOLD=0.2 54 | ORDER_ID=None #为空表示没有挂单,非空表示有挂单 55 | IS_INTITIAL_FINISH=False 56 | 57 | 58 | async def trade(): 59 | (ask1,bid1,last) = okexUtil.ticker_value 60 | logger.info('{},{},{}'.format(ask1,bid1,last)) 61 | global TRADE_LOCK 62 | global ORDER_ID 63 | global DIGITAL_COIN_NUM 64 | global FIAT_COIN_NUM 65 | 66 | 67 | global BUY_RATE_THRESHOLD 68 | global SELL_RATE_THRESHOLD 69 | if TRADE_LOCK: 70 | logger.debug('Ignore ticker') 71 | return 72 | TRADE_LOCK = True 73 | if DIGITAL_COIN_NUM is None or FIAT_COIN_NUM is None: 74 | await util.init_wallet() 75 | DIGITAL_COIN_NUM = util.WALLET[util.CURRENCY[0]]['free'] 76 | FIAT_COIN_NUM = util.WALLET[util.CURRENCY[1]]['free'] 77 | logger.info('re-fetch wallet info,DIGITAL_COIN_NUM :{},FIAT_COIN_NUM:{}'.format(DIGITAL_COIN_NUM,FIAT_COIN_NUM)) 78 | 79 | total_value = DIGITAL_COIN_NUM * last + FIAT_COIN_NUM 80 | diff = FIAT_COIN_NUM -total_value/2 81 | diff_rate = 1-DIGITAL_COIN_NUM * last/(FIAT_COIN_NUM+0.000000000001) 82 | state=None 83 | logger.info('CURRENCY diff: {},diff_rate:{},DIGITAL_COIN_NUM :{},FIAT_COIN_NUM:{}'.format(diff,diff_rate,DIGITAL_COIN_NUM,FIAT_COIN_NUM)) 84 | if diff_rate > BUY_RATE_THRESHOLD:#下段,法币远多于数字币,不平衡状态 85 | if ORDER_ID is None:# 86 | TRADE_LOCK=True 87 | amount=diff/ask1 88 | res =await util.buy(ask1,amount,is_market=True) 89 | logger.info('buy {} at marcket price to start,order_id is {}'.format(amount,res)) 90 | DIGITAL_COIN_NUM = None 91 | FIAT_COIN_NUM = None 92 | else:#从中下段 进入下段 93 | ORDER_ID = None 94 | last_balance_price = FIAT_COIN_NUM/DIGITAL_COIN_NUM 95 | balance_diff = DIGITAL_COIN_NUM*last_balance_price*BUY_RATE_THRESHOLD/2 96 | predict_balance_diff = (FIAT_COIN_NUM - DIGITAL_COIN_NUM*last_balance_price*(1-BUY_RATE_THRESHOLD))/2 97 | FIAT_COIN_NUM-=predict_balance_diff 98 | DIGITAL_COIN_NUM+=predict_balance_diff/(last_balance_price*(1-BUY_RATE_THRESHOLD)) 99 | state='dark_red' 100 | logger.info('trade buy {} at {}'.format(predict_balance_diff/(last_balance_price*(1-BUY_RATE_THRESHOLD)),last_balance_price*(1-BUY_RATE_THRESHOLD))) 101 | logger.info('state :{},{},{}'.format(FIAT_COIN_NUM,DIGITAL_COIN_NUM,FIAT_COIN_NUM/DIGITAL_COIN_NUM)) 102 | 103 | elif diff_rate > BUY_RATE_THRESHOLD /2 and diff_rate < BUY_RATE_THRESHOLD:#中下段,法币多,数字币少 104 | if ORDER_ID is None: 105 | last_balance_price = FIAT_COIN_NUM/DIGITAL_COIN_NUM 106 | predict_balance_diff = (FIAT_COIN_NUM - DIGITAL_COIN_NUM*last_balance_price*(1-BUY_RATE_THRESHOLD))/2 107 | 108 | ORDER_ID = await util.buy(last_balance_price*(1-BUY_RATE_THRESHOLD),predict_balance_diff/(last_balance_price*(1-BUY_RATE_THRESHOLD))) 109 | state='light_red' 110 | logger.info('state :{},{},{}'.format(FIAT_COIN_NUM,DIGITAL_COIN_NUM,FIAT_COIN_NUM/DIGITAL_COIN_NUM)) 111 | elif (diff_rate>= 0 and diff_rate <= BUY_RATE_THRESHOLD /2) or(diff_rate<0 and -diff_rate <= SELL_RATE_THRESHOLD /2): 112 | if ORDER_ID is not None: 113 | await util.cancel_order(ORDER_ID) 114 | ORDER_ID=None 115 | state='white' 116 | logger.info('state :{},{},{}'.format(FIAT_COIN_NUM,DIGITAL_COIN_NUM,FIAT_COIN_NUM/DIGITAL_COIN_NUM)) 117 | #TODO:平衡 118 | elif -diff_rate > SELL_RATE_THRESHOLD/2 and - diff_rate <= SELL_RATE_THRESHOLD:#中上段,法币少,数字币多 119 | if ORDER_ID is None: 120 | last_balance_price = FIAT_COIN_NUM/DIGITAL_COIN_NUM 121 | predict_balance_diff=(DIGITAL_COIN_NUM*last_balance_price*(1+SELL_RATE_THRESHOLD)-FIAT_COIN_NUM)/2 122 | ORDER_ID = await util.sell(last_balance_price*(1+SELL_RATE_THRESHOLD),predict_balance_diff/(last_balance_price*(1+SELL_RATE_THRESHOLD))) 123 | state='light_green' 124 | logger.info('state :{},{},{}'.format(FIAT_COIN_NUM,DIGITAL_COIN_NUM,FIAT_COIN_NUM/DIGITAL_COIN_NUM)) 125 | elif -diff_rate >SELL_RATE_THRESHOLD: #上段,数字币远多于法币 126 | if ORDER_ID is None:# 127 | amount=-diff/bid1 128 | res =await util.sell(bid1,amount,is_market=True) 129 | logger.info('sell {} at marcket price to start,order_id is {}'.format(amount,res)) 130 | DIGITAL_COIN_NUM = None 131 | FIAT_COIN_NUM = None 132 | else:#从中上段 进入上段 133 | ORDER_ID = None 134 | last_balance_price = FIAT_COIN_NUM/DIGITAL_COIN_NUM 135 | predict_balance_diff=(DIGITAL_COIN_NUM*last_balance_price*(1+SELL_RATE_THRESHOLD)-FIAT_COIN_NUM)/2 136 | FIAT_COIN_NUM+=predict_balance_diff 137 | DIGITAL_COIN_NUM-=predict_balance_diff/(last_balance_price*(1+SELL_RATE_THRESHOLD)) 138 | state='dark_green' 139 | logger.info('trade sell {} at {}'.format(predict_balance_diff/(last_balance_price*(1+SELL_RATE_THRESHOLD)),(last_balance_price*(1+SELL_RATE_THRESHOLD)))) 140 | logger.info('state :{},{},{}'.format(FIAT_COIN_NUM,DIGITAL_COIN_NUM,FIAT_COIN_NUM/DIGITAL_COIN_NUM)) 141 | TRADE_LOCK = False 142 | return state 143 | 144 | async def deal_handler(): 145 | return await asyncio.wait([okexUtil.ticker(trade),okexUtil.health_check()]) 146 | loop=asyncio.get_event_loop() 147 | # loop.run_until_complete(deal_handler()) 148 | async def test(): 149 | init_value =16.0 150 | # count = 100 151 | # direction=-1 152 | # while count>0: 153 | # count-=1 154 | # okexUtil.ticker_value=(init_value,init_value,init_value) 155 | # state=await trade() 156 | # print(count) 157 | # if state=='dark_green' or state == 'dark_red': 158 | # direction*=-1 159 | # init_value+=direction*0.01 160 | 161 | while init_value < 20: 162 | init_value+=0.01 163 | okexUtil.ticker_value=(init_value,init_value,init_value) 164 | await trade() 165 | 166 | while init_value>13: 167 | init_value-=0.01 168 | okexUtil.ticker_value=(init_value,init_value,init_value) 169 | await trade() 170 | 171 | loop.run_until_complete(test()) 172 | 173 | -------------------------------------------------------------------------------- /deal2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import websockets 5 | import json 6 | import requests 7 | from aiohttp import web 8 | import hmac 9 | import hashlib 10 | import random 11 | import logging 12 | from logging.handlers import TimedRotatingFileHandler 13 | logger = logging.getLogger("deal") 14 | logger.setLevel(logging.DEBUG) 15 | ch = TimedRotatingFileHandler('deal2.log', when='D', interval=1, backupCount=3) 16 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 17 | ch.setFormatter(formatter) 18 | logger.addHandler(ch) 19 | import sqlite3 20 | import os 21 | import sys 22 | from exchange.poloniex import poloniexUtil 23 | from exchange.okex import okexUtil 24 | from exchange.huobi import huobiUtil 25 | import time 26 | SUPPORT_PAIR='ETC_USDT' 27 | MAX_TRADE_SIZE=5 28 | poloniexUtil=poloniexUtil(SUPPORT_PAIR) 29 | huobiUtil = huobiUtil(SUPPORT_PAIR) 30 | exchanges=[poloniexUtil,huobiUtil] 31 | CREATE_SYSTEM_SQL='CREATE TABLE IF NOT EXISTS `system` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `key` TEXT NOT NULL, `value` TEXT NOT NULL )' 32 | SELECT_SYSTEM_SQL='SELECT * from system' 33 | UPDATE_SYSTEM_SQL='update system set value=? where key=?' 34 | INSERT_SYSTEM_SQL='insert into system (key,value) values(?,?)' 35 | CREATE_TRADE_SQL='CREATE TABLE IF NOT EXISTS `trade` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `ts` INTEGER NOT NULL, `per_profit` REAL NOT NULL, `amount` REAL NOT NULL, `ex_buy` TEXT NOT NULL,`ex_sell` TEXT NOT NULL,`buy_id` TEXT NOT NULL,`sell_id` TEXT NOT NULL)' 36 | INSERT_TRADE_SQL='insert into trade (ts,per_profit,amount,ex_buy,ex_sell,buy_id,sell_id)values(?,?,?,?,?,?,?)' 37 | FINISH_TRADE_LST=[] 38 | COMBINATION=[(0,1),(1,0)] 39 | THRES_MAP={} 40 | conn = sqlite3.connect('trade.db') 41 | def initAll(): 42 | logger.debug('start init all') 43 | for ex in exchanges: 44 | if ex.name+'_access_key' not in os.environ or ex.name+'_secret_key' not in os.environ: 45 | logger.error('{} has no access_key or secret_key in environment'.format(ex.name)) 46 | sys.exit() 47 | ex.access_key=os.environ[ex.name+'_access_key'] 48 | ex.secret_key=os.environ[ex.name+'_secret_key'] 49 | 50 | cursor = conn.cursor() 51 | cursor.execute(CREATE_SYSTEM_SQL) 52 | cursor.execute(CREATE_TRADE_SQL) 53 | cursor.execute(SELECT_SYSTEM_SQL) 54 | sysMap={} 55 | for item in cursor.fetchall(): 56 | sysMap[item[1]]=item[2] 57 | 58 | global THRES_MAP 59 | sysLst=[] 60 | for exch_pair in COMBINATION: 61 | thres_key=exchanges[exch_pair[0]].name+'_buy_'+exchanges[exch_pair[1]].name+'_sell_thres' 62 | if thres_key in sysMap: 63 | THRES_MAP[thres_key]=float(sysMap[thres_key]) 64 | else: 65 | sysLst.append((thres_key,str(0.1))) 66 | if len(sysLst)>0: 67 | cursor.executemany(INSERT_SYSTEM_SQL,sysLst) 68 | cursor.connection.commit() 69 | logger.info('Finish init all') 70 | cursor.close() 71 | 72 | trade_lock=False 73 | async def trade_handler(): 74 | global trade_lock 75 | global THRES_MAP 76 | global FINISH_TRADE_LST 77 | #at a time ,only one trade can be processed 78 | #at same time, not block other order book update 79 | if trade_lock: 80 | logger.debug('TradeLocked ignore the orderbook update') 81 | return 82 | trade_lock=True 83 | ts=int(time.time()) 84 | try: 85 | for exch_pair in COMBINATION: 86 | (ex1_ask_head,ex1_ask_head_volume,ex1_bid_head,ex1_bid_head_volume)=exchanges[exch_pair[0]].get_orderbook_head() 87 | (ex2_ask_head,ex2_ask_head_volume,ex2_bid_head,ex2_bid_head_volume)=exchanges[exch_pair[1]].get_orderbook_head() 88 | (ex1_avaliable_sell,ex1_sell_one_cost)=exchanges[exch_pair[0]].get_sell_info(ex1_bid_head) 89 | (ex2_availiable_buy,ex2_buy_one_cost)=exchanges[exch_pair[1]].get_buy_info(ex2_ask_head) 90 | ex2_buy_ex1_sell_profit=ex1_bid_head-ex2_ask_head-(ex1_sell_one_cost+ex2_buy_one_cost) 91 | if ex2_buy_ex1_sell_profit>THRES_MAP[exchanges[exch_pair[0]].name+'_buy_'+exchanges[exch_pair[1]].name+'_sell_thres']: 92 | min_volume=min([ex2_ask_head_volume,ex1_bid_head_volume,ex1_avaliable_sell,ex2_availiable_buy,MAX_TRADE_SIZE]) 93 | if min_volume< 0.01 or min_volume*ex2_ask_head<1: 94 | logger.debug('[trade]no enough volume for trade in {} buy,give up:{}'.format(ex2_buy_ex1_sell_profit)) 95 | else: 96 | results = await asyncio.gather(exchanges[exch_pair[0]].sell(ex1_bid_head,min_volume),exchanges[exch_pair[1]].buy(ex2_ask_head,min_volume),) 97 | FINISH_TRADE_LST.append((ts,ex2_buy_ex1_sell_profit,min_volume,exchanges[exch_pair[0]].name,exchanges[exch_pair[1]].name,results[0],results[1])) 98 | logger.info('[trade]Finish buy:{!r}. profit:{}'.format(results,ex2_buy_ex1_sell_profit)) 99 | logger.debug('buy from {} sell to {}:{} - {} ={}'.format( 100 | exchanges[exch_pair[0]].name,exchanges[exch_pair[1]].name, 101 | ex2_bid_head,ex1_ask_head,ex2_buy_ex1_sell_profit)) 102 | except Exception as e: 103 | logger.error("Trade_handler_error:{}".format(e)) 104 | trade_lock=False 105 | 106 | 107 | async def refreshWallet(): 108 | while True: 109 | await asyncio.wait([item.init_wallet() for item in exchanges],return_when=asyncio.FIRST_COMPLETED,) 110 | await asyncio.sleep(300) 111 | async def order_check(): 112 | global FINISH_TRADE_LST 113 | while True: 114 | await asyncio.sleep(60) 115 | if len(FINISH_TRADE_LST)>0: 116 | cursor = conn.cursor() 117 | cursor.executemany(INSERT_TRADE_SQL,FINISH_TRADE_LST) 118 | cursor.connection.commit() 119 | cursor.close() 120 | FINISH_TRADE_LST=[] 121 | logger.info('FINISH BACKUP trade item.') 122 | async def health_check(): 123 | await asyncio.wait([item.health_check() for item in exchanges],return_when=asyncio.FIRST_COMPLETED,) 124 | 125 | async def all_trade_handler(): 126 | await asyncio.wait([item.order_book(trade_handler) for item in exchanges],return_when=asyncio.FIRST_COMPLETED,) 127 | async def deal_handler(): 128 | initAll() 129 | return await asyncio.wait([all_trade_handler(),refreshWallet(),order_check(),health_check()],return_when=asyncio.FIRST_COMPLETED,) 130 | async def backgroud(app): 131 | app.loop.create_task(deal_handler()) 132 | 133 | 134 | 135 | async def get_threshold(request): 136 | global THRES_MAP 137 | return web.json_response(THRES_MAP) 138 | async def get_trade_info(request): 139 | cursor = conn.cursor() 140 | ts=int(time.time())-86400 141 | res={} 142 | cursor.execute('select count(1),sum(per_profit*amount) from trade where ts>?',(ts,)) 143 | db_res= cursor.fetchone() 144 | res['count']=db_res[0] 145 | res['sum'] = db_res[1] 146 | cursor.close() 147 | return web.json_response(res) 148 | async def change_threshold(request): 149 | peername = request.transport.get_extra_info('peername') 150 | if peername is None: 151 | return web.json_response({'msg':'unknown source request'}) 152 | if not (peername[0]=='45.62.107.169' or peername[0] =='172.96.18.216'or peername[0] == '127.0.0.1') : 153 | return web.json_response({'msg':'you are forbidden!!!'}) 154 | params = await request.json() 155 | if peername[0]=='172.96.18.216': 156 | print(params) 157 | randStr='I am really poor'+params['rand'] 158 | sign=hmac.new(randStr.encode(),digestmod=hashlib.sha256).hexdigest() 159 | if 'sign' not in params or sign!=params['sign']: 160 | return web.json_response({'msg':'invalid signature!!!'}) 161 | 162 | key = params['key'] 163 | value = float(params['value']) 164 | if value<0.01: 165 | return web.json_response({'msg':'failed, not in range1'}) 166 | print('{},{}'.format(key,value)) 167 | cursor = conn.cursor() 168 | cursor.executemany(UPDATE_SYSTEM_SQL,[(value,key)]) 169 | cursor.connection.commit() 170 | cursor.close() 171 | global THRES_MAP 172 | THRES_MAP[key]=float(value) 173 | logger.info('position changed. key:{},value:{}'.format(key,value)) 174 | return web.json_response({'msg':'successfully update'}) 175 | app = web.Application() 176 | app.router.add_get('/trade', get_trade_info) 177 | app.router.add_get('/threshold', get_threshold) 178 | app.router.add_post('/threshold', change_threshold) 179 | app.on_startup.append(backgroud) 180 | web.run_app(app,host='0.0.0.0',port=20183) -------------------------------------------------------------------------------- /deal.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import websockets 5 | import json 6 | import requests 7 | from aiohttp import web 8 | import hmac 9 | import hashlib 10 | import random 11 | import logging 12 | from logging.handlers import TimedRotatingFileHandler 13 | logger = logging.getLogger("deal") 14 | logger.setLevel(logging.DEBUG) 15 | ch = TimedRotatingFileHandler('deal.log', when='D', interval=1, backupCount=3) 16 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 17 | ch.setFormatter(formatter) 18 | logger.addHandler(ch) 19 | import sqlite3 20 | import os 21 | import sys 22 | from exchange.poloniex import poloniexUtil 23 | from exchange.okex import okexUtil 24 | from exchange.huobi import huobi 25 | import time 26 | SUPPOR_PAIR='ETC_USDT' 27 | MAX_TRADE_SIZE=5 28 | okexUtil=okexUtil(SUPPOR_PAIR) 29 | poloniexUtil=poloniexUtil(SUPPOR_PAIR) 30 | OK_BUY_THRES=0.1 31 | POLO_BUY_THRES=0.1 32 | CREATE_SYSTEM_SQL='CREATE TABLE IF NOT EXISTS `system` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `key` TEXT NOT NULL, `value` TEXT NOT NULL )' 33 | SELECT_SYSTEM_SQL='SELECT * from system' 34 | UPDATE_SYSTEM_SQL='update system set value=? where key=?' 35 | INSERT_SYSTEM_SQL='insert into system (key,value) values(?,?)' 36 | CREATE_TRADE_SQL='CREATE TABLE IF NOT EXISTS `trade` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `ts` INTEGER NOT NULL, `per_profit` REAL NOT NULL, `amount` REAL NOT NULL, `type` INTEGER NOT NULL )' 37 | INSERT_TRADE_SQL='insert into trade (ts,per_profit,amount,type)values(?,?,?,?)' 38 | HEALTH_CHECK_INTERVAL=60*4 39 | FINISH_TRADE_LST=[] 40 | conn = sqlite3.connect('trade.db') 41 | def initAll(): 42 | logger.debug('start init all') 43 | if 'ok_access_key' in os.environ and 'poloniex_access_key' in os.environ: 44 | okexUtil.access_key=os.environ['ok_access_key'] 45 | okexUtil.secret_key=os.environ['ok_secret_key'] 46 | poloniexUtil.access_key=os.environ['poloniex_access_key'].encode() 47 | poloniexUtil.secret_key=os.environ['poloniex_secret_key'].encode() 48 | else: 49 | logger.error('please check you exchange access key exist in your environment') 50 | sys.exit() 51 | cursor = conn.cursor() 52 | cursor.execute(CREATE_SYSTEM_SQL) 53 | cursor.execute(CREATE_TRADE_SQL) 54 | cursor.execute(SELECT_SYSTEM_SQL) 55 | sysMap={} 56 | for item in cursor.fetchall(): 57 | sysMap[item[1]]=item[2] 58 | global OK_BUY_THRES 59 | sysLst=[] 60 | if 'OK_BUY_THRES' in sysMap: 61 | OK_BUY_THRES=float(sysMap['OK_BUY_THRES']) 62 | else: 63 | sysLst.append(('OK_BUY_THRES',str(OK_BUY_THRES))) 64 | global POLO_BUY_THRES 65 | if 'POLO_BUY_THRES' in sysMap: 66 | POLO_BUY_THRES=float(sysMap['POLO_BUY_THRES']) 67 | else: 68 | sysLst.append(('POLO_BUY_THRES',str(POLO_BUY_THRES))) 69 | if len(sysLst)>0: 70 | cursor.executemany(INSERT_SYSTEM_SQL,sysLst) 71 | cursor.connection.commit() 72 | 73 | logger.info('Finish init all') 74 | cursor.close() 75 | 76 | trade_lock=False 77 | async def trade_handler(): 78 | global trade_lock 79 | global OK_BUY_THRES 80 | global POLO_BUY_THRES 81 | global FINISH_TRADE_LST 82 | #at a time ,only one trade can be processed 83 | #at same time, not block other order book update 84 | if trade_lock: 85 | logger.debug('TradeLocked ignore the orderbook update') 86 | return 87 | try: 88 | (ok_ask_head,ok_ask_head_volume,ok_bid_head,ok_bid_head_volume)=okexUtil.get_orderbook_head() 89 | (poloniex_ask_head,poloniex_ask_head_volume,poloniex_bid_head,poloniex_bid_head_volume)=poloniexUtil.get_orderbook_head() 90 | 91 | (ok_avaliable_buy,ok_buy_one_cost)=okexUtil.get_buy_info(ok_ask_head) 92 | (ok_avaliable_sell,ok_sell_one_cost)=okexUtil.get_sell_info(ok_bid_head) 93 | (poloniex_availiable_buy,poloniex_buy_one_cost)=poloniexUtil.get_buy_info(poloniex_ask_head) 94 | (poloniex_availiable_sell,poloniex_sell_one_cost)=poloniexUtil.get_sell_info(poloniex_bid_head) 95 | 96 | ok_buy_profit=poloniex_bid_head-ok_ask_head -(poloniex_sell_one_cost+ok_buy_one_cost) 97 | ts=int(time.time()) 98 | if ok_buy_profit>OK_BUY_THRES: 99 | min_volume=min([poloniex_bid_head_volume,ok_ask_head_volume,ok_avaliable_buy,poloniex_availiable_sell,MAX_TRADE_SIZE]) 100 | if min_volume< 0.01 or min_volume*poloniex_bid_head<1: 101 | logger.debug('[trade]no enough volume for trade in ok buy,give up:{}'.format(ok_buy_profit)) 102 | else: 103 | trade_lock=True 104 | results = await asyncio.gather(okexUtil.buy(ok_ask_head,min_volume),poloniexUtil.sell(poloniex_bid_head,min_volume),) 105 | logger.info('[trade]Finish okex buy:{!r}. profit:{}'.format(results,ok_buy_profit)) 106 | FINISH_TRADE_LST.append((ts,ok_buy_profit,min_volume,0)) 107 | trade_lock=False 108 | poloniex_buy_profit=ok_bid_head-poloniex_ask_head-(ok_sell_one_cost+poloniex_buy_one_cost) 109 | if poloniex_buy_profit>POLO_BUY_THRES: 110 | min_volume=min([poloniex_ask_head_volume,ok_bid_head_volume,ok_avaliable_sell,poloniex_availiable_buy,MAX_TRADE_SIZE]) 111 | if min_volume< 0.01 or min_volume*poloniex_ask_head<1: 112 | logger.debug('[trade]no enough volume for trade in poloniex buy,give up:{}'.format(poloniex_buy_profit)) 113 | else: 114 | trade_lock=True 115 | results = await asyncio.gather(okexUtil.sell(ok_bid_head,min_volume),poloniexUtil.buy(poloniex_ask_head,min_volume),) 116 | FINISH_TRADE_LST.append((ts,poloniex_buy_profit,min_volume,1)) 117 | trade_lock=False 118 | logger.info('[trade]Finish poloniex buy:{!r}. profit:{}'.format(results,poloniex_buy_profit)) 119 | logger.debug('buy_profit:{}:{}|{}:{}|{}|{}:{}|{}:{}|{}'.format( 120 | poloniex_bid_head,poloniex_bid_head_volume, 121 | ok_ask_head,ok_ask_head_volume, 122 | ok_buy_profit, 123 | ok_bid_head,ok_bid_head_volume, 124 | poloniex_ask_head,poloniex_ask_head_volume, 125 | poloniex_buy_profit)) 126 | except Exception as e: 127 | logger.error("Trade_handler_error:{}".format(e)) 128 | trade_lock=False 129 | 130 | 131 | async def refreshWallet(): 132 | while True: 133 | await asyncio.wait([poloniexUtil.init_wallet(),okexUtil.init_wallet()],return_when=asyncio.FIRST_COMPLETED,) 134 | await asyncio.sleep(300) 135 | async def order_check(): 136 | global FINISH_TRADE_LST 137 | while True: 138 | await asyncio.sleep(60) 139 | await asyncio.wait([poloniexUtil.unfinish_order_handler(),okexUtil.unfinish_order_handler()],return_when=asyncio.FIRST_COMPLETED,) 140 | if len(FINISH_TRADE_LST)>0: 141 | cursor = conn.cursor() 142 | cursor.executemany(INSERT_TRADE_SQL,FINISH_TRADE_LST) 143 | cursor.connection.commit() 144 | cursor.close() 145 | FINISH_TRADE_LST=[] 146 | logger.info('FINISH BACKUP trade item.') 147 | async def health_check(): 148 | await asyncio.wait([poloniexUtil.health_check(),okexUtil.health_check()],return_when=asyncio.FIRST_COMPLETED,) 149 | 150 | 151 | async def deal_handler(): 152 | initAll() 153 | return await asyncio.wait([poloniexUtil.order_book(trade_handler),okexUtil.order_book(trade_handler),refreshWallet(),order_check(),health_check()],return_when=asyncio.FIRST_COMPLETED,) 154 | async def backgroud(app): 155 | app.loop.create_task(deal_handler()) 156 | 157 | 158 | async def get_wallet(request): 159 | res={} 160 | res['ok']=okexUtil.WALLET 161 | res['poloniex']=poloniexUtil.WALLET 162 | return web.json_response(res) 163 | async def get_threshold(request): 164 | res={} 165 | res['OK_BUY_THRES'] = OK_BUY_THRES 166 | res['POLO_BUY_THRES'] = POLO_BUY_THRES 167 | return web.json_response(res) 168 | async def get_trade_info(request): 169 | cursor = conn.cursor() 170 | ts=int(time.time())-86400 171 | res={} 172 | cursor.execute('select count(1),sum(per_profit*amount) from trade where ts>?',(ts,)) 173 | db_res= cursor.fetchone() 174 | res['count']=db_res[0] 175 | res['sum'] = db_res[1] 176 | cursor.close() 177 | return web.json_response(res) 178 | async def change_threshold(request): 179 | peername = request.transport.get_extra_info('peername') 180 | if peername is None: 181 | return web.json_response({'msg':'unknown source request'}) 182 | if not (peername[0]=='45.62.107.169' or peername[0] =='172.96.18.216'or peername[0] == '127.0.0.1') : 183 | return web.json_response({'msg':'you are forbidden!!!'}) 184 | params = await request.json() 185 | if peername[0]=='172.96.18.216' or peername[0]=='127.0.0.1': 186 | print(params) 187 | randStr='I am really poor'+params['rand'] 188 | sign=hmac.new(randStr.encode(),digestmod=hashlib.sha256).hexdigest() 189 | if 'sign' not in params or sign!=params['sign']: 190 | return web.json_response({'msg':'invalid signature!!!'}) 191 | 192 | ok_buy_thres = params['ok_buy_thres'] 193 | poloniex_buy_thres = params['poloniex_buy_thres'] 194 | if ok_buy_thres+poloniex_buy_thres <0.04: 195 | return web.json_response({'msg':'failed, not in range'}) 196 | if ok_buy_thres<-0.01 or poloniex_buy_thres<-0.01: 197 | return web.json_response({'msg':'failed, not in range2'}) 198 | if abs(ok_buy_thres)>0.5 or abs(poloniex_buy_thres)>0.5: 199 | return web.json_response({'msg':'failed, not in range1'}) 200 | global OK_BUY_THRES 201 | global POLO_BUY_THRES 202 | OK_BUY_THRES=ok_buy_thres 203 | POLO_BUY_THRES=poloniex_buy_thres 204 | cursor = conn.cursor() 205 | cursor.executemany(UPDATE_SYSTEM_SQL,[(ok_buy_thres,'OK_BUY_THRES'),(poloniex_buy_thres,'POLO_BUY_THRES')]) 206 | cursor.connection.commit() 207 | cursor.close() 208 | logger.info('position changed. okex:{},poloniex:{}'.format(OK_BUY_THRES,POLO_BUY_THRES)) 209 | return web.json_response({'msg':'successfully update'}) 210 | app = web.Application() 211 | app.router.add_get('/wallet', get_wallet) 212 | app.router.add_get('/trade', get_trade_info) 213 | app.router.add_get('/threshold', get_threshold) 214 | app.router.add_post('/threshold', change_threshold) 215 | app.on_startup.append(backgroud) 216 | web.run_app(app,host='0.0.0.0',port=20183) -------------------------------------------------------------------------------- /grid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import websockets 5 | import json 6 | import requests 7 | from aiohttp import web 8 | import hmac 9 | import hashlib 10 | import random 11 | import logging 12 | from logging.handlers import TimedRotatingFileHandler 13 | logger = logging.getLogger("deal") 14 | logger.setLevel(logging.DEBUG) 15 | ch = TimedRotatingFileHandler('grid.log', when='D', interval=1, backupCount=5) 16 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 17 | ch.setFormatter(formatter) 18 | logger.addHandler(ch) 19 | import sqlite3 20 | import os 21 | import sys 22 | from exchange.okex import okexUtil 23 | from exchange.fake import fakeUtil 24 | import time 25 | 26 | SUPPOR_PAIR='ETC_OKB' 27 | util=okexUtil(SUPPOR_PAIR) 28 | # util=fakeUtil(SUPPOR_PAIR) 29 | 30 | 31 | 32 | CREATE_SYSTEM_SQL='CREATE TABLE IF NOT EXISTS `system` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `key` TEXT NOT NULL, `value` TEXT NOT NULL )' 33 | SELECT_SYSTEM_SQL='SELECT * from system' 34 | UPDATE_SYSTEM_SQL='update system set value=? where key=?' 35 | INSERT_SYSTEM_SQL='insert into system (key,value) values(?,?)' 36 | CREATE_TRADE_SQL='CREATE TABLE IF NOT EXISTS `trade` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `ts` INTEGER NOT NULL, `price` REAL NOT NULL, `amount` REAL NOT NULL, `type` INTEGER NOT NULL )' 37 | INSERT_TRADE_SQL='insert into trade (ts,price,amount,type)values(?,?,?,?)' 38 | conn = sqlite3.connect('trade.db') 39 | LAST_TRADE_PRICE_KEY='GRID_LAST_TRADE_PRICE' 40 | LAST_TRADE_PRICE=None 41 | BASE_TRADE_AMOUNT=0.2 42 | TRADE_LOCK=False 43 | STATE='W' 44 | FINISH_TRADE_LST=[] 45 | PRICE_LOCK=0# 0-无锁 1-阻止上涨 2-阻止下跌 46 | 47 | 48 | SELL_RATE_THRESHOLD=0.005 49 | BUY_RATE_THRESHOLD=SELL_RATE_THRESHOLD/(1+SELL_RATE_THRESHOLD) 50 | # BUY_RATE_THRESHOLD=0.0099 51 | # SELL_RATE_THRESHOLD=0.01 52 | # BUY_RATE_THRESHOLD=0.0196 53 | # SELL_RATE_THRESHOLD=0.02 54 | # BUY_RATE_THRESHOLD=0.04761904762 55 | # SELL_RATE_THRESHOLD=0.05 56 | # BUY_RATE_THRESHOLD=0.04761904762 57 | # SELL_RATE_THRESHOLD=0.05 58 | # BUY_RATE_THRESHOLD=0.0909 59 | # SELL_RATE_THRESHOLD=0.1 60 | # BUY_RATE_THRESHOLD=0.16668 61 | # SELL_RATE_THRESHOLD=0.2 62 | ORDER_ID=None #为空表示没有挂单,非空表示有挂单 63 | ORDER_CREATE_STATE=None#创建订单时的状态,为减少无效订单 64 | 65 | def initAll(): 66 | if 'okex_access_key' in os.environ: 67 | util.access_key=os.environ['okex_access_key'] 68 | util.secret_key=os.environ['okex_secret_key'] 69 | else: 70 | logger.error('please check you exchange access key exist in your environment') 71 | sys.exit() 72 | cursor = conn.cursor() 73 | cursor.execute(CREATE_SYSTEM_SQL) 74 | cursor.execute(CREATE_TRADE_SQL) 75 | cursor.execute(SELECT_SYSTEM_SQL) 76 | sysMap={} 77 | for item in cursor.fetchall(): 78 | sysMap[item[1]]=item[2] 79 | global LAST_TRADE_PRICE 80 | global LAST_TRADE_PRICE_KEY 81 | if LAST_TRADE_PRICE_KEY in sysMap: 82 | LAST_TRADE_PRICE = float(sysMap[LAST_TRADE_PRICE_KEY]) 83 | else: 84 | cursor.executemany(INSERT_SYSTEM_SQL,[(LAST_TRADE_PRICE_KEY,0)]) 85 | cursor.connection.commit() 86 | cursor.close() 87 | logger.info('Finish start up.LAST_TRADE_PRICE:{}'.format(LAST_TRADE_PRICE)) 88 | 89 | async def trade(): 90 | (ask1,bid1,last) = util.ticker_value 91 | logger.info('{},{},{}'.format(ask1,bid1,last)) 92 | global TRADE_LOCK 93 | global ORDER_ID 94 | global ORDER_CREATE_STATE 95 | global STATE 96 | global LAST_TRADE_PRICE 97 | global FINISH_TRADE_LST 98 | if LAST_TRADE_PRICE is None or LAST_TRADE_PRICE==0: 99 | LAST_TRADE_PRICE=last 100 | 101 | global BUY_RATE_THRESHOLD 102 | global SELL_RATE_THRESHOLD 103 | global PRICE_LOCK 104 | if TRADE_LOCK: 105 | logger.debug('Ignore ticker') 106 | return 107 | TRADE_LOCK = True 108 | ts=int(time.time()) 109 | try: 110 | diff_rate = (last -LAST_TRADE_PRICE)/LAST_TRADE_PRICE 111 | if diff_rate >SELL_RATE_THRESHOLD: #上段,数字币远多于法币 112 | STATE='DG' 113 | if ORDER_ID is None and diff_rate >1.5*SELL_RATE_THRESHOLD and PRICE_LOCK != 1:# 114 | LAST_TRADE_PRICE=(1+SELL_RATE_THRESHOLD)*LAST_TRADE_PRICE 115 | logger.error('price grow too fast,force chande last price:{}'.format(LAST_TRADE_PRICE)) 116 | elif ORDER_ID is not None:#从中上段 进入上段 117 | order_res = await util.order_info(ORDER_ID) 118 | if len(order_res)>0 and order_res[0]['status']==2: 119 | LAST_TRADE_PRICE=(1+SELL_RATE_THRESHOLD)*LAST_TRADE_PRICE 120 | ORDER_ID = None 121 | logger.info('state :{},{}'.format(util.WALLET,LAST_TRADE_PRICE)) 122 | FINISH_TRADE_LST.append((ts,LAST_TRADE_PRICE,BASE_TRADE_AMOUNT,0)) 123 | else: 124 | logger.error('error for confirm ordier:{}'.format(ORDER_ID)) 125 | elif diff_rate > SELL_RATE_THRESHOLD/2 and diff_rate <= SELL_RATE_THRESHOLD:#中上段,法币少,数字币多 126 | STATE='LG' 127 | if ORDER_ID is None: 128 | (ok_avaliable_sell,ok_sell_one_cost)=util.get_sell_info(LAST_TRADE_PRICE*(1+SELL_RATE_THRESHOLD)) 129 | if ok_avaliable_sell> BASE_TRADE_AMOUNT: 130 | PRICE_LOCK=0 131 | ORDER_ID = await util.sell(LAST_TRADE_PRICE*(1+SELL_RATE_THRESHOLD),BASE_TRADE_AMOUNT) 132 | ORDER_CREATE_STATE =STATE 133 | logger.info('state :{},{}'.format(diff_rate,last)) 134 | else: 135 | PRICE_LOCK=1 136 | logger.info('not enough to sell') 137 | elif ORDER_CREATE_STATE !=STATE: 138 | await util.cancel_order(ORDER_ID) 139 | ORDER_ID=None 140 | ORDER_CREATE_STATE=None 141 | logger.info('cancel order in LG state') 142 | elif (diff_rate< 0 and -diff_rate <= BUY_RATE_THRESHOLD /2) or(diff_rate>=0 and diff_rate <= SELL_RATE_THRESHOLD /2): 143 | STATE='W' 144 | elif -diff_rate > BUY_RATE_THRESHOLD /2 and -diff_rate < BUY_RATE_THRESHOLD:#中下段,法币多,数字币少 145 | STATE='LR' 146 | if ORDER_ID is None: 147 | (ok_avaliable_buy,ok_buy_one_cost)=util.get_buy_info(LAST_TRADE_PRICE*(1-BUY_RATE_THRESHOLD)) 148 | if ok_avaliable_buy >BASE_TRADE_AMOUNT: 149 | PRICE_LOCK=0 150 | ORDER_ID = await util.buy(LAST_TRADE_PRICE*(1-BUY_RATE_THRESHOLD),BASE_TRADE_AMOUNT) 151 | ORDER_CREATE_STATE =STATE 152 | logger.info('state :{},{}'.format(diff_rate,last)) 153 | else: 154 | PRICE_LOCK=2 155 | logger.info('not enough to buy') 156 | elif ORDER_CREATE_STATE !=STATE: 157 | await util.cancel_order(ORDER_ID) 158 | ORDER_ID=None 159 | ORDER_CREATE_STATE=None 160 | logger.info('cancel order in LG state') 161 | 162 | elif -diff_rate > BUY_RATE_THRESHOLD: 163 | STATE='DR' 164 | if ORDER_ID is None and -diff_rate> 1.5 * BUY_RATE_THRESHOLD and PRICE_LOCK != 2:# 165 | LAST_TRADE_PRICE=(1-BUY_RATE_THRESHOLD)*LAST_TRADE_PRICE 166 | logger.error('price drop too fast,force chande last price:{}'.format(LAST_TRADE_PRICE)) 167 | elif ORDER_ID is not None:#从中下段 进入下段 168 | order_res = await util.order_info(ORDER_ID) 169 | if len(order_res)>0 and order_res[0]['status']==2: 170 | LAST_TRADE_PRICE=(1-BUY_RATE_THRESHOLD)*LAST_TRADE_PRICE 171 | ORDER_ID = None 172 | FINISH_TRADE_LST.append((ts,LAST_TRADE_PRICE,BASE_TRADE_AMOUNT,1)) 173 | logger.info('state :{},{}'.format(util.WALLET,LAST_TRADE_PRICE)) 174 | else: 175 | logger.error('error for confirm ordier:{}'.format(ORDER_ID)) 176 | except Exception as e: 177 | logger.error("Trade_handler_error:{}".format(e)) 178 | TRADE_LOCK = False 179 | 180 | async def params_check(): 181 | 182 | while True: 183 | await asyncio.sleep(15) 184 | global LAST_TRADE_PRICE 185 | global LAST_TRADE_PRICE_KEY 186 | global FINISH_TRADE_LST 187 | 188 | cursor = conn.cursor() 189 | cursor.executemany(UPDATE_SYSTEM_SQL,[(LAST_TRADE_PRICE,LAST_TRADE_PRICE_KEY)]) 190 | cursor.connection.commit() 191 | if len(FINISH_TRADE_LST)>0: 192 | cursor = conn.cursor() 193 | cursor.executemany(INSERT_TRADE_SQL,FINISH_TRADE_LST) 194 | cursor.connection.commit() 195 | logger.info('Finish order check') 196 | FINISH_TRADE_LST=[] 197 | cursor.close() 198 | logger.info('Finish params check') 199 | 200 | 201 | async def deal_handler(): 202 | initAll() 203 | return await asyncio.wait([util.ticker(trade),util.health_check(),util.refresh_wallet(),params_check()]) 204 | 205 | 206 | async def backgroud(app): 207 | app.loop.create_task(deal_handler()) 208 | 209 | async def get_trade_report(request): 210 | cursor = conn.cursor() 211 | global SELL_RATE_THRESHOLD 212 | ts=int(time.time())-86400 213 | res={} 214 | rate=SELL_RATE_THRESHOLD-0.0015 215 | cursor.execute('select count(1),sum(price*amount*'+str(rate)+') from trade where ts>?',(ts,)) 216 | db_res= cursor.fetchone() 217 | res['count']=db_res[0] 218 | res['sum'] = db_res[1] 219 | cursor.close() 220 | return web.json_response(res) 221 | 222 | async def get_sysconfig(request): 223 | res={} 224 | global LAST_TRADE_PRICE 225 | global LAST_TRADE_PRICE_KEY 226 | global STATE 227 | global ORDER_ID 228 | global ORDER_CREATE_STATE 229 | res[LAST_TRADE_PRICE_KEY]=LAST_TRADE_PRICE 230 | res['state']=STATE 231 | if ORDER_ID is None: 232 | res['order_id']='empty' 233 | else: 234 | res['order_id']=ORDER_ID 235 | res['order_create_state']=ORDER_CREATE_STATE 236 | return web.json_response(res) 237 | async def change_sysconfig(request): 238 | peername = request.transport.get_extra_info('peername') 239 | logger.info(peername) 240 | if peername is None: 241 | return web.json_response({'msg':'unknown source request'}) 242 | if not (peername[0]=='45.62.107.169' or peername[0] =='172.96.18.216'or peername[0] == '127.0.0.1') : 243 | return web.json_response({'msg':'you are forbidden!!!'}) 244 | params = await request.json() 245 | global LAST_TRADE_PRICE 246 | global LAST_TRADE_PRICE_KEY 247 | cursor = conn.cursor() 248 | cursor.executemany(UPDATE_SYSTEM_SQL,[(params[LAST_TRADE_PRICE_KEY],LAST_TRADE_PRICE_KEY)]) 249 | cursor.connection.commit() 250 | cursor.close() 251 | LAST_TRADE_PRICE=float(params[LAST_TRADE_PRICE_KEY]) 252 | logger.info('position changed. key:{},value:{}'.format(LAST_TRADE_PRICE_KEY,LAST_TRADE_PRICE)) 253 | return web.json_response({'msg':'Successfully update'}) 254 | app = web.Application() 255 | app.router.add_get('/trade_report', get_trade_report) 256 | app.router.add_get('/sys_config', get_sysconfig) 257 | app.router.add_post('/sys_config', change_sysconfig) 258 | app.on_startup.append(backgroud) 259 | web.run_app(app,host='0.0.0.0',port=20184) 260 | -------------------------------------------------------------------------------- /exchange/okex.py: -------------------------------------------------------------------------------- 1 | 2 | import requests 3 | import json 4 | import hashlib 5 | import sys 6 | import os 7 | from optparse import OptionParser 8 | import logging 9 | import asyncio 10 | import websockets 11 | 12 | logger = logging.getLogger("deal") 13 | 14 | class okexUtil: 15 | def __init__(self,pair): 16 | self.name='okex' 17 | self.PAIR_MAP={'BTC_ETH':'eth_btc','BTC_LTC':'ltc_btc','BTC_USDT':'btc_usdt','ETH_LTC':'ltc_eth','ETC_USDT':'etc_usdt','LTC_USDT':'ltc_usdt','ETC_OKB':'etc_okb'} 18 | self.CURRENT_PAIR=self.PAIR_MAP[pair] 19 | self.CURRENCY=self.CURRENT_PAIR.split('_') 20 | self.WALLET={} 21 | self.ORDER_BOOK={} 22 | self.TAKER_FEE=0.002 23 | # 补偿,买一个币,只能得到(1-self.TAKER_FEE)个币,为了保证两边币的数量一致,增加一个补偿量 24 | self.BUY_PATCH=(1+self.TAKER_FEE)*self.TAKER_FEE 25 | self.ask_head_all=None 26 | self.bid_head_all=None 27 | self.ticker_value=None 28 | access_key=None 29 | secret_key=None 30 | 31 | 32 | 33 | def handleRequest(self,command,params={}): 34 | params['api_key']=self.access_key 35 | param_str=None 36 | for key in sorted(params.keys()): 37 | if param_str is None: 38 | param_str=key+'='+str(params[key]) 39 | else: 40 | param_str+='&'+key+'='+str(params[key]) 41 | m=hashlib.md5() 42 | param_str+='&secret_key='+self.secret_key 43 | m.update(param_str.encode('utf-8')) 44 | sign=m.hexdigest().upper() 45 | params['sign'] = sign 46 | try: 47 | url="https://www.okex.com/api/v1/"+command 48 | return json.loads(requests.post(url,data=params).text) 49 | except Exception as e: 50 | raise Exception(self.name,'Error in handleRequest:{}|{}|{}'.format(command,params,e)) 51 | 52 | 53 | async def buy(self,rate,amount,is_market=False): 54 | patch_amount=amount*(1+self.BUY_PATCH) 55 | self.WALLET[self.CURRENCY[1]]['free']-=patch_amount*rate 56 | self.WALLET[self.CURRENCY[1]]['locked']+=patch_amount*rate 57 | params={} 58 | if is_market: 59 | params={'symbol':self.CURRENT_PAIR,'type':'buy_market','amount':patch_amount} 60 | else: 61 | params={'symbol':self.CURRENT_PAIR,'type':'buy','price':rate,'amount':patch_amount} 62 | loop=asyncio.get_event_loop() 63 | res = await loop.run_in_executor(None,self.handleRequest,'trade.do',params) 64 | logger.debug('[OKEX] buy request {}|{}|{}.get result:{}'.format(self.CURRENT_PAIR,rate,patch_amount,res)) 65 | return res['order_id'] 66 | 67 | 68 | 69 | async def sell(self,rate,amount,is_market=False): 70 | self.WALLET[self.CURRENCY[0]]['free']-=amount 71 | self.WALLET[self.CURRENCY[0]]['locked']+=amount 72 | params={} 73 | if is_market: 74 | params={'symbol':self.CURRENT_PAIR,'type':'sell_market','amount':amount} 75 | else: 76 | params={'symbol':self.CURRENT_PAIR,'type':'sell','price':rate,'amount':amount} 77 | loop=asyncio.get_event_loop() 78 | res = await loop.run_in_executor(None,self.handleRequest,'trade.do',params) 79 | logger.debug('[OKEX] sell request {}|{}|{}get result:{}'.format(self.CURRENT_PAIR,rate,amount,res)) 80 | return res['order_id'] 81 | async def unfinish_order(self): 82 | loop=asyncio.get_event_loop() 83 | res = await loop.run_in_executor(None, self.handleRequest,'order_info.do',{'symbol':self.CURRENT_PAIR,'order_id':-1}) 84 | logger.debug('[OKEX] unfinished order get result:{}'.format(res)) 85 | print(res) 86 | if res is not None and res['result']==True: 87 | return res['orders'] 88 | else: 89 | raise Exception(self.name,'Error in unfinish_order') 90 | 91 | async def cancel_order(self,orderId): 92 | loop=asyncio.get_event_loop() 93 | params={'order_id':orderId,'symbol':self.CURRENT_PAIR} 94 | res = await loop.run_in_executor(None, self.handleRequest,'cancel_order.do',params) 95 | logger.debug('[OKEX] cancel_order get result:{}'.format(res)) 96 | print(res) 97 | if res is not None and 'result' in res and res['result']==True: 98 | return res['order_id'] 99 | else: 100 | raise Exception(self.name,'Error happen in cancel order {}|{}'.format(orderId,self.CURRENT_PAIR)) 101 | async def order_info(self,orderId): 102 | loop=asyncio.get_event_loop() 103 | res = await loop.run_in_executor(None, self.handleRequest,'order_info.do',{'order_id':orderId,'symbol':self.CURRENT_PAIR}) 104 | logger.debug('[OKEX] order_info get result:{}'.format(res)) 105 | if res is not None and res['result']==True: 106 | return res['orders'] 107 | else: 108 | raise Exception(self.name,'Error happen in order info {}|{}'.format(orderId,self.CURRENT_PAIR)) 109 | 110 | async def init_wallet(self): 111 | loop=asyncio.get_event_loop() 112 | res = await loop.run_in_executor(None, self.handleRequest,'userinfo.do',{}) 113 | self.WALLET={} 114 | if res is not None and res['result']==True: 115 | self.WALLET[self.CURRENCY[0]]={'free':float(res['info']['funds']['free'][self.CURRENCY[0]]),'locked':float(res['info']['funds']['freezed'][self.CURRENCY[0]])} 116 | self.WALLET[self.CURRENCY[1]]={'free':float(res['info']['funds']['free'][self.CURRENCY[1]]),'locked':float(res['info']['funds']['freezed'][self.CURRENCY[1]])} 117 | logger.info('Finish load okex wallet:{}'.format(self.WALLET)) 118 | else: 119 | raise Exception(self.name,'Error in init_wallet') 120 | 121 | async def order_book(self,trade_handler): 122 | channel='ok_sub_spot_'+self.CURRENT_PAIR+'_depth_5' 123 | while True: 124 | try: 125 | logger.info('OKEX BOOK start to connect') 126 | async with websockets.connect('wss://real.okex.com:10441/websocket') as websocket: 127 | 128 | logger.info('OKEX enter communication') 129 | param={'event':'addChannel','channel':channel} 130 | await websocket.send(json.dumps(param)) 131 | while True: 132 | message = await websocket.recv() 133 | res=json.loads(message) 134 | if type(res) is list and res[0]['channel'].startswith('ok'): 135 | ask_map={} 136 | for item in res[0]['data']['asks']: 137 | ask_map[item[0]]=float(item[1]) 138 | self.ORDER_BOOK['ask']=ask_map 139 | bid_map={} 140 | for item in res[0]['data']['bids']: 141 | bid_map[item[0]]=float(item[1]) 142 | self.ORDER_BOOK['bid']=bid_map 143 | 144 | ask_head=min(self.ORDER_BOOK['ask'],key=lambda subItem:float(subItem)) 145 | ask_head_volume=self.ORDER_BOOK['ask'][ask_head] 146 | ask_head_all=ask_head+':'+str(ask_head_volume) 147 | bid_head=max(self.ORDER_BOOK['bid'],key=lambda subItem:float(subItem)) 148 | bid_head_volume=self.ORDER_BOOK['bid'][bid_head] 149 | bid_head_all=bid_head+':'+str(bid_head_volume) 150 | if ask_head_all != self.ask_head_all or bid_head_all != self.bid_head_all: 151 | self.ask_head_all=ask_head_all 152 | self.bid_head_all=bid_head_all 153 | await trade_handler() 154 | except Exception as le: 155 | logger.error('OKEX BOOK connect:{}'.format(le)) 156 | self.ORDER_BOOK={} 157 | self.ask_head_all=None 158 | self.bid_head_all=None 159 | async def ticker(self,trade_handler): 160 | channel='ok_sub_spot_'+self.CURRENT_PAIR+'_ticker' 161 | while True: 162 | try: 163 | logger.info('OKEX BOOK start to connect') 164 | async with websockets.connect('wss://real.okex.com:10441/websocket') as websocket: 165 | self.websocket = websocket 166 | logger.info('OKEX enter communication') 167 | param={'event':'addChannel','channel':channel} 168 | await websocket.send(json.dumps(param)) 169 | while True: 170 | message = await websocket.recv() 171 | res=json.loads(message) 172 | if type(res) is list and res[0]['channel'].startswith('ok'): 173 | ask1=float(res[0]['data']['sell']) 174 | bid1=float(res[0]['data']['buy']) 175 | last=float(res[0]['data']['last']) 176 | self.ticker_value=(ask1,bid1,last) 177 | await trade_handler() 178 | except Exception as le: 179 | self.ticker_value=None 180 | logger.error('OKEX BOOK connect:{}'.format(le)) 181 | 182 | def get_orderbook_head(self): 183 | if self.ask_head_all is None or self.bid_head_all is None: 184 | raise Exception(self.name,'Error in get_orderbook_head') 185 | else: 186 | ask_heads=self.ask_head_all.split(':') 187 | bid_heads=self.bid_head_all.split(':') 188 | return (float(ask_heads[0]),float(ask_heads[1]),float(bid_heads[0]),float(bid_heads[1])) 189 | def get_sell_info(self,rate): 190 | if len(self.WALLET)<=0: 191 | raise Exception(self.name,'Error in get_sell_info') 192 | else: 193 | avaliable_amount=self.WALLET[self.CURRENCY[0]]['free'] 194 | cost=self.TAKER_FEE*rate 195 | return(avaliable_amount,cost) 196 | def get_buy_info(self,rate): 197 | if len(self.WALLET)<=0: 198 | raise Exception(self.name,'Error in get_buy_info') 199 | else: 200 | avaliable_amount=self.WALLET[self.CURRENCY[1]]['free']/rate/(1+self.BUY_PATCH) 201 | cost=self.TAKER_FEE*rate*(1+self.BUY_PATCH) 202 | return(avaliable_amount,cost) 203 | async def health_check(self): 204 | while True: 205 | await asyncio.sleep(30) 206 | if self.websocket is not None: 207 | param={'event':'ping'} 208 | await self.websocket.send(json.dumps(param)) 209 | logger.info('Finish send ping message') 210 | async def refresh_wallet(self): 211 | while True: 212 | await self.init_wallet() 213 | await asyncio.sleep(300) 214 | async def unfinish_order_handler(self): 215 | res = await self.unfinish_order() 216 | # if res is not None and len(res)>0: 217 | # for item in res: 218 | # head_res=self.get_orderbook_head() 219 | # if head_res is not None and item['type']=='sell' and head_res[2]-item['price']*1.001>0: 220 | # cancel_res= await self.cancel_order(item['order_id'],item['symbol']) 221 | # if cancel_res is not None and cancel_res['result']==True: 222 | # await self.sell(head_res[2],item['amount']) 223 | 224 | # if head_res is not None and item['type']=='buy' and item['price']*0.-head_res[0]*1.001>0: 225 | # cancel_res= await self.cancel_order(item['order_id'],item['symbol']) 226 | # if cancel_res is not None and cancel_res['result']==True: 227 | # await self.buy(head_res[0],item['amount']) 228 | async def test(ask1,bid1): 229 | pass 230 | 231 | def main(argv=None): 232 | parser = OptionParser() 233 | parser.add_option("-m", "--mode", dest="mode", help="0-wallet,1-buy,2-sell") 234 | parser.add_option("-r", "--rate", dest="rate", help="rate") 235 | parser.add_option("-a", "--amount", dest="amount", help="amount") 236 | parser.set_defaults(mode=1) 237 | util=okexUtil('ETC_OKB') 238 | loop=asyncio.get_event_loop() 239 | 240 | 241 | if 'okex_access_key' not in os.environ: 242 | return 243 | util.access_key=os.environ['okex_access_key'] 244 | util.secret_key=os.environ['okex_secret_key'] 245 | (opts, args) = parser.parse_args(argv) 246 | 247 | # if int(opts.mode) == 0: 248 | # loop.run_until_complete(util.unfinish_order()) 249 | # if int(opts.mode) ==1: 250 | # loop.run_until_complete(util.order_book(test)) 251 | 252 | loop.run_until_complete(util.init_wallet()) 253 | 254 | if __name__ == "__main__": 255 | sys.exit(main()) 256 | -------------------------------------------------------------------------------- /exchange/poloniex.py: -------------------------------------------------------------------------------- 1 | 2 | import requests 3 | import json 4 | import hashlib 5 | import hmac 6 | import asyncio 7 | import websockets 8 | import sys 9 | from optparse import OptionParser 10 | from time import time 11 | try: 12 | #python2 13 | from urllib import urlencode 14 | except ImportError: 15 | #python3 16 | from urllib.parse import urlencode 17 | import logging 18 | logger = logging.getLogger("deal") 19 | BOOK_LIMIT=10 20 | class poloniexUtil: 21 | def __init__(self,pair): 22 | self.name='poloniex' 23 | self.PAIR_MAP={'BTC_ETH':'BTC_ETH','BTC_LTC':'BTC_LTC','BTC_USDT':'USDT_BTC','ETC_USDT':'USDT_ETC','LTC_USDT':'USDT_LTC'} 24 | self.CURRENT_PAIR=self.PAIR_MAP[pair] 25 | self.CURRENCY=pair.split('_') 26 | self.WALLET={} 27 | self.ORDER_BOOK={} 28 | self.TAKER_FEE=0.0025 29 | # 补偿,买一个币,只能得到(1-self.TAKER_FEE)个币,为了保证两边币的数量一致,增加一个补偿量 30 | self.BUY_PATCH=(1+self.TAKER_FEE)*self.TAKER_FEE 31 | self.ask_head_all=None 32 | self.bid_head_all=None 33 | self.ticker_value=None 34 | self.NEED_RECONNECT=False 35 | access_key=None 36 | secret_key=None 37 | 38 | 39 | 40 | 41 | 42 | def handleRequest(self,command,params={}): 43 | try: 44 | 45 | payload = { 46 | 'command': command, 47 | 'nonce': int(time() * 1000), 48 | } 49 | for key in params.keys(): 50 | payload[key]=params[key] 51 | paybytes = urlencode(payload).encode('utf8') 52 | sign = hmac.new(self.secret_key.encode(), paybytes, hashlib.sha512).hexdigest() 53 | headers = {'Key': self.access_key.encode(),'Sign': sign,'Content-Type':'application/x-www-form-urlencoded'} 54 | url = 'https://poloniex.com/tradingApi' 55 | r = requests.post(url, headers=headers, data=paybytes) 56 | return json.loads(r.text) 57 | except Exception as e: 58 | raise Exception(self.name,'Error in handleRequest:{},{}'.format(command,params)) 59 | 60 | async def buy(self,rate,amount): 61 | patch_amount=amount*(1+self.BUY_PATCH) 62 | self.WALLET[self.CURRENCY[1]]['free']-=patch_amount*rate 63 | self.WALLET[self.CURRENCY[1]]['locked']+=patch_amount*rate 64 | params={'currencyPair':self.CURRENT_PAIR,'rate':rate,'amount':patch_amount} 65 | loop=asyncio.get_event_loop() 66 | res = await loop.run_in_executor(None, self.handleRequest,'buy',params) 67 | logger.debug('[poloniex] buy request {}|{}|{}.get result:{}'.format(self.CURRENT_PAIR,rate,patch_amount,res)) 68 | if res is not None: 69 | return res['orderNumber'] 70 | else: 71 | raise Exception(self.name,'Error in buy:{}|{}'.format(rate,amount)) 72 | async def sell(self,rate,amount): 73 | self.WALLET[self.CURRENCY[0]]['free']-=amount 74 | self.WALLET[self.CURRENCY[0]]['locked']+=amount 75 | params={'currencyPair':self.CURRENT_PAIR,'rate':rate,'amount':amount} 76 | loop=asyncio.get_event_loop() 77 | res = await loop.run_in_executor(None, self.handleRequest,'sell',params) 78 | logger.info('[poloniex] sell request {}|{}|{}.get result:{}'.format(self.CURRENT_PAIR,rate,amount,res)) 79 | if res is not None: 80 | return res['orderNumber'] 81 | else: 82 | raise Exception(self.name,'Error in sell:{}|{}'.format(rate,amount)) 83 | 84 | async def unfinish_order(self,pair): 85 | params={'currencyPair':self.CURRENT_PAIR} 86 | loop=asyncio.get_event_loop() 87 | res = await loop.run_in_executor(None, self.handleRequest,'returnOpenOrders',params) 88 | if res is not None: 89 | logger.debug('[poloniex] unfinish_order :{}.get result:{}'.format(pair,res)) 90 | return res 91 | else: 92 | raise Exception(self.name,'Error in unfinish_order') 93 | async def move_order(self,orderId,rate): 94 | params={'orderNumber':orderId} 95 | loop=asyncio.get_event_loop() 96 | res = await loop.run_in_executor(None, self.handleRequest,'moveOrder',params) 97 | if res is not None: 98 | logger.debug('[poloniex] move order:{}|{}.get result:{}'.format(orderId,rate,res)) 99 | return res 100 | else: 101 | raise Exception(self.name,'Error in move_order:{}|{}'.format(orderId,rate)) 102 | def cancel_order(self,orderId): 103 | params={'orderNumber':orderId} 104 | res=self.handleRequest('cancelOrder',params) 105 | if res is not None: 106 | return res 107 | else: 108 | raise Exception(self.name,'Error in cancel_order:{orderId}'.format(orderId)) 109 | 110 | async def init_wallet(self): 111 | loop=asyncio.get_event_loop() 112 | res = await loop.run_in_executor(None, self.handleRequest,'returnCompleteBalances',{}) 113 | self.WALLET={} 114 | if res is not None: 115 | self.WALLET[self.CURRENCY[0]]={'free':float(res[self.CURRENCY[0]]['available']),'locked':float(res[self.CURRENCY[0]]['onOrders'])} 116 | self.WALLET[self.CURRENCY[1]]={'free':float(res[self.CURRENCY[1]]['available']),'locked':float(res[self.CURRENCY[1]]['onOrders'])} 117 | logger.info('Finish load poloniex wallet:{}'.format(self.WALLET)) 118 | else: 119 | raise Exception(self.name,'Error in init_wallet') 120 | 121 | async def order_book(self,trade_handler): 122 | while True: 123 | try: 124 | async with websockets.connect('wss://api2.poloniex.com/') as websocket: 125 | param={'command':'subscribe','channel':self.CURRENT_PAIR} 126 | await websocket.send(json.dumps(param)) 127 | self.NEED_RECONNECT=False 128 | while not self.NEED_RECONNECT:# 为True的时候会导致结束循环,重新连接 129 | message = await websocket.recv() 130 | res=json.loads(message) 131 | if len(res)<2: 132 | continue 133 | for item in res[2]: 134 | if item[0] == 'i': 135 | book_size=0 136 | ask_map={} 137 | for key in sorted(item[1]['orderBook'][0],key=lambda subItem:float(subItem))[:BOOK_LIMIT]: 138 | ask_map[key]=float(item[1]['orderBook'][0][key]) 139 | self.ORDER_BOOK['ask']=ask_map 140 | bid_map={} 141 | for key in sorted(item[1]['orderBook'][1],key=lambda subItem:float(subItem),reverse=True)[:BOOK_LIMIT]: 142 | bid_map[key]=float(item[1]['orderBook'][1][key]) 143 | self.ORDER_BOOK['bid']=bid_map 144 | elif item[0] == 'o': 145 | # ['o', 1, '26.54474428', '0.00000000'] 146 | if item[1] == 0:#ask 147 | if float(item[3])==0 and item[2] in self.ORDER_BOOK['ask']: 148 | del self.ORDER_BOOK['ask'][item[2]] 149 | elif float(item[3])>0: 150 | self.ORDER_BOOK['ask'][item[2]]=float(item[3]) 151 | elif item[1] == 1:#bid 152 | if float(item[3])==0 and item[2] in self.ORDER_BOOK['bid']: 153 | del self.ORDER_BOOK['bid'][item[2]] 154 | elif float(item[3])>0: 155 | self.ORDER_BOOK['bid'][item[2]]=float(item[3]) 156 | ask_head=min(self.ORDER_BOOK['ask'],key=lambda subItem:float(subItem)) 157 | ask_head_volume=self.ORDER_BOOK['ask'][ask_head] 158 | bid_head=max(self.ORDER_BOOK['bid'],key=lambda subItem:float(subItem)) 159 | bid_head_volume=self.ORDER_BOOK['bid'][bid_head] 160 | ask_head_all=ask_head+':'+str(ask_head_volume) 161 | bid_head_all=bid_head+':'+str(bid_head_volume) 162 | if ask_head_all != self.ask_head_all or bid_head_all != self.bid_head_all: 163 | self.ask_head_all=ask_head_all 164 | self.bid_head_all=bid_head_all 165 | await trade_handler() 166 | except Exception as e: 167 | self.ORDER_BOOK={} 168 | self.ask_head_all=None 169 | self.bid_head_all=None 170 | logger.error('poloniex BOOK connect:{}'.format(e)) 171 | async def ticker(self,trade_handler): 172 | while True: 173 | try: 174 | async with websockets.connect('wss://api2.poloniex.com/') as websocket: 175 | param={'command':'subscribe','channel':1002} 176 | await websocket.send(json.dumps(param)) 177 | while True: 178 | message = await websocket.recv() 179 | res=json.loads(message) 180 | if len(res)<3: 181 | continue 182 | if res[2][0] == 173: 183 | last=float(res[2][1]) 184 | ask1=float(res[2][2]) 185 | bid1=float(res[2][3]) 186 | self.ticker_value=(ask1,bid1,last) 187 | await trade_handler() 188 | except Exception as e: 189 | self.ticker_value=None 190 | logger.error('poloniex ticker connect:{}'.format(e)) 191 | def get_orderbook_head(self): 192 | if self.ask_head_all is None or self.bid_head_all is None: 193 | raise Exception(self.name,'Error in get_orderbook_head') 194 | else: 195 | ask_heads=self.ask_head_all.split(':') 196 | bid_heads=self.bid_head_all.split(':') 197 | return (float(ask_heads[0]),float(ask_heads[1]),float(bid_heads[0]),float(bid_heads[1])) 198 | 199 | def get_sell_info(self,rate): 200 | if len(self.WALLET)<=0: 201 | raise Exception(self.name,'Error in get_sell_info') 202 | else: 203 | avaliable_amount=self.WALLET[self.CURRENCY[0]]['free'] 204 | cost=self.TAKER_FEE*rate 205 | return(avaliable_amount,cost) 206 | def get_buy_info(self,rate): 207 | if len(self.WALLET)<=0: 208 | raise Exception(self.name,'Error in get_buy_info') 209 | else: 210 | avaliable_amount=self.WALLET[self.CURRENCY[1]]['free']/rate/(1+self.BUY_PATCH) 211 | cost=self.TAKER_FEE*rate*(1+self.BUY_PATCH) 212 | return(avaliable_amount,cost) 213 | 214 | async def unfinish_order_handler(self): 215 | res = await self.unfinish_order(self.CURRENT_PAIR) 216 | # head=self.get_orderbook_head() 217 | # if res is not None and head is not None: 218 | # lst=[] 219 | # for item in res: 220 | # if item['type']=='sell' and float(item['rate'])head[0]: 223 | # lst.append(self.move_order(item['orderNumber'],head[0])) 224 | # if len(lst)>0: 225 | # await asyncio.wait(lst,return_when=asyncio.FIRST_COMPLETED,) 226 | async def health_check(self): 227 | poloniex_ask_head_all='begin' 228 | poloniex_bid_head_all='begin' 229 | while True: 230 | await asyncio.sleep(300) 231 | if poloniex_bid_head_all == self.bid_head_all or poloniex_ask_head_all== self.ask_head_all: 232 | logger.error("poloniex order head update die !!!") 233 | self.NEED_RECONNECT=True 234 | else: 235 | poloniex_bid_head_all = self.bid_head_all 236 | poloniex_ask_head_all = self.ask_head_all 237 | 238 | async def test(ask1,bid1,last): 239 | print('test:{},{},{}'.format(ask1,bid1,last)) 240 | def main(argv=None): 241 | parser = OptionParser() 242 | parser.add_option("-m", "--mode", dest="mode", help="0-wallet,1-buy,2-sell") 243 | parser.add_option("-r", "--rate", dest="rate", help="rate") 244 | parser.add_option("-a", "--amount", dest="amount", help="amount") 245 | parser.add_option("-p", "--pair", dest="pair", help="pair") 246 | parser.set_defaults(mode=0,pair='ETC_USDT') 247 | 248 | util = poloniexUtil('ETC_USDT') 249 | 250 | # if 'bitfinex_access_key' not in os.environ: 251 | # return 252 | # util.access_key=os.environ['bitfinex_access_key'] 253 | # util.secret_key=os.environ['bitfinex_secret_key'] 254 | (opts, args) = parser.parse_args(argv) 255 | loop=asyncio.get_event_loop() 256 | loop.run_until_complete(util.ticker(test)) 257 | if __name__ == "__main__": 258 | sys.exit(main()) 259 | -------------------------------------------------------------------------------- /exchange/huobi.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import urllib 3 | import urllib.parse 4 | import urllib.request 5 | import requests 6 | import json 7 | import hashlib 8 | import hmac 9 | import asyncio 10 | import websockets 11 | from time import time 12 | import gzip 13 | import sys 14 | import os 15 | import datetime 16 | from optparse import OptionParser 17 | import math 18 | try: 19 | #python2 20 | from urllib import urlencode 21 | except ImportError: 22 | #python3 23 | from urllib.parse import urlencode 24 | import logging 25 | logger = logging.getLogger("deal") 26 | def http_get_request(url, params, add_to_headers=None): 27 | 28 | headers = { 29 | "Content-type": "application/x-www-form-urlencoded", 30 | 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36', 31 | } 32 | if add_to_headers: 33 | headers.update(add_to_headers) 34 | postdata = urllib.parse.urlencode(params) 35 | response = requests.get(url, postdata, headers=headers, timeout=10) 36 | try: 37 | 38 | if response.status_code == 200: 39 | return response.json() 40 | else: 41 | return 42 | except BaseException as e: 43 | print("httpGet failed, detail is:%s,%s" %(response.text,e)) 44 | return 45 | 46 | 47 | def http_post_request(url, params, add_to_headers=None): 48 | headers = { 49 | "Accept": "application/json", 50 | 'Content-Type': 'application/json' 51 | } 52 | if add_to_headers: 53 | headers.update(add_to_headers) 54 | postdata = json.dumps(params) 55 | response = requests.post(url, postdata, headers=headers, timeout=10) 56 | try: 57 | if response.status_code == 200: 58 | return response.json() 59 | else: 60 | return 61 | except BaseException as e: 62 | print("httpPost failed, detail is:%s,%s" %(response.text,e)) 63 | return 64 | 65 | def createSign(pParams, method, host_url, request_path, secret_key): 66 | sorted_params = sorted(pParams.items(), key=lambda d: d[0], reverse=False) 67 | encode_params = urllib.parse.urlencode(sorted_params) 68 | payload = [method, host_url, request_path, encode_params] 69 | payload = '\n'.join(payload) 70 | payload = payload.encode(encoding='UTF8') 71 | secret_key = secret_key.encode(encoding='UTF8') 72 | 73 | digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest() 74 | signature = base64.b64encode(digest) 75 | signature = signature.decode() 76 | return signature 77 | 78 | 79 | 80 | 81 | 82 | class huobiUtil: 83 | def __init__(self,pair): 84 | self.name='huobi' 85 | self.PAIR_MAP={'BTC_ETH':'ethbtc','BTC_LTC':'ltcbtc','BTC_USDT':'btcusdt','ETC_USDT':'etcusdt'} 86 | self.CURRENT_PAIR=self.PAIR_MAP[pair] 87 | self.CURRENCY=[self.CURRENT_PAIR[:3],self.CURRENT_PAIR[3:]] 88 | self.WALLET={} 89 | self.ORDER_BOOK={} 90 | self.TAKER_FEE=0.002 91 | self.BUY_PATCH=(1+self.TAKER_FEE)*self.TAKER_FEE 92 | self.ask_head_all=None 93 | self.bid_head_all=None 94 | self.ticker_value=None 95 | self.account_id=0 96 | self.NEED_RECONNECT=False 97 | access_key=None 98 | secret_key=None 99 | def api_key_get(self,params, request_path): 100 | method = 'GET' 101 | timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') 102 | params.update({'AccessKeyId': self.access_key, 103 | 'SignatureMethod': 'HmacSHA256', 104 | 'SignatureVersion': '2', 105 | 'Timestamp': timestamp}) 106 | 107 | host_url = 'https://api.huobi.pro' 108 | host_name = urllib.parse.urlparse(host_url).hostname 109 | host_name = host_name.lower() 110 | params['Signature'] = createSign(params, method, host_name, request_path, self.secret_key) 111 | 112 | url = host_url + request_path 113 | return http_get_request(url, params) 114 | 115 | 116 | def api_key_post(self,params, request_path): 117 | method = 'POST' 118 | timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') 119 | params_to_sign = {'AccessKeyId': self.access_key, 120 | 'SignatureMethod': 'HmacSHA256', 121 | 'SignatureVersion': '2', 122 | 'Timestamp': timestamp} 123 | 124 | host_url = 'https://api.huobi.pro' 125 | host_name = urllib.parse.urlparse(host_url).hostname 126 | host_name = host_name.lower() 127 | params_to_sign['Signature'] = createSign(params_to_sign, method, host_name, request_path, self.secret_key) 128 | url = host_url + request_path + '?' + urllib.parse.urlencode(params_to_sign) 129 | return http_post_request(url, params) 130 | async def order_book(self,trade_handler): 131 | while True: 132 | async with websockets.connect('wss://api.huobi.pro/ws') as websocket: 133 | try: 134 | param={'sub':'market.'+self.CURRENT_PAIR+'.depth.step0','id':'5201314'} 135 | await websocket.send(json.dumps(param)) 136 | self.NEED_RECONNECT=False 137 | while not self.NEED_RECONNECT: 138 | msg=await websocket.recv() 139 | message = json.loads(gzip.decompress(msg).decode("utf-8")) 140 | if 'ping' in message: 141 | await websocket.send(json.dumps({'pong':message['ping']})) 142 | elif 'tick' in message: 143 | ask_head_all=str(message['tick']['asks'][0][0])+':'+str(message['tick']['asks'][0][1]) 144 | bid_head_all=str(message['tick']['bids'][0][0])+':'+str(message['tick']['bids'][0][1]) 145 | if ask_head_all != self.ask_head_all or bid_head_all != self.bid_head_all: 146 | self.ask_head_all=ask_head_all 147 | self.bid_head_all=bid_head_all 148 | await trade_handler() 149 | except Exception as e: 150 | self.ORDER_BOOK={} 151 | self.ask_head_all=None 152 | self.bid_head_all=None 153 | logger.error('ERROR happen in {} connection:{}'.format(huobiUtil.__name__,e)) 154 | websocket.close() 155 | async def ticker(self,trade_handler): 156 | while True: 157 | try: 158 | async with websockets.connect('wss://api.huobi.pro/ws') as websocket: 159 | param={'sub':'market.'+self.CURRENT_PAIR+'.depth.step0','id':'5201314'} 160 | await websocket.send(json.dumps(param)) 161 | while True: 162 | msg=await websocket.recv() 163 | message = json.loads(gzip.decompress(msg).decode("utf-8")) 164 | if 'ping' in message: 165 | await websocket.send(json.dumps({'pong':message['ping']})) 166 | elif 'tick' in message: 167 | ask_head_all=str(message['tick']['asks'][0][0])+':'+str(message['tick']['asks'][0][1]) 168 | bid_head_all=str(message['tick']['bids'][0][0])+':'+str(message['tick']['bids'][0][1]) 169 | 170 | self.ticker_value=(message['tick']['asks'][0][0],message['tick']['bids'][0][0],None) 171 | await trade_handler() 172 | else: 173 | print(message) 174 | except Exception as e: 175 | self.ticker_value=None 176 | logger.error('ERROR happen in {} connection:{}'.format(huobiUtil.__name__,e)) 177 | websocket.close() 178 | 179 | def get_orderbook_head(self): 180 | if self.ask_head_all is None or self.bid_head_all is None: 181 | raise Exception(self.name,'Error in get_orderbook_head') 182 | else: 183 | ask_heads=self.ask_head_all.split(':') 184 | bid_heads=self.bid_head_all.split(':') 185 | return (float(ask_heads[0]),float(ask_heads[1]),float(bid_heads[0]),float(bid_heads[1])) 186 | def get_sell_info(self,rate): 187 | if len(self.WALLET)<=0: 188 | raise Exception(self.name,'Error in get_sell_info') 189 | else: 190 | avaliable_amount=self.WALLET[self.CURRENCY[0]]['free'] 191 | cost=self.TAKER_FEE*rate 192 | return(avaliable_amount,cost) 193 | def get_buy_info(self,rate): 194 | if len(self.WALLET)<=0: 195 | raise Exception(self.name,'Error in get_buy_info') 196 | else: 197 | avaliable_amount=self.WALLET[self.CURRENCY[1]]['free']/rate/(1+self.BUY_PATCH) 198 | cost=self.TAKER_FEE*rate*(1+self.BUY_PATCH) 199 | return(avaliable_amount,cost) 200 | 201 | async def buy(self,rate,amount,is_market=False): 202 | patch_amount=math.ceil(amount*(1+self.BUY_PATCH)*10000)/10000 203 | rate=math.ceil(rate*100)/100 204 | self.WALLET[self.CURRENCY[1]]['free']-=patch_amount*rate 205 | self.WALLET[self.CURRENCY[1]]['locked']+=patch_amount*rate 206 | if self.account_id==0: 207 | await self.get_account() 208 | params={} 209 | if is_market: 210 | params={"account-id": self.account_id,"amount": patch_amount, "symbol": self.CURRENT_PAIR,"type": 'buy-market'} 211 | else: 212 | params={"account-id": self.account_id,"amount": patch_amount, "symbol": self.CURRENT_PAIR,"type": 'buy-limit',"price":rate} 213 | loop=asyncio.get_event_loop() 214 | res = await loop.run_in_executor(None,self.api_key_post,params,'/v1/order/orders/place') 215 | logger.debug('[huobi] buy request {}|{}|{}.get result:{}'.format(self.CURRENT_PAIR,rate,patch_amount,res)) 216 | return res['data'] 217 | 218 | 219 | 220 | async def sell(self,rate,amount,is_market=False): 221 | amount=math.ceil(amount*(1+self.BUY_PATCH)*10000)/10000 222 | rate=math.ceil(rate*100)/100 223 | if self.WALLET is not None: 224 | self.WALLET[self.CURRENCY[0]]['free']-=amount 225 | self.WALLET[self.CURRENCY[0]]['locked']+=amount 226 | if self.account_id==0: 227 | await self.get_account() 228 | params={} 229 | if is_market: 230 | params={"account-id": self.account_id,"amount": amount, "symbol": self.CURRENT_PAIR,"type": 'sell-market'} 231 | else: 232 | params={"account-id": self.account_id,"amount": amount, "symbol": self.CURRENT_PAIR,"type": 'sell-limit',"price":rate} 233 | loop=asyncio.get_event_loop() 234 | res = await loop.run_in_executor(None,self.api_key_post,params,'/v1/order/orders/place') 235 | logger.debug('[huobi] sell request {}|{}|{}get result:{}'.format(self.CURRENT_PAIR,rate,amount,res)) 236 | return res['data'] 237 | async def unfinish_order(self): 238 | raise Exception(self.name,'not implemented') 239 | 240 | async def cancel_order(self,orderId): 241 | # if self.account_id==0: 242 | # await self.get_account() 243 | loop=asyncio.get_event_loop() 244 | 245 | res = await loop.run_in_executor(None, self.api_key_post,{},"/v1/order/orders/{0}/submitcancel".format(orderId)) 246 | if res is not None and res['status']=='ok': 247 | return res 248 | else: 249 | raise Exception(self.name,'Error happen in cancel order {}'.format(orderId)) 250 | 251 | async def get_account(self): 252 | loop=asyncio.get_event_loop() 253 | res = await loop.run_in_executor(None, self.api_key_get,{},'/v1/account/accounts') 254 | self.account_id=res['data'][0]['id'] 255 | 256 | 257 | async def init_wallet(self): 258 | if self.account_id==0: 259 | await self.get_account() 260 | loop=asyncio.get_event_loop() 261 | res = await loop.run_in_executor(None, self.api_key_get,{},'/v1/account/accounts/{0}/balance'.format(self.account_id)) 262 | self.WALLET={} 263 | if res is not None and res['status']=='ok': 264 | for item in res['data']['list']: 265 | for cur in self.CURRENCY: 266 | if cur not in self.WALLET: 267 | self.WALLET[cur]={} 268 | if item['currency']== cur: 269 | if item['type']=='trade': 270 | self.WALLET[cur]['free']=float(item['balance']) 271 | elif item['type'] == 'frozen': 272 | self.WALLET[cur]['locked']=float(item['balance']) 273 | logger.info('Finish load huobi wallet:{}'.format(self.WALLET)) 274 | else: 275 | raise Exception(self.name,'Error in init_wallet') 276 | async def health_check(self): 277 | huobi_ask_head_all='begin' 278 | huobi_bid_head_all='begin' 279 | while True: 280 | await asyncio.sleep(300) 281 | if huobi_bid_head_all == self.bid_head_all or huobi_ask_head_all== self.ask_head_all: 282 | logger.error("huobi order head update die !!!") 283 | self.NEED_RECONNECT=True 284 | else: 285 | huobi_bid_head_all = self.bid_head_all 286 | huobi_ask_head_all = self.ask_head_all 287 | # 288 | async def test(): 289 | print('nothing here:{}'.format('larla')) 290 | def main(argv=None): 291 | parser = OptionParser() 292 | parser.add_option("-p", "--pair", dest="pair", help="pair") 293 | parser.add_option("-m", "--mode", dest="mode", help="0-ticker,1-buy,2-sell,3-wallet") 294 | parser.set_defaults(pair='ETC_USDT',mode=0) 295 | 296 | util = huobiUtil('ETC_USDT') 297 | util.access_key=os.environ['huobi_access_key'] 298 | util.secret_key=os.environ['huobi_secret_key'] 299 | (opts, args) = parser.parse_args(argv) 300 | loop=asyncio.get_event_loop() 301 | if int(opts.mode)==0: 302 | loop.run_until_complete(util.order_book(test)) 303 | if int(opts.mode)==1: 304 | loop.run_until_complete(util.buy(1,0.1)) 305 | if int(opts.mode)==2: 306 | loop.run_until_complete(util.sell(100,0.1)) 307 | elif int(opts.mode)==3: 308 | loop.run_until_complete(util.init_wallet()) 309 | elif int(opts.mode)==4: 310 | loop.run_until_complete(util.unfinish_order()) 311 | elif int(opts.mode)==5: 312 | loop.run_until_complete(util.cancel_order('2631428446')) 313 | if __name__ == "__main__": 314 | sys.exit(main()) 315 | 316 | --------------------------------------------------------------------------------