diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aea22fc
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/hb.iml b/.idea/hb.iml
new file mode 100644
index 0000000..6711606
--- /dev/null
+++ b/.idea/hb.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..a5f3f98
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api/HuobiServices.py b/api/HuobiServices.py
new file mode 100644
index 0000000..5839141
--- /dev/null
+++ b/api/HuobiServices.py
@@ -0,0 +1,425 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @Date : 2017-12-20 15:40:03
+# @Author : KlausQiu
+# @QQ : 375235513
+# @github : https://github.com/KlausQIU
+
+from api.Utils import *
+
+'''
+Market data API
+'''
+
+
+# 获取KLine
+def get_kline(symbol, period, size=150):
+ """
+ :param symbol
+ :param period: 可选值:{1min, 5min, 15min, 30min, 60min, 1day, 1mon, 1week, 1year }
+ :param size: 可选值: [1,2000]
+ :return:
+ """
+ params = {'symbol': symbol,
+ 'period': period,
+ 'size': size}
+
+ url = MARKET_URL + '/market/history/kline'
+ return http_get_request(url, params)
+
+
+# 获取marketdepth
+def get_depth(symbol, type):
+ """
+ :param symbol
+ :param type: 可选值:{ percent10, step0, step1, step2, step3, step4, step5 }
+ :return:
+ """
+ params = {'symbol': symbol,
+ 'type': type}
+
+ url = MARKET_URL + '/market/depth'
+ return http_get_request(url, params)
+
+
+# 获取tradedetail
+def get_trade(symbol):
+ """
+ :param symbol
+ :return:
+ """
+ params = {'symbol': symbol}
+
+ url = MARKET_URL + '/market/trade'
+ return http_get_request(url, params)
+
+
+# 获取merge ticker
+def get_ticker(symbol):
+ """
+ :param symbol:
+ :return:
+ """
+ params = {'symbol': symbol}
+
+ url = MARKET_URL + '/market/detail/merged'
+ return http_get_request(url, params)
+
+
+# 获取 Market Detail 24小时成交量数据
+def get_detail(symbol):
+ """
+ :param symbol
+ :return:
+ """
+ params = {'symbol': symbol}
+
+ url = MARKET_URL + '/market/detail'
+ return http_get_request(url, params)
+
+# 获取 支持的交易对
+def get_symbols(long_polling=None):
+ """
+
+ """
+ params = {}
+ if long_polling:
+ params['long-polling'] = long_polling
+ path = '/v1/common/symbols'
+ return api_key_get(params, path)
+
+'''
+Trade/Account API
+'''
+
+
+def get_accounts():
+ """
+ :return:
+ """
+ path = "/v1/account/accounts"
+ params = {}
+ return api_key_get(params, path)
+
+
+# 获取当前账户资产
+def get_balance(acct_id=None):
+ """
+ :param acct_id
+ :return:
+ """
+
+ if not acct_id:
+ accounts = get_accounts()
+ acct_id = accounts['data'][0]['id'];
+
+ url = "/v1/account/accounts/{0}/balance".format(acct_id)
+ params = {"account-id": acct_id}
+ return api_key_get(params, url)
+
+
+# 下单
+
+# 创建并执行订单
+def send_order(amount, source, symbol, _type, price=0):
+ """
+ :param amount:
+ :param source: 如果使用借贷资产交易,请在下单接口,请求参数source中填写'margin-api'
+ :param symbol:
+ :param _type: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
+ :param price:
+ :return:
+ """
+ try:
+ accounts = get_accounts()
+ acct_id = accounts['data'][0]['id']
+ except BaseException as e:
+ print ('get acct_id error.%s' % e)
+ acct_id = ACCOUNT_ID
+
+ params = {"account-id": acct_id,
+ "amount": amount,
+ "symbol": symbol,
+ "type": _type,
+ "source": source}
+ if price:
+ params["price"] = price
+
+ url = '/v1/order/orders/place'
+ return api_key_post(params, url)
+
+
+# 撤销订单
+def cancel_order(order_id):
+ """
+
+ :param order_id:
+ :return:
+ """
+ params = {}
+ url = "/v1/order/orders/{0}/submitcancel".format(order_id)
+ return api_key_post(params, url)
+
+
+# 查询某个订单
+def order_info(order_id):
+ """
+
+ :param order_id:
+ :return:
+ """
+ params = {}
+ url = "/v1/order/orders/{0}".format(order_id)
+ return api_key_get(params, url)
+
+
+# 查询某个订单的成交明细
+def order_matchresults(order_id):
+ """
+
+ :param order_id:
+ :return:
+ """
+ params = {}
+ url = "/v1/order/orders/{0}/matchresults".format(order_id)
+ return api_key_get(params, url)
+
+
+# 查询当前委托、历史委托
+def orders_list(symbol, states, types=None, start_date=None, end_date=None, _from=None, direct=None, size=None):
+ """
+
+ :param symbol:
+ :param states: 可选值 {pre-submitted 准备提交, submitted 已提交, partial-filled 部分成交, partial-canceled 部分成交撤销, filled 完全成交, canceled 已撤销}
+ :param types: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
+ :param start_date:
+ :param end_date:
+ :param _from:
+ :param direct: 可选值{prev 向前,next 向后}
+ :param size:
+ :return:
+ """
+ params = {'symbol': symbol,
+ 'states': states}
+
+ if types:
+ params[types] = types
+ if start_date:
+ params['start-date'] = start_date
+ if end_date:
+ params['end-date'] = end_date
+ if _from:
+ params['from'] = _from
+ if direct:
+ params['direct'] = direct
+ if size:
+ params['size'] = size
+ url = '/v1/order/orders'
+ return api_key_get(params, url)
+
+
+# 查询当前成交、历史成交
+def orders_matchresults(symbol, types=None, start_date=None, end_date=None, _from=None, direct=None, size=None):
+ """
+
+ :param symbol:
+ :param types: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
+ :param start_date:
+ :param end_date:
+ :param _from:
+ :param direct: 可选值{prev 向前,next 向后}
+ :param size:
+ :return:
+ """
+ params = {'symbol': symbol}
+
+ if types:
+ params[types] = types
+ if start_date:
+ params['start-date'] = start_date
+ if end_date:
+ params['end-date'] = end_date
+ if _from:
+ params['from'] = _from
+ if direct:
+ params['direct'] = direct
+ if size:
+ params['size'] = size
+ url = '/v1/order/matchresults'
+ return api_key_get(params, url)
+
+
+
+# 申请提现虚拟币
+def withdraw(address, amount, currency, fee=0, addr_tag=""):
+ """
+
+ :param address_id:
+ :param amount:
+ :param currency:btc, ltc, bcc, eth, etc ...(火币Pro支持的币种)
+ :param fee:
+ :param addr-tag:
+ :return: {
+ "status": "ok",
+ "data": 700
+ }
+ """
+ params = {'address': address,
+ 'amount': amount,
+ "currency": currency,
+ "fee": fee,
+ "addr-tag": addr_tag}
+ url = '/v1/dw/withdraw/api/create'
+
+ return api_key_post(params, url)
+
+# 申请取消提现虚拟币
+def cancel_withdraw(address_id):
+ """
+
+ :param address_id:
+ :return: {
+ "status": "ok",
+ "data": 700
+ }
+ """
+ params = {}
+ url = '/v1/dw/withdraw-virtual/{0}/cancel'.format(address_id)
+
+ return api_key_post(params, url)
+
+
+'''
+借贷API
+'''
+
+# 创建并执行借贷订单
+
+
+def send_margin_order(amount, source, symbol, _type, price=0):
+ """
+ :param amount:
+ :param source: 'margin-api'
+ :param symbol:
+ :param _type: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
+ :param price:
+ :return:
+ """
+ try:
+ accounts = get_accounts()
+ acct_id = accounts['data'][0]['id']
+ except BaseException as e:
+ print ('get acct_id error.%s' % e)
+ acct_id = ACCOUNT_ID
+
+ params = {"account-id": acct_id,
+ "amount": amount,
+ "symbol": symbol,
+ "type": _type,
+ "source": 'margin-api'}
+ if price:
+ params["price"] = price
+
+ url = '/v1/order/orders/place'
+ return api_key_post(params, url)
+
+# 现货账户划入至借贷账户
+
+
+def exchange_to_margin(symbol, currency, amount):
+ """
+ :param amount:
+ :param currency:
+ :param symbol:
+ :return:
+ """
+ params = {"symbol": symbol,
+ "currency": currency,
+ "amount": amount}
+
+ url = "/v1/dw/transfer-in/margin"
+ return api_key_post(params, url)
+
+# 借贷账户划出至现货账户
+
+
+def margin_to_exchange(symbol, currency, amount):
+ """
+ :param amount:
+ :param currency:
+ :param symbol:
+ :return:
+ """
+ params = {"symbol": symbol,
+ "currency": currency,
+ "amount": amount}
+
+ url = "/v1/dw/transfer-out/margin"
+ return api_key_post(params, url)
+
+# 申请借贷
+def get_margin(symbol, currency, amount):
+ """
+ :param amount:
+ :param currency:
+ :param symbol:
+ :return:
+ """
+ params = {"symbol": symbol,
+ "currency": currency,
+ "amount": amount}
+ url = "/v1/margin/orders"
+ return api_key_post(params, url)
+
+# 归还借贷
+def repay_margin(order_id, amount):
+ """
+ :param order_id:
+ :param amount:
+ :return:
+ """
+ params = {"order-id": order_id,
+ "amount": amount}
+ url = "/v1/margin/orders/{0}/repay".format(order_id)
+ return api_key_post(params, url)
+
+# 借贷订单
+def loan_orders(symbol, currency, start_date="", end_date="", start="", direct="", size=""):
+ """
+ :param symbol:
+ :param currency:
+ :param direct: prev 向前,next 向后
+ :return:
+ """
+ params = {"symbol": symbol,
+ "currency": currency}
+ if start_date:
+ params["start-date"] = start_date
+ if end_date:
+ params["end-date"] = end_date
+ if start:
+ params["from"] = start
+ if direct and direct in ["prev", "next"]:
+ params["direct"] = direct
+ if size:
+ params["size"] = size
+ url = "/v1/margin/loan-orders"
+ return api_key_get(params, url)
+
+
+# 借贷账户详情,支持查询单个币种
+def margin_balance(symbol):
+ """
+ :param symbol:
+ :return:
+ """
+ params = {}
+ url = "/v1/margin/accounts/balance"
+ if symbol:
+ params['symbol'] = symbol
+
+ return api_key_get(params, url)
+
+
+if __name__ == '__main__':
+ print (get_symbols())
diff --git a/api/Utils.py b/api/Utils.py
new file mode 100644
index 0000000..91cfb88
--- /dev/null
+++ b/api/Utils.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @Date : 2017-12-20 15:40:03
+# @Author : KlausQiu
+# @QQ : 375235513
+# @github : https://github.com/KlausQIU
+
+import base64
+import datetime
+import hashlib
+import hmac
+import json
+import urllib
+import urllib.parse
+import urllib.request
+import requests
+
+# 此处填写APIKEY
+
+ACCESS_KEY = "2c862239-1f861cf9-9ce73af3-6c7ed"
+SECRET_KEY = "66ead5b3-261721f8-cc099e40-e97e4"
+
+
+
+# API 请求地址
+MARKET_URL = "https://api.huobi.pro"
+TRADE_URL = "https://api.huobi.pro"
+
+# 首次运行可通过get_accounts()获取acct_id,然后直接赋值,减少重复获取。
+ACCOUNT_ID = None
+
+#'Timestamp': '2017-06-02T06:13:49'
+
+def http_get_request(url, params, add_to_headers=None):
+ headers = {
+ "Content-type": "application/x-www-form-urlencoded",
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
+ }
+ if add_to_headers:
+ headers.update(add_to_headers)
+ postdata = urllib.parse.urlencode(params)
+ response = requests.get(url, postdata, headers=headers, timeout=5)
+ try:
+
+ if response.status_code == 200:
+ return response.json()
+ else:
+ return
+ except BaseException as e:
+ print("httpGet failed, detail is:%s,%s" %(response.text,e))
+ return
+
+
+def http_post_request(url, params, add_to_headers=None):
+ headers = {
+ "Accept": "application/json",
+ 'Content-Type': 'application/json'
+ }
+ if add_to_headers:
+ headers.update(add_to_headers)
+ postdata = json.dumps(params)
+ response = requests.post(url, postdata, headers=headers, timeout=10)
+ try:
+
+ if response.status_code == 200:
+ return response.json()
+ else:
+ return
+ except BaseException as e:
+ print("httpPost failed, detail is:%s,%s" %(response.text,e))
+ return
+
+
+def api_key_get(params, request_path):
+ method = 'GET'
+ timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
+ params.update({'AccessKeyId': ACCESS_KEY,
+ 'SignatureMethod': 'HmacSHA256',
+ 'SignatureVersion': '2',
+ 'Timestamp': timestamp})
+
+ host_url = TRADE_URL
+ host_name = urllib.parse.urlparse(host_url).hostname
+ host_name = host_name.lower()
+ params['Signature'] = createSign(params, method, host_name, request_path, SECRET_KEY)
+
+ url = host_url + request_path
+ return http_get_request(url, params)
+
+
+def api_key_post(params, request_path):
+ method = 'POST'
+ timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
+ params_to_sign = {'AccessKeyId': ACCESS_KEY,
+ 'SignatureMethod': 'HmacSHA256',
+ 'SignatureVersion': '2',
+ 'Timestamp': timestamp}
+
+ host_url = TRADE_URL
+ host_name = urllib.parse.urlparse(host_url).hostname
+ host_name = host_name.lower()
+ params_to_sign['Signature'] = createSign(params_to_sign, method, host_name, request_path, SECRET_KEY)
+ url = host_url + request_path + '?' + urllib.parse.urlencode(params_to_sign)
+ return http_post_request(url, params)
+
+
+def createSign(pParams, method, host_url, request_path, secret_key):
+ sorted_params = sorted(pParams.items(), key=lambda d: d[0], reverse=False)
+ encode_params = urllib.parse.urlencode(sorted_params)
+ payload = [method, host_url, request_path, encode_params]
+ payload = '\n'.join(payload)
+ payload = payload.encode(encoding='UTF8')
+ secret_key = secret_key.encode(encoding='UTF8')
+
+ digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest()
+ signature = base64.b64encode(digest)
+ signature = signature.decode()
+ return signature
+
diff --git a/api/__init__.py b/api/__init__.py
new file mode 100644
index 0000000..143f486
--- /dev/null
+++ b/api/__init__.py
@@ -0,0 +1 @@
+# __init__.py
diff --git a/application.py b/application.py
new file mode 100644
index 0000000..556b3bf
--- /dev/null
+++ b/application.py
@@ -0,0 +1,12 @@
+import time
+from task import *
+from api.HuobiServices import *
+
+kline = get_kline('eoseth', '60min', 2000)
+
+order_list = load_pending_order_list()
+print(order_list)
+
+s = datetime.datetime(2018, 2, 22, 13)
+t = time.mktime(s.timetuple())
+print(t)
\ No newline at end of file
diff --git a/log.txt b/log.txt
new file mode 100644
index 0000000..e264ac6
--- /dev/null
+++ b/log.txt
@@ -0,0 +1,10 @@
+2018-02-22 Thursday 16:46:53 run.py : INFO ʼ ...
+2018-02-22 Thursday 16:46:53 run.py : INFO д1ʱ - 1519289213.9993107
+2018-02-22 Thursday 16:46:54 connectionpool.py : DEBUG Starting new HTTPS connection (1): api.huobi.pro
+2018-02-22 Thursday 16:46:54 connectionpool.py : DEBUG https://api.huobi.pro:443 "GET /v1/account/accounts?AccessKeyId=2c862239-1f861cf9-9ce73af3-6c7ed&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2018-02-22T08%3A46%3A53&Signature=AsWEfJec2gQ4Ix2tbpvGWn1x0KEdPzl5Fl3kEQXpKc0%3D HTTP/1.1" 200 None
+2018-02-22 Thursday 16:46:54 connectionpool.py : DEBUG Starting new HTTPS connection (1): api.huobi.pro
+2018-02-22 Thursday 16:46:55 connectionpool.py : DEBUG https://api.huobi.pro:443 "POST /v1/order/orders/place?AccessKeyId=2c862239-1f861cf9-9ce73af3-6c7ed&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2018-02-22T08%3A46%3A54&Signature=aY4vnmVrDdc3EIWo%2FSncFbcYjQMxO32bUhc%2B5VoWGLE%3D HTTP/1.1" 200 None
+2018-02-22 Thursday 16:47:05 run.py : INFO д2ʱ - 1519289225.1821854
+2018-02-22 Thursday 16:47:15 run.py : INFO д3ʱ - 1519289235.1897564
+2018-02-22 Thursday 16:47:25 run.py : INFO д4ʱ - 1519289245.1974354
+2018-02-22 Thursday 16:47:35 run.py : INFO д5ʱ - 1519289255.2036781
diff --git a/run.py b/run.py
new file mode 100644
index 0000000..bdad70e
--- /dev/null
+++ b/run.py
@@ -0,0 +1,63 @@
+import sched
+import logging
+from task import *
+from api.HuobiServices import *
+
+
+def intitial_logging():
+ ''''' Output log to file and console '''
+ # Define a Handler and set a format which output to file
+ logging.basicConfig(
+ level=logging.DEBUG, # 定义输出到文件的log级别,大于此级别的都被输出
+ format='%(asctime)s %(filename)s : %(levelname)s %(message)s', # 定义输出log的格式
+ datefmt='%Y-%m-%d %A %H:%M:%S', # 时间
+ filename='log.txt', # log文件名
+ filemode='w') # 写入模式“w”或“a”
+ # Define a Handler and set a format which output to console
+ console = logging.StreamHandler() # 定义console handler
+ console.setLevel(logging.INFO) # 定义该handler级别
+ formatter = logging.Formatter('%(asctime)s %(filename)s : %(levelname)s %(message)s') # 定义该handler格式
+ console.setFormatter(formatter)
+ # Create an instance
+ logging.getLogger().addHandler(console) # 实例化添加handler
+
+
+if __name__ == "__main__":
+ intitial_logging()
+
+s = sched.scheduler(time.time, time.sleep)
+# 计数器,一个循环任务,总共让自己执行3次
+index = 0
+
+
+# 第二个工作函数,自调任务,自己开启定时并启动。
+def run_task():
+ # 只要没有让自己调用到第3次,那么继续重头开始执行本任务
+ # 这里的delay 可以重新指定
+ global index
+ index = index + 1
+ msg = '运行次数({0})运行时间 - {1}'.format(index, time.time())
+ logging.info(msg)
+
+ order_list = load_pending_order_list()
+ for order in order_list:
+ # 未执行已经并且已经到达执行时间
+ if order.status == OrderStatus.pending.value and order.time < time.time():
+ # 买单
+ if order.type == OrderType.buy.value :
+ # 下单
+ result = send_order(order.amount, '', order.symbol, 'buy-limit', order.price)
+ order.status = OrderStatus.done.value
+ order.id = result["data"]
+
+ save_order_list(order_list)
+
+ s.enter(10, 1, run_task)
+ s.run()
+
+logging.info('开始运行 ...')
+
+# 开启自调任务
+run_task()
+
+
diff --git a/task/__init__.py b/task/__init__.py
new file mode 100644
index 0000000..72b612c
--- /dev/null
+++ b/task/__init__.py
@@ -0,0 +1,91 @@
+# __init__.py
+import json
+import datetime
+import time
+from enum import Enum
+
+
+class OrderType(Enum):
+ buy = 1
+ sell = 2
+
+
+class OrderStatus(Enum):
+ pending = 1
+ done = 2
+
+
+class Order():
+ def __init__(self):
+ self.symbol = None
+ self.amount = 0
+ self.time = None
+ self.id = ""
+ self.status = OrderStatus.pending # 1 pending, 2 done
+ self.type = OrderType.buy
+ self.price = 0
+
+
+def load_pending_order_list():
+ # 读取下单Order任务
+ file = open("task/pending.json")
+ s = file.read()
+ file.close()
+
+ item_list = json.loads(s)
+
+ # 转换成 Order List 对象
+ order_list = []
+ for item in item_list:
+ order = Order()
+ order.symbol = item["symbol"]
+ order.amount = item["amount"]
+ order.time = item["time"]
+ order.status = int(item["status"])
+ order.type = int(item["type"])
+ order.price = float(item["price"])
+ order_list.append(order)
+
+ return order_list
+
+def save_order_list(order_list):
+ pending_list = []
+
+ # 读取完成 的订单
+ now = datetime.datetime.now()
+ file = open("task/d{0}-{1}-{2}.json".format(now.year, now.month, now.day))
+ s = file.read()
+ file.close()
+
+ done_list = []
+ if s !="":
+ done_list = json.loads(s)
+
+
+ for order in order_list:
+ item = {};
+ item["id"] = order.id
+ item["symbol"] = order.symbol
+ item["amount"] = order.amount
+ item["time"] = order.time
+ item["status"] = order.status
+ item["type"] = order.type
+ item["price"] = order.price
+ if order.status == OrderStatus.pending.value:
+ pending_list.append(item)
+
+ if order.status == OrderStatus.done.value:
+ done_list.append(item)
+
+ # 保存 未完成 的订单
+ s = json.dumps(pending_list)
+ file = open("task/pending.json", 'w')
+ file.write(s)
+ file.close()
+
+ # 保存 完成 的订单
+ s = json.dumps(done_list)
+ now = datetime.datetime.now()
+ file = open("task/d{0}-{1}-{2}.json".format(now.year, now.month, now.day), 'w')
+ file.write(s)
+ file.close()
\ No newline at end of file
diff --git a/task/d2018-2-22.json b/task/d2018-2-22.json
new file mode 100644
index 0000000..d7db1e4
--- /dev/null
+++ b/task/d2018-2-22.json
@@ -0,0 +1 @@
+[{"id": "1720868327", "symbol": "eoseth", "amount": 1, "time": 1519275600, "status": 2, "type": 1, "price": 0.0001}]
\ No newline at end of file
diff --git a/task/pending.json b/task/pending.json
new file mode 100644
index 0000000..0aecd34
--- /dev/null
+++ b/task/pending.json
@@ -0,0 +1 @@
+[{"id": "", "symbol": "eoseth", "amount": 1, "time": 1519308000, "status": 1, "type": 1, "price": 0.0001}]
\ No newline at end of file