commit 52707d9acb5fd29450d4a018bb51399352211a91
Author: dustoair <107600816+dustoair@users.noreply.github.com>
Date: Mon Jul 25 19:22:24 2022 +0800
open src
diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..6327199
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,10 @@
+pipeline:
+ ssh:
+ image: 'me/sre/drone-ssh:20210519'
+ host: 172.16.0.22
+ username: root
+ password: 123456
+ port: 22
+ #username: deploy+ volumes:+ -/root/drone_rsa:/root/ssh/drone_rsa key_path:/root/ssh/drone_rsa
+ script:
+ - echo"test ssh1"
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2885d26
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,118 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+*.idea/*
+*.idea
+*.iml
+# C extensions
+*.so
+.DS_Store
+config.json
+venv
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+nohub.out
+myquant.txt
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.idea
+.idea/*
+
+# log file
+grid_trader_log.txt
+*.log
+
+
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..337e179
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,7 @@
+#mmmmmmmmmmmm/binance_grid_trader:67
+FROM python:3.9
+#RUN pip install -r requirements.txt
+WORKDIR /app
+ADD . /app
+RUN python -m pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
+CMD ["python", "main.py"]
\ No newline at end of file
diff --git a/Dockerfile-requirements b/Dockerfile-requirements
new file mode 100644
index 0000000..20ea662
--- /dev/null
+++ b/Dockerfile-requirements
@@ -0,0 +1,5 @@
+#mmmmmmmmmmmmmmmmmmmmmmmmm/binance_grid_trader_requirements:5
+FROM python:3.9
+
+RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
+RUN pip install -r requirements.txt
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c0e8b21
--- /dev/null
+++ b/README.md
@@ -0,0 +1,118 @@
+
+# 配置 文件`config.json`说明
+
+
+
+| 配置 | 说明 |
+| ----------- | ----------- |
+| sleep_time | 循环间隔 |
+| cancel_time | 取消过期订单间隔 |
+| platform | 交易类型 现货交易 **binance_spot** 或者 合约交易**binance_future** |
+|symbol | 交易对: BTCUSDT, BNBUSDT等 |
+|api_key | 从交易所获取 |
+|api_secret | 交易所获取 |
+|profit_scroll | 滚动止盈比例,比如 1.05 表示触发滚动止盈后将卖单价格提到1.05被再挂上|
+|profit_intend | 滚动触发比例,比如0.95 表示当前价格达到卖单价格的0.95后,触发滚动 |
+|deficit_scroll | 滚动止损比例,比如0.9 表示在触发滚动止盈时候,同时将止损价设置为当前价格的0.9 |
+|gap_percent | 期望利润率,网格交易的价格间隙
buy_price = round_to(float(check_order.get("price")) * (1 - float(config.gap_percent)),
sell_price = round_to(float(check_order.get("price")) * (1 + float(config.gap_percent)), float(config.min_price))
越大单次利润率越高,越小越容易成交 最好不超过1% |
+|quantity | 每次下单的数量 比如50个XRP |
+|min_price | 价格波动的最小单位, 用来计算价格精度: 如
XRPUSDT 0.00001
BTCUSDT 是0.01
BNBUSDT是0.0001
ETHUSDT 是0.01
这个价格要从交易所查看,每个交易对不一样。|
+|price_stop_to_buy | 抛仓价,高于这个就不要买了 |
+|price_stop_to_sell | 清仓价,低于这个就不要卖了 |
+|min_qty | 最小的下单量
现货要求最小下单是10USDT等值的币
XRPUSDT的话 10USDT大概40个XRP
而对于合约来说, BTCUSDT要求是0.001个BTC |
+|max_orders | 最多允许的挂买单数量
超过该值时候 删除最高价的一个买单|
+|max_sell_orders|卖单的上限,达到上限之后不再创建买单;但是不影响创建卖单|
+|proxy_host | 如果需要用代理的话,请填写你的代理IP |
+|proxy_port | 代理端口号 |
+|dingding_robot_url | 钉钉机器人的地址 |
+|dingding_user_phone | 自己在钉钉群的手机号 |
+|user_email | 邮件接收地址 |
+|SMTP_HOST | 邮件服务器 |
+|SMTP_PORT | 邮件服务器端口 |
+|SMTP_USER | 邮件服务器发信邮箱 |
+|SMTP_PASSWORD | 邮件服务器发信邮箱密码 |
+
+
+
+
+例如:
+
+```json
+{
+ "platform": "binance_spot",
+ "symbol": "LINKUSDT",
+ "api_key": "11111111111111111111111111111",
+ "api_secret": "2222222222222222222222222",
+ "gap_percent": 0.005,
+ "quantity": 5,
+ "min_price": 0.0001,
+ "price_stop_to_buy": 22,
+ "price_stop_to_sell": 14,
+ "min_qty": 5,
+ "max_orders": 10,
+ "max_sell_orders": 10,
+ "proxy_host": "",
+ "proxy_port": 0,
+ "dingding_robot_url": "https://oapi.dingtalk.com/robot/send?access_token=33333333333333333333333333333d29c",
+ "dingding_user_phone": "33333333333333333",
+ "user_email": "3333333333333333333333",
+ "SMTP_HOST": "33333333333333333333",
+ "SMTP_PORT": 80,
+ "SMTP_USER": "33333333333333333333333",
+ "SMTP_PASSWORD": "33333333333333333333333333"
+}
+
+```
+
+
+
+### start.sh
+```bash
+nohup python -u main.py > grid_nohup.out 2>&1 &
+```
+
+
+
+## 网格交易策略使用行情
+- 震荡行情
+- 适合币圈的高波动率的品种
+- 适合现货, 如果交易合约,需要注意防止极端行情爆仓。
+
+# 51bitquant网格交易策略
+https://github.com/51bitquant/binance_grid_trader
+
+
+网格交易的原理视频讲解链接:
+[https://www.bilibili.com/video/BV1Jg4y1v7vr/](https://www.bilibili.com/video/BV1Jg4y1v7vr/)
+
+
+视频讲解如下:
+[https://www.bilibili.com/video/BV1eK4y147HT/](https://www.bilibili.com/video/BV1eK4y147HT/)
+
+https://github.com/hengxuZ/binance-quantization
+# 币圈低风险套利赚钱-年化15%以上
+如何在币圈进行低风险的套利。
+
+
+## 买卖出入金的方式
+通过场外OTC,使用人民购买USDT,付给对方钱后,点击已付款,OTC商家会释放相应USDT给你。 在把法币的资产划转到币币账户进行交易相应的数字资产。
+
+相应的如果你想把数字资产换成法币,那么你把你的资产划转到法币,然后出售资产,在收到对方的钱,且确认是对方的账户转给你,数目也是对的情况下,点击已收款,然后释放对应的数字资产给商家。
+
+
+## 存币赚取利息
+ 通过存币,交易所会帮你把资产借贷给相应的人,然后对方需要支付相应的利息给你。
+
+## 挖矿赚利息
+类似DEFI,挖矿,或者CEFI挖矿的方式。为相应的交易对提供流动性,赚取对应的手续费
+
+## 永续资金费率套利
+在多头市场情况下,永续资金费率有的高达0.75%,一般情况下,有的资金费率在万1到千2之间,也有可能是负是资金费率。正的资金费率表示都头出资金费率,负的资金费率表示空方付对应的资金费率。
+
+## 远近合约跨期套利
+一般情况下,远期合约的价格币现货价格、永续合约或者近期的合约的价格要高。如果你买入现货,去对应的远期合约做空对应的资产,那么就相当于你把资产卖出一个较高的价格,等到他们价格相等或者接近的时候,再平仓。不过这个一般是要用程序去执行比较好。这样可以随时间监控他们之间的价差,捕捉更多的套利机会。
+
+## 持有平台币参与交易所的一些活动
+持有BNB或者OKB,参加他们平台的一些活动,比如最近的BNB要用来挖矿,类似之前的IEO活动。
+
+
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bakreq.txt b/bakreq.txt
new file mode 100644
index 0000000..a3e92b7
--- /dev/null
+++ b/bakreq.txt
@@ -0,0 +1,6 @@
+requests==2.22.0
+dingtalkchatbot
+pytz
+APScheduler==3.6.3
+aiohttp==3.6.2
+websocket_client==0.57.0
\ No newline at end of file
diff --git a/binance/__init__.py b/binance/__init__.py
new file mode 100644
index 0000000..5b75099
--- /dev/null
+++ b/binance/__init__.py
@@ -0,0 +1,3 @@
+from .binance_constant import OrderType, OrderStatus, OrderSide, RequestMethod,Interval
+from .binance_spot import BinanceSpotHttp
+from .binance_future import BinanceFutureHttp
\ No newline at end of file
diff --git a/binance/binance_constant.py b/binance/binance_constant.py
new file mode 100644
index 0000000..ea757d3
--- /dev/null
+++ b/binance/binance_constant.py
@@ -0,0 +1,55 @@
+from enum import Enum
+
+class RequestMethod(Enum):
+ """
+ 请求的方法.
+ """
+ GET = 'GET'
+ POST = 'POST'
+ PUT = 'PUT'
+ DELETE = 'DELETE'
+
+
+
+class OrderStatus(Enum):
+ NEW = "NEW"
+ PARTIALLY_FILLED = "PARTIALLY_FILLED"
+ FILLED = "FILLED"
+ CANCELED = "CANCELED"
+ PENDING_CANCEL = "PENDING_CANCEL"
+ REJECTED = "REJECTED"
+ EXPIRED = "EXPIRED"
+
+
+class OrderType(Enum):
+ LIMIT = "LIMIT"
+ MARKET = "MARKET"
+ STOP = "STOP"
+
+
+
+class OrderSide(Enum):
+ BUY = "BUY"
+ SELL = "SELL"
+
+
+class Interval(Enum):
+ """
+ 请求的K线数据..
+ """
+ MINUTE_1 = '1m'
+ MINUTE_3 = '3m'
+ MINUTE_5 = '5m'
+ MINUTE_15 = '15m'
+ MINUTE_30 = '30m'
+ HOUR_1 = '1h'
+ HOUR_2 = '2h'
+ HOUR_4 = '4h'
+ HOUR_6 = '6h'
+ HOUR_8 = '8h'
+ HOUR_12 = '12h'
+ DAY_1 = '1d'
+ DAY_3 = '3d'
+ WEEK_1 = '1w'
+ MONTH_1 = '1M'
+
diff --git a/binance/binance_future.py b/binance/binance_future.py
new file mode 100644
index 0000000..0cb448b
--- /dev/null
+++ b/binance/binance_future.py
@@ -0,0 +1,345 @@
+from binance import OrderType, OrderStatus, OrderSide, RequestMethod,Interval
+import requests
+import time
+import hmac
+import hashlib
+from threading import Thread, Lock
+from datetime import datetime
+
+class BinanceFutureHttp(object):
+
+ def __init__(self, api_key=None, secret=None, host=None, proxy_host="", proxy_port=0, timeout=5, try_counts=5):
+ self.key = api_key
+ self.secret = secret
+ self.host = host if host else "https://fapi.binance.com"
+ self.recv_window = 5000
+ self.timeout = timeout
+ self.order_count_lock = Lock()
+ self.order_count = 1_000_000
+ self.try_counts = try_counts # 失败尝试的次数.
+ self.proxy_host = proxy_host
+ self.proxy_port = proxy_port
+
+ @property
+ def proxies(self):
+ if self.proxy_host and self.proxy_port:
+ proxy = f"http://{self.proxy_host}:{self.proxy_port}"
+ return {"http": proxy, "https": proxy}
+ return {}
+
+ def build_parameters(self, params: dict):
+ keys = list(params.keys())
+ keys.sort()
+ return '&'.join([f"{key}={params[key]}" for key in params.keys()])
+
+ def request(self, req_method: RequestMethod, path: str, requery_dict=None, verify=False):
+ url = self.host + path
+
+ if verify:
+ query_str = self._sign(requery_dict)
+ url += '?' + query_str
+ elif requery_dict:
+ url += '?' + self.build_parameters(requery_dict)
+ headers = {"X-MBX-APIKEY": self.key}
+
+ for i in range(0, self.try_counts):
+ try:
+ response = requests.request(req_method.value, url=url, headers=headers, timeout=self.timeout, proxies=self.proxies)
+ if response.status_code == 200:
+ return response.json()
+ else:
+ print(f"请求没有成功: {response.status_code}, 继续尝试请求")
+ except Exception as error:
+ print(f"请求:{path}, 发生了错误: {error}, 时间: {datetime.now()}")
+ time.sleep(3)
+
+ def server_time(self):
+ path = '/fapi/v1/time'
+ return self.request(req_method=RequestMethod.GET, path=path)
+
+ def exchangeInfo(self):
+
+ """
+ {'timezone': 'UTC', 'serverTime': 1570802268092, 'rateLimits':
+ [{'rateLimitType': 'REQUEST_WEIGHT', 'interval': 'MINUTE', 'intervalNum': 1, 'limit': 1200},
+ {'rateLimitType': 'ORDERS', 'interval': 'MINUTE', 'intervalNum': 1, 'limit': 1200}],
+ 'exchangeFilters': [], 'symbols':
+ [{'symbol': 'BTCUSDT', 'status': 'TRADING', 'maintMarginPercent': '2.5000', 'requiredMarginPercent': '5.0000',
+ 'baseAsset': 'BTC', 'quoteAsset': 'USDT', 'pricePrecision': 2, 'quantityPrecision': 3, 'baseAssetPrecision': 8,
+ 'quotePrecision': 8,
+ 'filters': [{'minPrice': '0.01', 'maxPrice': '100000', 'filterType': 'PRICE_FILTER', 'tickSize': '0.01'},
+ {'stepSize': '0.001', 'filterType': 'LOT_SIZE', 'maxQty': '1000', 'minQty': '0.001'},
+ {'stepSize': '0.001', 'filterType': 'MARKET_LOT_SIZE', 'maxQty': '1000', 'minQty': '0.001'},
+ {'limit': 200, 'filterType': 'MAX_NUM_ORDERS'},
+ {'multiplierDown': '0.8500', 'multiplierUp': '1.1500', 'multiplierDecimal': '4', 'filterType': 'PERCENT_PRICE'}],
+ 'orderTypes': ['LIMIT', 'MARKET', 'STOP'], 'timeInForce': ['GTC', 'IOC', 'FOK', 'GTX']}]}
+
+ :return:
+ """
+
+ path = '/fapi/v1/exchangeInfo'
+ return self.request(req_method=RequestMethod.GET, path=path)
+
+ def order_book(self, symbol, limit=5):
+ limits = [5, 10, 20, 50, 100, 500, 1000]
+ if limit not in limits:
+ limit = 5
+
+ path = "/fapi/v1/depth"
+ query_dict = {"symbol": symbol,
+ "limit": limit
+ }
+
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_kline(self, symbol, interval: Interval, start_time=None, end_time=None, limit=500, max_try_time=10):
+ """
+
+ :param symbol:
+ :param interval:
+ :param start_time:
+ :param end_time:
+ :param limit:
+ :return:
+ [
+ 1499040000000, // 开盘时间
+ "0.01634790", // 开盘价
+ "0.80000000", // 最高价
+ "0.01575800", // 最低价
+ "0.01577100", // 收盘价(当前K线未结束的即为最新价)
+ "148976.11427815", // 成交量
+ 1499644799999, // 收盘时间
+ "2434.19055334", // 成交额
+ 308, // 成交笔数
+ "1756.87402397", // 主动买入成交量
+ "28.46694368", // 主动买入成交额
+ "17928899.62484339" // 请忽略该参数
+ ]
+ """
+ path = "/fapi/v1/klines"
+
+ query_dict = {
+ "symbol": symbol,
+ "interval": interval.value,
+ "limit": limit
+ }
+
+ if start_time:
+ query_dict['startTime'] = start_time
+
+ if end_time:
+ query_dict['endTime'] = end_time
+
+ for i in range(max_try_time):
+ data = self.request(RequestMethod.GET, path, query_dict)
+ if isinstance(data, list) and len(data):
+ return data
+
+ def get_latest_price(self, symbol):
+ path = "/fapi/v1/ticker/price"
+ query_dict = {"symbol": symbol}
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_ticker(self, symbol):
+ path = "/fapi/v1/ticker/bookTicker"
+ query_dict = {"symbol": symbol}
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ ########################### the following request is for private data ########################
+
+ def _timestamp(self):
+ return int(time.time() * 1000)
+
+ def _sign(self, params):
+
+ requery_string = self.build_parameters(params)
+ hexdigest = hmac.new(self.secret.encode('utf8'), requery_string.encode("utf-8"), hashlib.sha256).hexdigest()
+ return requery_string + '&signature=' + str(hexdigest)
+
+ def get_client_order_id(self):
+
+ """
+ generate the client_order_id for user.
+ :return: new client order id
+ """
+ with self.order_count_lock:
+ self.order_count += 1
+ return "x-cLbi5uMH" + str(self._timestamp()) + str(self.order_count)
+
+ def place_order(self, symbol: str, order_side: OrderSide, order_type: OrderType, quantity, price,
+ time_inforce="GTC", client_order_id=None, recvWindow=5000, stop_price=0):
+
+ """
+ 下单..
+ :param symbol: BTCUSDT
+ :param side: BUY or SELL
+ :param type: LIMIT MARKET STOP
+ :param quantity: 数量.
+ :param price: 价格
+ :param stop_price: 停止单的价格.
+ :param time_inforce:
+ :param params: 其他参数
+
+ LIMIT : timeInForce, quantity, price
+ MARKET : quantity
+ STOP: quantity, price, stopPrice
+ :return:
+
+ """
+
+ path = '/fapi/v1/order'
+
+ if client_order_id is None:
+ client_order_id = self.get_client_order_id()
+
+ params = {
+ "symbol": symbol,
+ "side": order_side.value,
+ "type": order_type.value,
+ "quantity": quantity,
+ "price": price,
+ "recvWindow": recvWindow,
+ "timeInForce": "GTC",
+ "timestamp": self._timestamp(),
+ "newClientOrderId": client_order_id
+ }
+
+ if order_type == OrderType.LIMIT:
+ params['timeInForce'] = time_inforce
+
+ if order_type == OrderType.MARKET:
+ if params.get('price'):
+ del params['price']
+
+ if order_type == OrderType.STOP:
+ if stop_price > 0:
+ params["stopPrice"] = stop_price
+ else:
+ raise ValueError("stopPrice must greater than 0")
+ # print(params)
+ return self.request(RequestMethod.POST, path=path, requery_dict=params, verify=True)
+
+ def get_order(self, symbol, client_order_id=None):
+ path = "/fapi/v1/order"
+ query_dict = {"symbol": symbol, "timestamp": self._timestamp()}
+ if client_order_id:
+ query_dict["origClientOrderId"] = client_order_id
+
+ return self.request(RequestMethod.GET, path, query_dict, verify=True)
+
+ def cancel_order(self, symbol, client_order_id=None):
+ path = "/fapi/v1/order"
+ params = {"symbol": symbol, "timestamp": self._timestamp()}
+ if client_order_id:
+ params["origClientOrderId"] = client_order_id
+
+ return self.request(RequestMethod.DELETE, path, params, verify=True)
+
+ def get_open_orders(self, symbol=None):
+ path = "/fapi/v1/openOrders"
+
+ params = {"timestamp": self._timestamp()}
+ if symbol:
+ params["symbol"] = symbol
+
+ return self.request(RequestMethod.GET, path, params, verify=True)
+
+ def cancel_open_orders(self, symbol):
+ """
+ 撤销某个交易对的所有挂单
+ :param symbol: symbol
+ :return: return a list of orders.
+ """
+ path = "/fapi/v1/allOpenOrders"
+
+ params = {"timestamp": self._timestamp(),
+ "recvWindow": self.recv_window,
+ "symbol": symbol
+ }
+
+ return self.request(RequestMethod.DELETE, path, params, verify=True)
+
+ def get_balance(self):
+ """
+ [{'accountId': 18396, 'asset': 'USDT', 'balance': '530.21334791', 'withdrawAvailable': '530.21334791', 'updateTime': 1570330854015}]
+ :return:
+ """
+ path = "/fapi/v1/balance"
+ params = {"timestamp": self._timestamp()}
+
+ return self.request(RequestMethod.GET, path=path, requery_dict=params, verify=True)
+
+ def get_account_info(self):
+ """
+ {'feeTier': 2, 'canTrade': True, 'canDeposit': True, 'canWithdraw': True, 'updateTime': 0, 'totalInitialMargin': '0.00000000',
+ 'totalMaintMargin': '0.00000000', 'totalWalletBalance': '530.21334791', 'totalUnrealizedProfit': '0.00000000',
+ 'totalMarginBalance': '530.21334791', 'totalPositionInitialMargin': '0.00000000', 'totalOpenOrderInitialMargin': '0.00000000',
+ 'maxWithdrawAmount': '530.2133479100000', 'assets':
+ [{'asset': 'USDT', 'walletBalance': '530.21334791', 'unrealizedProfit': '0.00000000', 'marginBalance': '530.21334791',
+ 'maintMargin': '0.00000000', 'initialMargin': '0.00000000', 'positionInitialMargin': '0.00000000', 'openOrderInitialMargin': '0.00000000',
+ 'maxWithdrawAmount': '530.2133479100000'}]}
+ :return:
+ """
+ path = "/fapi/v1/account"
+ params = {"timestamp": self._timestamp()}
+ return self.request(RequestMethod.GET, path, params, verify=True)
+
+ def get_position_info(self):
+ """
+ [{'symbol': 'BTCUSDT', 'positionAmt': '0.000', 'entryPrice': '0.00000', 'markPrice': '8326.40833498', 'unRealizedProfit': '0.00000000', 'liquidationPrice': '0'}]
+ :return:
+ """
+ path = "/fapi/v1/positionRisk"
+ params = {"timestamp": self._timestamp()}
+ return self.request(RequestMethod.GET, path, params, verify=True)
+
+
+if __name__ == '__main__':
+ # import pandas as pd
+
+ key = "xxxx"
+ secret = 'xxxx'
+ binance = BinanceFutureHttp(api_key=key, secret=secret)
+
+ # import datetime
+ # print(datetime.datetime.now())
+
+ data = binance.get_kline('BTCUSDT', Interval.HOUR_1, limit=100)
+ print(data)
+ print(isinstance(data, list))
+
+ exit()
+ # info = binance.exchangeInfo()
+ # print(info)
+ # exit()
+ # print(binance.order_book(symbol='BTCUSDT', limit=5))
+ # exit()
+
+ # print(binance.latest_price('BTCUSDT'))
+ # kline = binance.kline('BTCUSDT', interval='1m')
+ # print(pd.DataFrame(kline))
+
+ # print(binance.ticker("BTCUSDT"))
+
+ # print(binance.get_ticker('BTCUSDT'))
+ # {'symbol': 'BTCUSDT', 'side': 'SELL', 'type': 'LIMIT', 'quantity': 0.001, 'price': 8360.82, 'recvWindow': 5000, 'timestamp': 1570969995974, 'timeInForce': 'GTC'}
+
+ # data = binance.place_order(symbol="BTCUSDT", side=OrderSide.BUY, order_type=OrderType.MARKET, quantity=0.001, price=8250.82)
+ # print(data)
+
+ # cancel_order = binance.cancel_order("BTCUSDT", order_id="30714952")
+ # print(cancel_order)
+
+ # balance = binance.get_balance()
+ # print(balance)
+
+ # account_info = binance.get_account_info()
+ # print(account_info)
+
+ # account_info = binance.get_position_info()
+ # print(account_info)
+
+ """
+ {'orderId': 30714952, 'symbol': 'BTCUSDT', 'accountId': 18396, 'status': 'NEW', 'clientOrderId': 'ZC3qSbzbODl0GId9idK9hM', 'price': '7900', 'origQty': '0.010', 'executedQty': '0', 'cumQty': '0', 'cumQuote': '0', 'timeInForce': 'GTC', 'type': 'LIMIT', 'reduceOnly': False, 'side': 'BUY', 'stopPrice': '0', 'updateTime': 1569658787083}
+ {'orderId': 30714952, 'symbol': 'BTCUSDT', 'accountId': 18396, 'status': 'NEW', 'clientOrderId': 'ZC3qSbzbODl0GId9idK9hM', 'price': '7900', 'origQty': '0.010', 'executedQty': '0', 'cumQty': '0', 'cumQuote': '0', 'timeInForce': 'GTC', 'type': 'LIMIT', 'reduceOnly': False, 'side': 'BUY', 'stopPrice': '0', 'updateTime': 1569658787083}
+ """
diff --git a/binance/binance_future_trader.py b/binance/binance_future_trader.py
new file mode 100644
index 0000000..f50f002
--- /dev/null
+++ b/binance/binance_future_trader.py
@@ -0,0 +1,226 @@
+from binance import BinanceFutureHttp, OrderStatus, OrderType, OrderSide
+from utils import config
+from utils import round_to
+import logging
+from datetime import datetime
+
+class BinanceFutureTrader(object):
+
+ def __init__(self):
+ self.http_client = BinanceFutureHttp(api_key=config.api_key, secret=config.api_secret, proxy_host=config.proxy_host, proxy_port=config.proxy_port)
+
+ self.buy_orders = [] # 买单. buy orders
+ self.sell_orders = [] # 卖单. sell orders
+
+
+ def get_bid_ask_price(self):
+
+ ticker = self.http_client.get_ticker(config.symbol)
+
+ bid_price = 0
+ ask_price = 0
+ if ticker:
+ bid_price = float(ticker.get('bidPrice', 0))
+ ask_price = float(ticker.get('askPrice', 0))
+
+ return bid_price, ask_price
+
+ def get_latest_price(self):
+ #获取最新价格
+ ticker = self.http_client.get_latest_price(config.symbol)
+ price = 0
+ if ticker:
+ price = float(ticker.get('price', 0))
+ return price
+
+ def grid_trader(self):
+ """
+ 执行核心逻辑,网格交易的逻辑.
+
+ the grid trading logic
+ :return:
+ """
+
+ bid_price, ask_price = self.get_bid_ask_price()
+ print(f"bid_price: {bid_price}, ask_price: {ask_price}, time: {datetime.now()}")
+
+ quantity = round_to(float(config.quantity), float(config.min_qty))
+
+ self.buy_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
+ self.sell_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
+
+ buy_delete_orders = [] # 需要删除买单
+ sell_delete_orders = [] # 需要删除的卖单
+
+
+ # 买单逻辑,检查成交的情况.
+ for buy_order in self.buy_orders:
+
+ check_order = self.http_client.get_order(buy_order.get('symbol', config.symbol),client_order_id=buy_order.get('clientOrderId'))
+
+ if check_order:
+ if check_order.get('status') == OrderStatus.CANCELED.value:
+ buy_delete_orders.append(buy_order)
+ print(f"buy order status was canceled: {check_order.get('status')}, time: {datetime.now()}")
+ elif check_order.get('status') == OrderStatus.FILLED.value:
+ # 买单成交,挂卖单.
+ print(f"买单成交了, 时间: {datetime.now()}")
+ logging.info(f"买单成交时间: {datetime.now()}, 价格: {check_order.get('price')}, 数量: {check_order.get('origQty')}")
+
+ sell_price = round_to(float(check_order.get("price")) * (1 + float(config.gap_percent)), float(config.min_price))
+
+ if 0 < sell_price < ask_price:
+ # 防止价格
+ sell_price = round_to(ask_price, float(config.min_price))
+
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL, order_type=OrderType.LIMIT, quantity=quantity, price=sell_price)
+ if new_sell_order:
+ print(f"买单成交,下了对应价格的卖单: {new_sell_order}, 时间: {datetime.now()}")
+ buy_delete_orders.append(buy_order)
+ self.sell_orders.append(new_sell_order)
+
+ buy_price = round_to(float(check_order.get("price")) * (1 - float(config.gap_percent)),
+ config.min_price)
+
+ if buy_price > bid_price > 0:
+ buy_price = round_to(buy_price, float(config.min_price))
+
+ new_buy_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.BUY, order_type=OrderType.LIMIT, quantity=quantity, price=buy_price)
+ if new_buy_order:
+ print(f"买单成交,下了更低价的买单: {new_buy_order}, 时间: {datetime.now()}")
+ self.buy_orders.append(new_buy_order)
+
+
+ elif check_order.get('status') == OrderStatus.NEW.value:
+ print(f"buy order status is: New , 时间: {datetime.now()}")
+ else:
+ print(f"buy order status is not above options: {check_order.get('status')}, 时间: {datetime.now()}")
+
+ # 过期或者拒绝的订单删除掉.
+ for delete_order in buy_delete_orders:
+ self.buy_orders.remove(delete_order)
+
+ # 卖单逻辑, 检查卖单成交情况.
+ for sell_order in self.sell_orders:
+
+ check_order = self.http_client.get_order(sell_order.get('symbol', config.symbol),
+ client_order_id=sell_order.get('clientOrderId'))
+ if check_order:
+ if check_order.get('status') == OrderStatus.CANCELED.value:
+ sell_delete_orders.append(sell_order)
+
+ print(f"sell order status was canceled: {check_order.get('status')}, 时间: {datetime.now()}")
+ elif check_order.get('status') == OrderStatus.FILLED.value:
+ print(f"卖单成交了, 时间: {datetime.now()}")
+ logging.info(
+ f"卖单成交时间: {datetime.now()}, 价格: {check_order.get('price')}, 数量: {check_order.get('origQty')}")
+ # 卖单成交,先下买单.
+ buy_price = round_to(float(check_order.get("price")) * (1 - float(config.gap_percent)), float(config.min_price))
+ if buy_price > bid_price > 0:
+ buy_price = round_to(buy_price, float(config.min_price))
+
+ new_buy_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.BUY,
+ order_type=OrderType.LIMIT, quantity=quantity, price=buy_price)
+ if new_buy_order:
+ print(f"卖单成交,下了对应价格的买单: {new_buy_order}, 时间: {datetime.now()}")
+ sell_delete_orders.append(sell_order)
+ self.buy_orders.append(new_buy_order)
+
+ sell_price = round_to(float(check_order.get("price")) * (1 + float(config.gap_percent)), float(config.min_price))
+
+ if 0 < sell_price < ask_price:
+ # 防止价格
+ sell_price = round_to(ask_price, float(config.min_price))
+
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
+ order_type=OrderType.LIMIT, quantity=quantity,
+ price=sell_price)
+ if new_sell_order:
+ print(f"卖单成交,下了更高价的卖单: {new_sell_order}, 时间: {datetime.now()}")
+ self.sell_orders.append(new_sell_order)
+
+ elif check_order.get('status') == OrderStatus.NEW.value:
+ print(f"sell order status is: New, 时间: {datetime.now()}")
+ else:
+ print(f"sell order status is not in above options: {check_order.get('status')}, 时间: {datetime.now()}")
+
+ # 过期或者拒绝的订单删除掉.
+ for delete_order in sell_delete_orders:
+ self.sell_orders.remove(delete_order)
+
+ # 没有买单的时候.
+ if len(self.buy_orders) <= 0:
+ if bid_price > 0:
+ price = round_to(bid_price * (1 - float(config.gap_percent)), float(config.min_price))
+
+ buy_order = self.http_client.place_order(symbol=config.symbol,order_side=OrderSide.BUY, order_type=OrderType.LIMIT, quantity=quantity,price=price)
+ print(f'没有买单,根据盘口下买单: {buy_order}, 时间: {datetime.now()}')
+ if buy_order:
+ self.buy_orders.append(buy_order)
+ else:
+ self.buy_orders.sort(key=lambda x: float(x['price']), reverse=False) # 最低价到最高价
+ delete_orders = []
+ for i in range(len(self.buy_orders) -1):
+ order = self.buy_orders[i]
+ next_order = self.buy_orders[i+1]
+
+ if float(next_order['price'])/float(order['price']) - 1 < 0.001:
+ print(f"买单之间价差太小,撤销订单:{next_order}, 时间: {datetime.now()}")
+ cancel_order = self.http_client.cancel_order(next_order.get('symbol'),
+ client_order_id=next_order.get('clientOrderId'))
+ if cancel_order:
+ delete_orders.append(next_order)
+
+ for order in delete_orders:
+ self.buy_orders.remove(order)
+
+
+ if len(self.buy_orders) > int(config.max_orders): # 最多允许的挂单数量.
+ # 订单数量比较多的时候.
+ self.buy_orders.sort(key=lambda x: float(x['price']), reverse=False) # 最低价到最高价
+
+ delete_order = self.buy_orders[0]
+ print(f"订单太多了,撤销最低价的买单:{delete_order}, 时间: {datetime.now()}")
+ order = self.http_client.cancel_order(delete_order.get('symbol'), client_order_id=delete_order.get('clientOrderId'))
+ if order:
+ self.buy_orders.remove(delete_order)
+
+ # 没有卖单的时候.
+ if len(self.sell_orders) <= 0:
+ if ask_price > 0:
+ price = round_to(ask_price * (1 + float(config.gap_percent)), float(config.min_price))
+ sell_order = self.http_client.place_order(symbol=config.symbol,order_side=OrderSide.SELL, order_type=OrderType.LIMIT, quantity=quantity,price=price)
+ print(f'没有卖单,根据盘口下卖单:{sell_order} , 时间: {datetime.now()}')
+ if sell_order:
+ self.sell_orders.append(sell_order)
+
+ else:
+ self.sell_orders.sort(key=lambda x: float(x['price']), reverse=False) # 最低价到最高价
+ delete_orders = []
+ for i in range(len(self.sell_orders) - 1):
+ order = self.sell_orders[i]
+ next_order = self.sell_orders[i + 1]
+
+ if float(next_order['price']) / float(order['price']) - 1 < 0.001:
+ print(f"卖单之间价差太小,撤销订单:{next_order}, 时间: {datetime.now()}")
+ cancel_order = self.http_client.cancel_order(next_order.get('symbol'),
+ client_order_id=next_order.get('clientOrderId'))
+ if cancel_order:
+ delete_orders.append(next_order)
+
+ for order in delete_orders:
+ self.sell_orders.remove(order)
+
+ if len(self.sell_orders) > int(config.max_orders): # 最多允许的挂单数量.
+ # 订单数量比较多的时候.
+ self.sell_orders.sort(key=lambda x: x['price'], reverse=True) # 最高价到最低价
+
+ delete_order = self.sell_orders[0]
+ print(f"订单太多了,撤销最高价的卖单:{delete_order}, 时间:{datetime.now()}")
+ order = self.http_client.cancel_order(delete_order.get('symbol'),
+ client_order_id=delete_order.get('clientOrderId'))
+ if order:
+ self.sell_orders.remove(delete_order)
+
+
+
diff --git a/binance/binance_spot.py b/binance/binance_spot.py
new file mode 100644
index 0000000..88febc9
--- /dev/null
+++ b/binance/binance_spot.py
@@ -0,0 +1,456 @@
+from binance import OrderType, OrderStatus, OrderSide, RequestMethod,Interval
+import requests
+import time
+import hmac
+import hashlib
+from threading import Lock
+from utils import config
+from utils import utility
+
+
+class BinanceSpotHttp(object):
+ '''
+ # https://binance-docs.github.io/apidocs/spot/cn/ # 152d3db758
+ # 如果上面的baseURL访问有性能问题,请访问下面的API集群:
+ # https://api1.binance.com
+ # https://api2.binance.com
+ # https://api3.binance.com
+ # https://api4.binance.com
+ # ORDERS rateLimits 100次/10s 200000次/1day
+ '''
+
+ def __init__(self, api_key=None, secret=None, host=None, proxy_host=None, proxy_port=0, timeout=5, try_counts=5):
+ self.api_key = api_key
+ self.secret = secret
+ self.host = host if host else "https://api.binance.com"
+ self.recv_window = 10000
+ self.timeout = timeout
+ self.order_count_lock = Lock()
+ self.order_count = 1_000_000
+ self.try_counts = try_counts # 失败尝试的次数.
+ self.proxy_host = proxy_host
+ self.proxy_port = proxy_port
+
+ @property
+ def proxies(self):
+ if self.proxy_host and self.proxy_port:
+ proxy = f"http://{self.proxy_host}:{self.proxy_port}"
+ return {"http": proxy, "https": proxy}
+
+ return None
+
+ def build_parameters(self, params: dict):
+ keys = list(params.keys())
+ keys.sort()
+ return '&'.join([f"{key}={params[key]}" for key in params.keys()])
+
+ def request(self, req_method: RequestMethod, path: str, requery_dict=None, verify=False):
+ url = self.host + path
+
+ if verify:
+ query_str = self._sign(requery_dict)
+ url += '?' + query_str
+ elif requery_dict:
+ url += '?' + self.build_parameters(requery_dict)
+ headers = {"X-MBX-APIKEY": self.api_key}
+
+ for i in range(0, self.try_counts):
+ try:
+ response = requests.request(req_method.value, url=url, headers=headers, timeout=self.timeout, proxies=self.proxies)
+ http_code = response.status_code
+
+ if http_code == 200:
+ return response.json()
+ elif http_code == 400 and "Account has insufficient balance for requested action." in str(response.json()):
+ print(f"交易对: {config.symbol} 余额不足")
+ break
+ elif http_code == 400 and "Filter failure: MIN_NOTIONAL" in str(response.json()):
+ print(f"交易对: {config.symbol} 最小下单数量不够,多买点")
+ break
+ elif http_code == 400 and "Unknown order sent." in str(response.json()):
+ print(f"交易对: {config.symbol} 订单号错误")
+ print(f"请求结果{str(response.json())}")
+ break
+ elif http_code == 400 and "Illegal characters found in parameter 'symbol'" in str(response.json()):
+ print(f"交易对: {config.symbol} 交易对配置错误,无法识别")
+ break
+ elif http_code == 400 and "This action disabled is on this account" in str(response.json()):
+ print(f"交易对: {config.symbol} 账号无权此操作")
+ break
+ else:
+ print("未知请求错误:")
+ print(response.json(), http_code)
+
+ except Exception as error:
+ print(f"请求:{path}, 发生了错误: {error}")
+ time.sleep(3)
+
+ def get_ping(self):
+ #测试服务器连通性
+ #测试能否联通 Rest API。
+ path = '/api/v3/ping'
+ return self.request(req_method=RequestMethod.GET, path=path)
+
+ def get_server_time(self):
+ #获取服务器时间
+ #测试能否联通 Rest API 并 获取服务器时间。
+ path = '/api/v3/time'
+ return self.request(req_method=RequestMethod.GET, path=path)
+
+
+
+
+ def get_exchange_info(self):
+ #交易规范信息
+ #获取交易规则和交易对信息。
+
+ """
+ return:
+ the exchange info in json format:
+ {'timezone': 'UTC', 'serverTime': 1570802268092, 'rateLimits':
+ [{'rateLimitType': 'REQUEST_WEIGHT', 'interval': 'MINUTE', 'intervalNum': 1, 'limit': 1200},
+ {'rateLimitType': 'ORDERS', 'interval': 'MINUTE', 'intervalNum': 1, 'limit': 1200}],
+ 'exchangeFilters': [], 'symbols':
+ [{'symbol': 'BTCUSDT', 'status': 'TRADING', 'maintMarginPercent': '2.5000', 'requiredMarginPercent': '5.0000',
+ 'baseAsset': 'BTC', 'quoteAsset': 'USDT', 'pricePrecision': 2, 'quantityPrecision': 3, 'baseAssetPrecision': 8,
+ 'quotePrecision': 8,
+ 'filters': [{'minPrice': '0.01', 'maxPrice': '100000', 'filterType': 'PRICE_FILTER', 'tickSize': '0.01'},
+ {'stepSize': '0.001', 'filterType': 'LOT_SIZE', 'maxQty': '1000', 'minQty': '0.001'},
+ {'stepSize': '0.001', 'filterType': 'MARKET_LOT_SIZE', 'maxQty': '1000', 'minQty': '0.001'},
+ {'limit': 200, 'filterType': 'MAX_NUM_ORDERS'},
+ {'multiplierDown': '0.8500', 'multiplierUp': '1.1500', 'multiplierDecimal': '4', 'filterType': 'PERCENT_PRICE'}],
+ 'orderTypes': ['LIMIT', 'MARKET', 'STOP'], 'timeInForce': ['GTC', 'IOC', 'FOK', 'GTX']}]}
+
+ """
+
+ path = '/api/v3/exchangeInfo'
+ return self.request(req_method=RequestMethod.GET, path=path)
+
+
+
+ def get_order_book(self, symbol, limit=5):
+ """
+ 深度信息
+ https://binance-docs.github.io/apidocs/spot/cn/#e7746f7d60
+ :param symbol: BTCUSDT, BNBUSDT ect, 交易对.
+ :param limit: market depth. 默认 100; 最大 5000. 可选值:[5, 10, 20, 50, 100, 500, 1000, 5000]
+ :return: return order_book in json 返回订单簿,json数据格式.
+ """
+ limits = [5, 10, 20, 50, 100, 500, 1000]
+ if limit not in limits:
+ limit = 5
+
+ path = "/api/v3/depth"
+ query_dict = {"symbol": symbol,
+ "limit": limit
+ }
+
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_recent_trades(self, symbol, limit=5):
+ """
+ 近期成交列表
+ https://binance-docs.github.io/apidocs/spot/cn/#38a975b802
+ :param symbol: BTCUSDT, BNBUSDT ect, 交易对.
+ :param limit: 默认 500; 最大值 1000.
+ :return: return order_book in json 返回订单簿,json数据格式.
+ """
+
+ if limit >= 1000:
+ limit = 1000
+
+ path = "/api/v3/trades"
+ query_dict = {"symbol": symbol,
+ "limit": limit
+ }
+
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_kline(self, symbol, interval: Interval, start_time=None, end_time=None, limit=500, max_try_time=10):
+ """
+ 获取K线数据.
+ 每根K线代表一个交易对。
+ 每根K线的开盘时间可视为唯一ID
+ https://binance-docs.github.io/apidocs/spot/cn/#c59e471e81
+ :param symbol:
+ :param interval: 间隔
+ :param start_time:
+ :param end_time:
+ :param limit: 默认 500; 最大 1000
+ :param max_try_time:
+ :return:
+ [
+ [
+ 1499040000000, // 开盘时间
+ "0.01634790", // 开盘价
+ "0.80000000", // 最高价
+ "0.01575800", // 最低价
+ "0.01577100", // 收盘价(当前K线未结束的即为最新价)
+ "148976.11427815", // 成交量
+ 1499644799999, // 收盘时间
+ "2434.19055334", // 成交额
+ 308, // 成交笔数
+ "1756.87402397", // 主动买入成交量
+ "28.46694368", // 主动买入成交额
+ "17928899.62484339" // 请忽略该参数
+ ]
+]
+ """
+ path = "/api/v3/klines"
+ query_dict = {
+ "symbol": symbol,
+ "interval": interval,
+ "limit": limit
+ }
+
+ # query_dict = {
+ # "symbol": symbol,
+ # "interval": interval.value,
+ # "limit": limit
+ # }
+
+ if start_time:
+ query_dict['startTime'] = start_time
+
+ if end_time:
+ query_dict['endTime'] = end_time
+
+ for i in range(max_try_time):
+ data = self.request(RequestMethod.GET, path, query_dict)
+ if isinstance(data, list) and len(data):
+ return data
+
+ def get_latest_price(self, symbol):
+ """
+ 获取交易对最新价格
+ 不发送交易对参数,则会返回所有交易对信息
+ 权重:
+ 1 单一交易对;
+ 2 交易对参数缺失
+ :param symbol: 获取最新的价格.
+ :return: {'symbol': 'BTCUSDT', 'price': '9168.90000000'}
+
+
+ """
+ path = "/api/v3/ticker/price"
+ query_dict = {"symbol": symbol}
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_avg_price_5min(self, symbol):
+ """
+ 当前平均价格 in last 5min
+ 权重: 1
+ :param symbol:
+ :return:
+ {
+ "mins": 5,
+ "price": "9.35751834"
+ }
+
+ """
+ path = "/api/v3/avgPrice"
+ query_dict = {"symbol": symbol}
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_price_info_24h(self, symbol):
+ """
+ 24hr 价格变动情况
+ 24 小时滚动窗口价格变动数据。 请注意,不携带symbol参数会返回全部交易对数据,不仅数据庞大,而且权重极高
+ 权重: 1 单一交易对;
+ 40 交易对参数缺失;
+ 请注意,不携带symbol参数会返回全部交易对数据
+
+ :param symbol:
+ :return:
+{
+ "symbol": "BNBBTC",
+ "priceChange": "-94.99999800",
+ "priceChangePercent": "-95.960",
+ "weightedAvgPrice": "0.29628482",
+ "prevClosePrice": "0.10002000",
+ "lastPrice": "4.00000200",
+ "lastQty": "200.00000000",
+ "bidPrice": "4.00000000",
+ "askPrice": "4.00000200",
+ "openPrice": "99.00000000",
+ "highPrice": "100.00000000",
+ "lowPrice": "0.10000000",
+ "volume": "8913.30000000",
+ "quoteVolume": "15.30000000",
+ "openTime": 1499783499040,
+ "closeTime": 1499869899040,
+ "firstId": 28385, // 首笔成交id
+ "lastId": 28460, // 末笔成交id
+ "count": 76 // 成交笔数
+}
+
+ """
+ path = "/api/v3/ticker/24hr"
+ query_dict = {"symbol": symbol}
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_ticker(self, symbol):
+ """
+ 当前最优挂单
+ 返回当前最优的挂单(最高买单,最低卖单)
+ bid price的本意是出价 最高买价
+ ask price的本意是要价 最低卖价
+ ask是“要”的意思,就卖家喊出来的价
+ bid是“投标”的意思,是买家愿意出的最高价。
+ :param symbol: 交易对
+ :return: 返回的数据如下:
+ {
+ 'symbol': 'BTCUSDT', 'bidPrice': '9168.50000000', 'bidQty': '1.27689900',
+ 'askPrice': '9168.51000000', 'askQty': '0.93307800'
+ }
+ """
+ path = "/api/v3/ticker/bookTicker"
+ query_dict = {"symbol": symbol}
+ return self.request(RequestMethod.GET, path, query_dict)
+
+ def get_client_order_id(self):
+ """
+ generate the client_order_id for user.
+ :return:
+ """
+ with self.order_count_lock:
+ self.order_count += 1
+ return config.symbol + str(utility.get_current_timestamp()) + str(self.order_count)
+
+ def _sign(self, params):
+ """
+ 签名的方法, signature for the private request.
+ :param params: request parameters
+ :return:
+ """
+
+ query_string = self.build_parameters(params)
+ hex_digest = hmac.new(self.secret.encode('utf8'), query_string.encode("utf-8"), hashlib.sha256).hexdigest()
+ return query_string + '&signature=' + str(hex_digest)
+
+ def place_order(self, symbol: str, order_side: OrderSide, order_type: OrderType, quantity: float, price: float,
+ client_order_id: str = None, time_inforce="GTC", stop_price=0):
+ """
+
+ :param symbol: 交易对名称
+ :param order_side: 买或者卖, BUY or SELL
+ :param order_type: 订单类型 LIMIT or other order type.
+ :param quantity: 数量
+ :param price: 价格.
+ :param client_order_id: 用户的订单ID
+ :param time_inforce:
+ :param stop_price:
+ :return:
+ """
+
+ path = '/api/v3/order'
+
+ if client_order_id is None:
+ client_order_id = self.get_client_order_id()
+
+
+
+ params = {
+ "symbol": symbol,
+ "side": order_side.value,
+ "type": order_type.value,
+ "quantity": quantity,
+ "price": price,
+ "recvWindow": self.recv_window,
+ "timestamp": utility.get_current_timestamp(),
+ "newClientOrderId": client_order_id
+ }
+
+
+ if order_type == OrderType.LIMIT:
+ params['timeInForce'] = time_inforce
+
+ if order_type == OrderType.MARKET:
+ if params.get('price'):
+ del params['price']
+
+ if order_type == OrderType.STOP:
+ if stop_price > 0:
+ params["stopPrice"] = stop_price
+ else:
+ raise ValueError("stopPrice must greater than 0")
+
+ return self.request(RequestMethod.POST, path=path, requery_dict=params, verify=True)
+
+ def get_order(self, symbol: str, client_order_id: str):
+ """
+ 获取订单状态.
+ :param symbol:
+ :param client_order_id:
+ :return:
+ """
+ path = "/api/v3/order"
+ prams = {"symbol": symbol, "timestamp": utility.get_current_timestamp(), "origClientOrderId": client_order_id}
+
+ return self.request(RequestMethod.GET, path, prams, verify=True)
+
+ def cancel_order(self, symbol, client_order_id):
+ """
+ 撤销订单.
+ :param symbol:
+ :param client_order_id:
+ :return:
+ """
+ path = "/api/v3/order"
+ params = {"symbol": symbol, "timestamp": utility.get_current_timestamp(),
+ "origClientOrderId": client_order_id
+ }
+
+ for i in range(0, 3):
+ try:
+ order = self.request(RequestMethod.DELETE, path, params, verify=True)
+ return order
+ except Exception as error:
+ print(f'取消订单失败:{error}')
+ return
+
+ def get_open_orders(self, symbol=None):
+ """
+ 获取所有的订单.
+ :param symbol: BNBUSDT, or BTCUSDT etc.
+ :return:
+ """
+ path = "/api/v3/openOrders"
+
+ params = {"timestamp": utility.get_current_timestamp()}
+ if symbol:
+ params["symbol"] = symbol
+
+ return self.request(RequestMethod.GET, path, params, verify=True)
+
+ def cancel_open_orders(self, symbol):
+ """
+ 撤销某个交易对的所有挂单
+ :param symbol: symbol
+ :return: return a list of orders.
+ """
+ path = "/api/v3/openOrders"
+
+ params = {"timestamp": utility.get_current_timestamp(),
+ "recvWindow": self.recv_window,
+ "symbol": symbol
+ }
+
+ return self.request(RequestMethod.DELETE, path, params, verify=True)
+
+ def get_account_info(self):
+ """
+ {'feeTier': 2, 'canTrade': True, 'canDeposit': True, 'canWithdraw': True, 'updateTime': 0, 'totalInitialMargin': '0.00000000',
+ 'totalMaintMargin': '0.00000000', 'totalWalletBalance': '530.21334791', 'totalUnrealizedProfit': '0.00000000',
+ 'totalMarginBalance': '530.21334791', 'totalPositionInitialMargin': '0.00000000', 'totalOpenOrderInitialMargin': '0.00000000',
+ 'maxWithdrawAmount': '530.2133479100000', 'assets':
+ [{'asset': 'USDT', 'walletBalance': '530.21334791', 'unrealizedProfit': '0.00000000', 'marginBalance': '530.21334791',
+ 'maintMargin': '0.00000000', 'initialMargin': '0.00000000', 'positionInitialMargin': '0.00000000', 'openOrderInitialMargin': '0.00000000',
+ 'maxWithdrawAmount': '530.2133479100000'}]}
+ :return:
+ """
+ path = "/api/v3/account"
+ params = {"timestamp": utility.get_current_timestamp(),
+ "recvWindow": self.recv_window
+ }
+ return self.request(RequestMethod.GET, path, params, verify=True)
diff --git a/binance/binance_trader.py b/binance/binance_trader.py
new file mode 100644
index 0000000..009ed8f
--- /dev/null
+++ b/binance/binance_trader.py
@@ -0,0 +1,434 @@
+from binance import BinanceSpotHttp, OrderStatus, OrderType, OrderSide
+from utils import config
+from utils import round_to
+from utils import utility
+
+
+class BinanceTrader(object):
+
+ def __init__(self):
+ """
+ :param api_key:
+ :param secret:
+ :param trade_type: 交易的类型, only support future and spot.
+ """
+ self.loop_counts = 0
+ self.config_error_counts = 0 #止损价配置不当,提示次数
+ self.sleep_time = config.sleep_time #循环间隔秒数
+ self.cancel_time = config.cancel_time #取消订单周期
+ self.http_client = BinanceSpotHttp(api_key=config.api_key, secret=config.api_secret, proxy_host=config.proxy_host, proxy_port=config.proxy_port)
+
+ self.buy_orders = [] # 本地买单列表.
+ self.sell_orders = [] # 本地卖单列表.
+ self.deficit_scroll_map = {} # 滚动止盈的清仓价
+
+
+ def get_avg_price_5min(self):
+ #获取5分钟内的平均价
+ ticker = self.http_client.get_avg_price_5min(config.symbol)
+ price = 0
+ if ticker:
+ price = float(ticker.get('price', 0))
+ return price
+
+ def get_latest_price(self):
+ #获取最新价格
+ ticker = self.http_client.get_latest_price(config.symbol)
+ price = 0
+ if ticker:
+ price = float(ticker.get('price', 0))
+ return price
+
+ def get_price_change_percent_24h(self):
+ #获取24h内的价格变化百分比数据
+ ticker = self.http_client.get_price_info_24h(config.symbol)
+ priceChangePercent = 0
+ if ticker:
+ priceChangePercent = float(ticker.get('priceChangePercent', 0))
+ return priceChangePercent
+
+ def get_bid_ask_price(self):
+ #最高买价 最低卖价
+ ticker = self.http_client.get_ticker(config.symbol)
+
+ bid_price = 0
+ ask_price = 0
+ if ticker:
+ bid_price = float(ticker.get('bidPrice', 0))
+ ask_price = float(ticker.get('askPrice', 0))
+
+ return bid_price, ask_price
+
+ def get_depth(self,limit):
+ ticker = self.http_client.get_order_book(config.symbol,limit=limit)
+ if ticker:
+ #high_price = float(ticker[0][2])
+ print(ticker)
+ return 123
+
+
+ def get_kline(self,interval):
+ ticker = self.http_client.get_kline(config.symbol,interval=interval,limit=1)
+ if ticker:
+ high_price = float(ticker[0][2])
+ low_price = float(ticker[0][3])
+ return low_price, high_price
+
+ # 获取倒数第二个k线数据
+ def get_kline_pre4(self,interval):
+ ticker = self.http_client.get_kline(config.symbol,interval=interval,limit=4)
+ if ticker:
+ high_price = float(ticker[3][2])
+ low_price = float(ticker[3][3])
+ return low_price, high_price
+
+ def create_buy_order(self,quantity,buy_price):
+ new_buy_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.BUY,
+ order_type=OrderType.LIMIT, quantity=quantity,
+ price=buy_price)
+ if new_buy_order:
+ # 买单维护到买单列表待处理
+ self.buy_orders.append(new_buy_order)
+ print(f"买单创建 价格: {buy_price}, 数量: {quantity} ")
+ else:
+ print(f"买单创建失败 价格: {buy_price}, 数量: {quantity} ")
+
+ def create_sell_order(self,quantity,sell_price):
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
+ order_type=OrderType.LIMIT, quantity=quantity, price=sell_price)
+ if new_sell_order:
+ # 放到卖单列表待处理
+ self.sell_orders.append(new_sell_order)
+ print(f"卖单创建 价格: {sell_price}, 数量: {quantity} ")
+ else:
+ print(f"卖单创建失败 价格: {sell_price}, 数量: {quantity} ")
+
+ def create_sell_order_by_client_id(self,quantity,sell_price,client_order_id):
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
+ order_type=OrderType.LIMIT, quantity=quantity, price=sell_price,client_order_id=client_order_id)
+ if new_sell_order is not None:
+ # 放到卖单列表待处理
+ self.sell_orders.append(new_sell_order)
+ print(f"卖单创建 价格: {sell_price}, 数量: {quantity} ")
+ return True
+ else:
+ print(f"卖单创建失败 价格: {sell_price}, 数量: {quantity} ")
+ return False
+
+ def create_sell_order_market(self,quantity):
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
+ order_type=OrderType.MARKET, quantity=quantity)
+ if new_sell_order:
+ # 市价订单必定成功
+ self.sell_orders.append(new_sell_order)
+ print(f"交易对:{config.symbol} 市价卖出数量: {quantity} ")
+ return True
+ else:
+ print(f"交易对:{config.symbol} 卖单创建失败 市价卖出数量: {quantity} ")
+ return False
+
+ def cancel_single_order(self,cancel_order):
+ order = self.http_client.cancel_order(config.symbol,cancel_order.get('clientOrderId'))
+ if order is None:
+ utility.alert(f"订单撤销失败 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
+ return False
+ else:
+ print(f"订单撤销成功 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
+ return True
+
+
+ def cancel_open_orders(self):
+ open_orders_list = self.http_client.get_open_orders(config.symbol)
+ for open_order in open_orders_list:
+ if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.BUY.value:
+ #只取消本客户端创建的买单
+ if open_order.get('clientOrderId') and config.symbol in open_order.get('clientOrderId'):
+ self.cancel_single_order(open_order)
+ print(f"买单{open_order.get('clientOrderId')}已取消取消")
+
+ def sync_open_orders(self):
+ open_orders_list = self.http_client.get_open_orders(config.symbol)
+ for open_order in open_orders_list:
+ #只同步本客户端创建的订单
+ if open_order.get('clientOrderId') and config.symbol in open_order.get('clientOrderId'):
+ if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.BUY.value:
+ self.buy_orders.append(open_order)
+ print(f"买单{open_order.get('clientOrderId')}同步到本地")
+ if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.SELL.value:
+ self.sell_orders.append(open_order)
+ print(f"卖单{open_order.get('clientOrderId')}同步到本地")
+
+ #网格交易核心逻辑.
+ def grid_trader(self):
+ """
+ #网格交易核心逻辑.
+ """
+
+ quantity = round_to(float(config.quantity), float(config.min_qty)) ## quantity是config.quantity到config.min_qty的精度转换
+ profit_scroll = float(config.profit_scroll) ## 每次刷新止盈价的比例
+ deficit_scroll = float(config.deficit_scroll) ## 每次刷新止损价的比例
+ profit_intend = float(config.profit_intend) ## 每次准备刷新止盈价的比例,到了这个比例直接刷
+ gap_percent= float(config.gap_percent) ## 卖单提价比例
+
+ self.loop_counts+=1
+ print(f"{utility.cn_time()} 第{self.loop_counts}轮循环开始")
+
+ bid_price, ask_price = self.get_bid_ask_price()
+ print(f"{config.symbol}:最高买价bid_price: {bid_price}, 最低卖价ask_price: {ask_price}")
+ avg_price_5min = float(self.get_avg_price_5min())
+ #latest_price = self.get_latest_price()
+
+ #5分钟k线 15分钟k线
+ low_price_in_15m, high_price_in_15m = self.get_kline("15m")
+ print(f"{config.symbol}:15分钟最低价: {low_price_in_15m}, 15分钟最高价: {high_price_in_15m}")
+
+ low_price_in_4h, high_price_in_4h = self.get_kline("4h")
+ low_price_pre_in_4h, high_price_pre_in_4h = self.get_kline_pre4("2h")
+ print(f"low_price_in_4h: {low_price_in_4h}")
+ print(f"high_price_in_4h: {high_price_in_4h}")
+ print(f"low_price_pre_in_4h: {low_price_pre_in_4h}")
+ print(f"high_price_pre_in_4h: {high_price_pre_in_4h}")
+ # 取波峰波谷的中间值
+ peak_price = high_price_in_4h if high_price_in_4h <= high_price_pre_in_4h else high_price_pre_in_4h
+ bottom_price = low_price_in_4h if low_price_in_4h >= low_price_pre_in_4h else low_price_pre_in_4h
+ delta_price_in_4h = float(peak_price)-float(bottom_price)
+ price_stop_to_sell = float(bottom_price) + delta_price_in_4h * 0.1
+ price_stop_to_buy = float(peak_price) - delta_price_in_4h * 0.8
+ print(f"high_price_in_4h_4h: {peak_price}")
+ print(f"low_price_in_4h_4h: {bottom_price}")
+ print(f"价格上限: {price_stop_to_buy}")
+ print(f"价格下限: {price_stop_to_sell}")
+ # price_stop_to_sell = float(config.price_stop_to_sell)
+ # price_stop_to_buy = float(config.price_stop_to_buy)
+
+
+
+ self.buy_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
+ self.sell_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
+
+
+ buy_delete_orders = [] # 需要删除的买单
+ sell_delete_orders = [] # 需要删除的卖单
+
+
+
+ # 买单逻辑,检查买单列表成交的情况.一旦成功立即加价卖
+ print("-------------买单逻辑-----------------")
+ for buy_order in self.buy_orders:
+ check_order = self.http_client.get_order(buy_order.get('symbol', config.symbol),client_order_id=buy_order.get('clientOrderId'))
+ if check_order:
+ order_status = check_order.get('status')
+ order_price = check_order.get('price')
+ order_orig_qty = check_order.get('origQty')
+ order_client_id = buy_order.get('clientOrderId')
+
+ #取消状态的买单 不管
+ if order_status == OrderStatus.CANCELED.value:
+ buy_delete_orders.append(buy_order)
+ print(f"买单{order_client_id}状态为取消: {order_status}")
+
+ #成交状态买单,需要处理
+ elif order_status == OrderStatus.FILLED.value:
+ print(f"成交状态的买单: {order_client_id} 成交时间: {utility.cn_time()} 价格: {order_price} 数量: {order_orig_qty}")
+ # 买单成交后,提高价格,挂盈利卖单
+ sell_price = round_to(float(order_price) * (1 + float(config.gap_percent)), float(config.min_price))
+ if 0 < sell_price < ask_price:
+ # 防止价格小于最低卖价ask_price
+ sell_price = round_to(ask_price, float(config.min_price))
+ #如果卖价小于设定的持仓价格price_stop_to_sell,将卖价提高至持仓价,才交易
+ if 0 < sell_price < price_stop_to_sell:
+ print(f"卖价{sell_price} 小于持仓价{price_stop_to_sell} ,已调整为持仓价交易")
+ sell_price = price_stop_to_sell
+ #盈利卖单挂
+ if self.create_sell_order(quantity, sell_price):
+ print(f"{utility.cn_time()} :交易对:{config.symbol}, 买单 {order_client_id} 成交, 价格: {order_price}, 数量: {order_orig_qty}")
+ # 盈利卖单挂成功的话,删掉本买单
+ buy_delete_orders.append(buy_order)
+ if buy_order in self.buy_orders:
+ self.buy_orders.remove(buy_order)
+
+
+
+ #未成交的买单不处理
+ elif order_status == OrderStatus.NEW.value:
+ print(f"未成交的买单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+ # 部分成交的买单 提示下就行了
+ elif order_status == OrderStatus.PARTIALLY_FILLED.value:
+ print(f"部分成交的买单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+ #异常状态买单
+ else:
+ utility.alert(f"交易对:{config.symbol}, 时间: {utility.cn_time()}, 买单{order_client_id}失败{order_status}")
+
+ # 过期或者拒绝的订单删除掉.
+ for delete_order in buy_delete_orders:
+ if delete_order in self.buy_orders:
+ self.buy_orders.remove(delete_order)
+
+
+ # 卖单逻辑, 检查卖单成交情况
+ print("--------------卖单逻辑----------------")
+ for sell_order in self.sell_orders:
+ check_order = self.http_client.get_order(sell_order.get('symbol', config.symbol),client_order_id=sell_order.get('clientOrderId'))
+ if check_order:
+ order_status = check_order.get('status')
+ order_price = check_order.get('price')
+ order_orig_qty = check_order.get('origQty')
+ order_client_id = sell_order.get('clientOrderId')
+ #卖单状态为取消 不管
+ if order_status == OrderStatus.CANCELED.value:
+ sell_delete_orders.append(sell_order)
+ print(f"卖单{order_client_id}状态为取消: {order_status}")
+
+ #卖单状态为成交
+ elif order_status == OrderStatus.FILLED.value:
+ print(f"卖单{order_client_id}成交时间: {utility.cn_time()}, 价格: {order_price}, 数量: {order_orig_qty}")
+ #第一遍才发通知
+ if sell_order not in sell_delete_orders:
+ utility.alert(f"卖单成交: {utility.cn_time()} {order_client_id}, 交易对:{config.symbol}, 价格: {order_price}, 数量: {order_orig_qty}")
+ # 卖单成交,不再维护该卖单状态
+ sell_delete_orders.append(sell_order)
+ if sell_order in self.sell_orders:
+ self.sell_orders.remove(sell_order)
+
+
+ # 新挂的卖单 创建移动止盈策略
+ elif order_status == OrderStatus.NEW.value:
+ print(f"还没成交的卖单:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+ print(f"当前最低卖价: {ask_price}")
+ orig_buy_price = float(order_price) / (float(gap_percent) + 1)
+ print(f"预估本单的买入价格: {orig_buy_price}")
+ triger_price = float(order_price) * float(profit_intend)
+ print(f"移动止盈触发价格: {triger_price}")
+ if ask_price >= triger_price:
+ print("最低卖价大于移动止盈触发价格,先删除本卖单,再创建一个更高价的卖单")
+ if self.cancel_single_order(check_order):
+ # 删除成功 再创建一个更高价的卖单
+ new_order_price_profit = float(order_price) * float(profit_scroll)
+ new_order_price_deficit = float(ask_price) * float(deficit_scroll)
+ new_order_price_profit = round_to(new_order_price_profit, float(config.min_price))
+ new_order_price_deficit = round_to(new_order_price_deficit, float(config.min_price))
+ print(f"new_order_price_profit: {new_order_price_profit}")
+ print(f"new_order_price_deficit: {new_order_price_deficit}")
+ profit_client_order_id = self.http_client.get_client_order_id()
+ profit_sell_order = self.create_sell_order_by_client_id(order_orig_qty,
+ new_order_price_profit,
+ profit_client_order_id)
+ if profit_sell_order:
+ print("----------------移动止盈触发成功-------yyyyyyyyyyyyyyyyyyyyyyyyyyyy------")
+ print(
+ f"{utility.cn_time()} :卖单 {order_client_id}滚动止盈触发新卖单{profit_client_order_id},交易对:{config.symbol},新期望价格: {new_order_price_profit}, 数量: {order_orig_qty}")
+ # 移动止盈卖单挂成功的话,本地不再维护原卖单
+ sell_delete_orders.append(sell_order)
+ if sell_order in self.sell_orders:
+ self.sell_orders.remove(sell_order)
+ # 本地记录新卖单的止损价格
+ self.deficit_scroll_map[profit_client_order_id] = new_order_price_deficit
+ else:
+ print("----------------移动止盈触发失败------eeeeeeeeeeeeeeeeeeeeeeeeeeee-------")
+ print(
+ f"{utility.cn_time()} 移动止盈卖单创建失败 交易对:{config.symbol} 价格: {new_order_price_profit}, 数量: {order_orig_qty} ")
+ utility.alert(
+ f"{utility.cn_time()} 移动止盈卖单创建失败 交易对:{config.symbol} 价格: {new_order_price_profit}, 数量: {order_orig_qty} ")
+ else:
+ print("原买单删除失败")
+
+ # 当前卖单存在止损价,且最低卖价不大于止损价时候,止损处理掉当前卖单
+ # 其实所存的止损价是经过滚动止盈产生的,所以还是在盈利
+ # 当前卖价要大于买单价格才可以
+ elif order_client_id in self.deficit_scroll_map and ask_price <= self.deficit_scroll_map[order_client_id] and ask_price > orig_buy_price:
+ # 先删除本卖单,挂太高了,卖不出去
+ if self.cancel_open_orders(check_order):
+ # 市价卖出本订单待卖的币
+ self.create_sell_order_market(order_orig_qty)
+ print("----------移动止损成功--------------")
+ else:
+ print(f"止损卖单取消失败:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+
+
+ # 部分成交的卖单 提示下就行了
+ elif order_status == OrderStatus.PARTIALLY_FILLED.value:
+ print(f"部分成交的卖单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+ else:
+ utility.alert(f"挂卖单失败 交易对:{config.symbol}, 时间: {utility.cn_time()}, {order_status}")
+
+ #处理过期或者拒绝的订单,不在维护到本地
+ for delete_order in sell_delete_orders:
+ if delete_order in self.sell_orders:
+ self.sell_orders.remove(delete_order)
+
+ # 买单数量未满 并且卖单也没满时候 usdt没有积压 可以新建买单
+ if len(self.buy_orders) < int(config.max_orders) and len(self.sell_orders) < int(config.max_sell_orders):
+ print("买单数量未满,卖单也没积压,开始创建一个新买单:")
+ print("------------------------------")
+ if bid_price > 0:
+ buy_price = round_to(bid_price * (1 - float(config.gap_percent)), float(config.min_price))
+ ##如果买价大于设定的抛仓价格price_stop_to_buy,将买价降低至抛仓价,才交易
+ if buy_price > price_stop_to_buy > 0:
+ buy_price = price_stop_to_buy
+ #动态价格保护,如果买价不高于15分钟k线最高值,说明价格在跌,适合买
+ if buy_price <= high_price_in_15m :
+ # 如果价格不大于5分钟内的平均价 说明价格还可以,能继续买入
+ if buy_price <= avg_price_5min:
+ buy_price = round_to(buy_price,float(config.min_price))
+ print(f"开始创建买单:价格{buy_price} 数量 {quantity} ")
+ self.create_buy_order(quantity,buy_price)
+ else:
+ print(f"买价 {buy_price}大于平均价 {avg_price_5min} 再等等")
+ else:
+ print(f"买价 {buy_price}大于15分钟最高价 {high_price_in_15m} 高位勿买")
+
+ # 买单数量过多,cancel_time 取消一次价格最离谱的买单
+ elif len(self.buy_orders) >= int(config.max_orders):
+ # cancel_time更新一次 主循环cancel_time/gcd_time次
+ cancel_period=self.cancel_time/self.sleep_time
+ print(f"当前买单数量达到{config.max_orders},将在{(cancel_period - self.loop_counts % cancel_period)*self.sleep_time}秒后删除一个价格最离谱的买单")
+ if self.loop_counts % cancel_period == 0:
+ print(f"开始删除价格最离谱的买单:")
+ print("------------------------------")
+ # 最离谱到最接近
+ self.buy_orders.sort(key=lambda x: abs(float(x['price'])-float(avg_price_5min)), reverse=True)
+ impossible_order=self.buy_orders[0]
+ if self.cancel_single_order(impossible_order):
+ if impossible_order in self.buy_orders:
+ self.buy_orders.remove(impossible_order)
+ buy_delete_orders.append(impossible_order)
+ print(f"买单列表已满 价格离谱的买单{impossible_order.get('clientOrderId')}删除 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
+ else:
+ print(f"买单列表已满 价格离谱的买单{impossible_order.get('clientOrderId')}删除失败 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
+ for fade_order in self.buy_orders:
+ if float(fade_order.get('price')) > high_price_in_15m or float(fade_order.get('price')) < low_price_in_15m:
+ if fade_order in self.buy_orders:
+ self.buy_orders.remove(fade_order)
+ buy_delete_orders.append(fade_order)
+ print(f"取消价格未在15m k线的买单{fade_order.get('clientOrderId')} 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
+
+
+
+ # 没有卖单的时候 暂时先不创建卖单,防止利润计算错误
+ if len(self.sell_orders) <= 0:
+ print(f"当前没有卖单,请注意查看{config.symbol}余额")
+ print("------------------------------")
+
+
+
+ #卖单过多,暂不处理
+ elif len(self.sell_orders) > int(config.max_orders):
+ print("卖单过多:")
+ print("------------------------------")
+ print(f"交易对{config.symbol}卖单较多,大于个{config.max_orders} 请处理")
+
+ #集中处理 buy_delete_orders sell_delete_orders
+ for buy_order in buy_delete_orders:
+ if self.cancel_single_order(buy_order):
+ if buy_order in buy_delete_orders:
+ buy_delete_orders.remove(buy_order)
+ for sell_order in sell_delete_orders:
+ if self.cancel_single_order(sell_order):
+ if sell_order in sell_delete_orders:
+ sell_delete_orders.remove(sell_order)
+
+ print(f"{utility.cn_time()} 第{self.loop_counts}轮循环结束 休息{self.sleep_time}s")
\ No newline at end of file
diff --git a/binance/trade.txt b/binance/trade.txt
new file mode 100644
index 0000000..de30dda
--- /dev/null
+++ b/binance/trade.txt
@@ -0,0 +1,12 @@
+
+ #循环提醒配置不当
+ ##循环配置不当的逻辑先去掉,直接从4h kline自适应
+ # if avg_price_5min > price_stop_to_buy or avg_price_5min < price_stop_to_sell:
+ # print(f"交易对{config.symbol}:止损范围配置不当,恐难以成交,五分钟内平均价: {avg_price_5min}")
+ # self.config_error_counts+=1
+ # #15次循环提醒一次配置不当
+ # if self.config_error_counts%15 == 0:
+ # self.config_error_counts = 0
+ # utility.alert(f"交易对{config.symbol}:止损范围配置不当,恐难以成交,五分钟内平均价: {avg_price_5min}")
+ # else:
+ # self.config_error_counts = 0
diff --git a/binance/trader_bak b/binance/trader_bak
new file mode 100644
index 0000000..22e5613
--- /dev/null
+++ b/binance/trader_bak
@@ -0,0 +1,404 @@
+from binance import BinanceSpotHttp, OrderStatus, OrderType, OrderSide
+from utils import config
+from utils import round_to
+from utils import utility
+
+
+class BinanceTrader(object):
+
+ def __init__(self):
+ """
+ :param api_key:
+ :param secret:
+ :param trade_type: 交易的类型, only support future and spot.
+ """
+ self.loop_counts = 0
+ self.config_error_counts = 0 #止损价配置不当,提示次数
+ self.http_client = BinanceSpotHttp(api_key=config.api_key, secret=config.api_secret, proxy_host=config.proxy_host, proxy_port=config.proxy_port)
+
+ self.buy_orders = [] # 本地买单列表.
+ self.sell_orders = [] # 本地卖单列表.
+
+
+
+
+ def get_avg_price_5min(self):
+ #获取5分钟内的平均价
+ ticker = self.http_client.get_avg_price_5min(config.symbol)
+ price = 0
+ if ticker:
+ price = float(ticker.get('price', 0))
+ return price
+
+ def get_latest_price(self):
+ #获取最新价格
+ ticker = self.http_client.get_latest_price(config.symbol)
+ price = 0
+ if ticker:
+ price = float(ticker.get('price', 0))
+ return price
+
+ def get_price_change_percent_24h(self):
+ #获取24h内的价格变化百分比数据
+ ticker = self.http_client.get_price_info_24h(config.symbol)
+ priceChangePercent = 0
+ if ticker:
+ priceChangePercent = float(ticker.get('priceChangePercent', 0))
+ return priceChangePercent
+
+ def get_bid_ask_price(self):
+ #最高买价 最低卖价
+ ticker = self.http_client.get_ticker(config.symbol)
+
+ bid_price = 0
+ ask_price = 0
+ if ticker:
+ bid_price = float(ticker.get('bidPrice', 0))
+ ask_price = float(ticker.get('askPrice', 0))
+
+ return bid_price, ask_price
+
+ def get_depth(self,limit):
+ ticker = self.http_client.get_order_book(config.symbol,limit=limit)
+ if ticker:
+ #high_price = float(ticker[0][2])
+ print(ticker)
+ return 123
+
+ def get_kline_15m(self):
+ ticker = self.http_client.get_kline(config.symbol,interval="15m",limit=1)
+ if ticker:
+ high_price = float(ticker[0][2])
+ low_price = float(ticker[0][3])
+ return low_price, high_price
+
+ def get_kline_5m(self):
+ ticker = self.http_client.get_kline(config.symbol,interval="5m",limit=1)
+ if ticker:
+ high_price = float(ticker[0][2])
+ low_price = float(ticker[0][3])
+ return low_price, high_price
+
+ def create_buy_order(self,quantity,buy_price):
+ new_buy_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.BUY,
+ order_type=OrderType.LIMIT, quantity=quantity,
+ price=buy_price)
+ if new_buy_order:
+ # 买单维护到买单列表待处理
+ self.buy_orders.append(new_buy_order)
+ print(f"买单创建 价格: {buy_price}, 数量: {quantity} ")
+ else:
+ print(f"买单创建失败 价格: {buy_price}, 数量: {quantity} ")
+
+ def create_buy_order_by_client_id(self,quantity,buy_price,client_order_id):
+ new_buy_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.BUY,
+ order_type=OrderType.LIMIT, quantity=quantity,
+ price=buy_price,client_order_id=client_order_id)
+ if new_buy_order:
+ # 买单维护到买单列表待处理
+ self.buy_orders.append(new_buy_order)
+ print(f"买单创建 价格: {buy_price}, 数量: {quantity} ")
+ else:
+ print(f"买单创建失败 价格: {buy_price}, 数量: {quantity} ")
+
+ def create_sell_order(self,quantity,sell_price):
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
+ order_type=OrderType.LIMIT, quantity=quantity, price=sell_price)
+ if new_sell_order:
+ # 放到卖单列表待处理
+ self.sell_orders.append(new_sell_order)
+ print(f"卖单创建 价格: {sell_price}, 数量: {quantity} ")
+ else:
+ print(f"卖单创建失败 价格: {sell_price}, 数量: {quantity} ")
+
+ def create_sell_order_by_client_id(self,quantity,sell_price,client_order_id):
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
+ order_type=OrderType.LIMIT, quantity=quantity, price=sell_price,client_order_id=client_order_id)
+ if new_sell_order:
+ # 放到卖单列表待处理
+ self.sell_orders.append(new_sell_order)
+ print(f"卖单创建 价格: {sell_price}, 数量: {quantity} ")
+ else:
+ print(f"卖单创建失败 价格: {sell_price}, 数量: {quantity} ")
+
+ def create_sell_order_market(self,quantity):
+ new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
+ order_type=OrderType.MARKET, quantity=quantity)
+ if new_sell_order:
+ # 市价订单必定成功
+ self.sell_orders.append(new_sell_order)
+ print(f"交易对:{config.symbol} 市价卖出数量: {quantity} ")
+ else:
+ print(f"交易对:{config.symbol} 卖单创建失败 市价卖出数量: {quantity} ")
+
+ def cancel_single_order(self,cancel_order):
+ order = self.http_client.cancel_order(config.symbol,cancel_order.get('clientOrderId'))
+ if order:
+ print(f"订单撤销 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
+ else:
+ print(f"订单撤销失败 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
+
+ def cancel_open_orders(self):
+ open_orders_list = self.http_client.get_open_orders(config.symbol)
+ for open_order in open_orders_list:
+ if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.BUY.value:
+ #只取消本客户端创建的买单
+ if open_order.get('clientOrderId') and config.symbol in open_order.get('clientOrderId'):
+ self.cancel_single_order(open_order)
+ print(f"买单{open_order.get('clientOrderId')}已取消取消")
+
+ def sync_open_orders(self):
+ open_orders_list = self.http_client.get_open_orders(config.symbol)
+ for open_order in open_orders_list:
+ #只同步本客户端创建的订单
+ if open_order.get('clientOrderId') and config.symbol in open_order.get('clientOrderId'):
+ if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.BUY.value:
+ self.buy_orders.append(open_order)
+ print(f"买单{open_order.get('clientOrderId')}同步到本地")
+ if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.SELL.value:
+ self.sell_orders.append(open_order)
+ print(f"卖单{open_order.get('clientOrderId')}同步到本地")
+
+ #网格交易核心逻辑.
+ def grid_trader(self):
+ """
+ #网格交易核心逻辑.
+ """
+ price_stop_to_sell = float(config.price_stop_to_sell)
+ price_stop_to_buy = float(config.price_stop_to_buy)
+ quantity = round_to(float(config.quantity), float(config.min_qty)) ## quantity是config.quantity到config.min_qty的四舍五入值
+ profit_scroll = float(config.profit_scroll) ## 每次刷新止盈价的比例
+ deficit_scroll = float(config.deficit_scroll) ## 每次刷新止损价的比例
+ profit_intend = float(config.profit_intend) ## 每次准备刷新止盈价的比例,到了这个比例直接刷
+
+ self.loop_counts+=1
+ print(f"{utility.cn_time()} 第{self.loop_counts}轮循环开始")
+
+ bid_price, ask_price = self.get_bid_ask_price()
+ print(f"{config.symbol}:最高买价bid_price: {bid_price}, 最低卖价ask_price: {ask_price}")
+ avg_price_5min = float(self.get_avg_price_5min())
+ #latest_price = self.get_latest_price()
+
+ #5分钟k线 15分钟k线
+ low_price_in_15m, high_price_in_15m = self.get_kline_15m()
+ print(f"{config.symbol}:15分钟最低价: {low_price_in_15m}, 15分钟最高价: {high_price_in_15m}")
+
+ # low_price_in_5m, high_price_in_5m = self.get_kline_5m()
+ # print(f"{config.symbol}:5分钟最低价: {low_price_in_5m}, 5分钟最高价: {high_price_in_5m}")
+
+ #循环提醒配置不当
+ if avg_price_5min > price_stop_to_buy or avg_price_5min < price_stop_to_sell:
+ print(f"交易对{config.symbol}:止损范围配置不当,恐难以成交,五分钟内平均价: {avg_price_5min}")
+ self.config_error_counts+=1
+ #15次循环提醒一次配置不当
+ if self.config_error_counts%15 == 0:
+ self.config_error_counts = 0
+ utility.alert(f"交易对{config.symbol}:止损范围配置不当,恐难以成交,五分钟内平均价: {avg_price_5min}")
+ else:
+ self.config_error_counts = 0
+
+
+ self.buy_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
+ self.sell_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
+
+
+ buy_delete_orders = [] # 需要删除的买单
+ sell_delete_orders = [] # 需要删除的卖单
+ deficit_scroll_map = {} # 滚动止盈的清仓价
+ buy_order_price_map = {} # 买单的价格
+ buy_order_sell_map = {} # 买卖单对应关系
+
+
+ # 买单逻辑,检查买单列表成交的情况.一旦成功立即加价卖
+ # print("当前委托买单:")
+ # print(self.buy_orders)
+ print("-------------买单逻辑-----------------")
+ for buy_order in self.buy_orders:
+ check_order = self.http_client.get_order(buy_order.get('symbol', config.symbol),client_order_id=buy_order.get('clientOrderId'))
+ if check_order:
+ order_status = check_order.get('status')
+ order_price = check_order.get('price')
+ order_orig_qty = check_order.get('origQty')
+ order_client_id = buy_order.get('clientOrderId')
+
+ #取消状态的买单 不管
+ if order_status == OrderStatus.CANCELED.value:
+ buy_delete_orders.append(buy_order)
+ print(f"买单{order_client_id}状态为取消: {order_status}")
+
+ #成交状态买单,需要处理
+ elif order_status == OrderStatus.FILLED.value:
+ print(f"买单{order_client_id}成交时间: {utility.cn_time()}, 价格: {order_price}, 数量: {order_orig_qty}")
+ # 买单成交后,提高价格,挂盈利卖单
+ sell_price = round_to(float(order_price) * (1 + float(config.gap_percent)), float(config.min_price))
+ if 0 < sell_price < ask_price:
+ # 防止价格小于最低卖价ask_price
+ sell_price = round_to(ask_price, float(config.min_price))
+ #如果卖价小于设定的持仓价格price_stop_to_sell,将卖价提高至持仓价,才交易
+ if 0 < sell_price < price_stop_to_sell:
+ print(f"卖价{sell_price} 小于持仓价{price_stop_to_sell} ,已调整为持仓价交易")
+ sell_price = price_stop_to_sell
+ #盈利卖单挂
+ profit_client_order_id = BinanceSpotHttp.get_client_order_id()
+ if self.create_sell_order_by_client_id(quantity, sell_price,profit_client_order_id):
+ print(f"{utility.cn_time()} :交易对:{config.symbol}, 买单 {order_client_id} 成交, 价格: {order_price}, 数量: {order_orig_qty}")
+ # 盈利卖单挂成功的话,删掉本买单
+ buy_delete_orders.append(buy_order)
+ self.buy_orders.remove(buy_order)
+ # 维护买卖单关系
+ buy_order_sell_map[order_client_id] = profit_client_order_id
+
+
+
+ #未成交的买单不处理
+ elif order_status == OrderStatus.NEW.value:
+ print(f"未成交的买单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+ # 部分成交的买单 提示下就行了
+ elif order_status == OrderStatus.PARTIALLY_FILLED.value:
+ print(f"部分成交的买单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+ #异常状态买单
+ else:
+ utility.alert(f"交易对:{config.symbol}, 时间: {utility.cn_time()}, 买单{order_client_id}失败{order_status}")
+
+ # 过期或者拒绝的订单删除掉.
+ for delete_order in buy_delete_orders:
+ self.buy_orders.remove(delete_order)
+
+
+ # 卖单逻辑, 检查卖单成交情况.不做措施
+ # print("当前委托卖单:")
+ # print(self.sell_orders)
+ print("--------------卖单逻辑----------------")
+ for sell_order in self.sell_orders:
+ check_order = self.http_client.get_order(sell_order.get('symbol', config.symbol),client_order_id=sell_order.get('clientOrderId'))
+ if check_order:
+ order_status = check_order.get('status')
+ order_price = check_order.get('price')
+ order_orig_qty = check_order.get('origQty')
+ order_client_id = sell_order.get('clientOrderId')
+ #卖单状态为取消 不管
+ if order_status == OrderStatus.CANCELED.value:
+ sell_delete_orders.append(sell_order)
+ print(f"卖单{order_client_id}状态为取消: {order_status}")
+
+ #卖单状态为成交
+ elif order_status == OrderStatus.FILLED.value:
+ print(f"卖单{order_client_id}成交时间: {utility.cn_time()}, 价格: {order_price}, 数量: {order_orig_qty}")
+ #第一遍才发通知
+ if sell_order not in sell_delete_orders:
+ utility.alert(f"卖单成交: {utility.cn_time()} {order_client_id}, 交易对:{config.symbol}, 价格: {order_price}, 数量: {order_orig_qty}")
+ # 卖单成交,不再维护该卖单状态
+ sell_delete_orders.append(sell_order)
+ self.sell_orders.remove(sell_order)
+
+ # 新挂的卖单 创建移动止盈策略
+ elif order_status == OrderStatus.NEW.value:
+ print(f"还没成交的卖单:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+ print(f"当前最低卖价: {ask_price}")
+ #预估
+ # 最低卖价大于原卖单定价的止盈比例时候,先删除本卖单,再创建一个更高价的卖单
+ if ask_price >= order_price * profit_intend:
+ # 先删除本卖单
+ if self.cancel_open_orders(check_order):
+ # 再创建一个更高价的卖单
+ new_order_price_profit = order_price * profit_scroll
+ new_order_price_deficit = ask_price * deficit_scroll
+ profit_client_order_id = BinanceSpotHttp.get_client_order_id()
+ profit_sell_order = self.self.create_sell_order_by_client_id(order_orig_qty,new_order_price_profit,profit_client_order_id)
+ if profit_sell_order:
+ print(
+ f"{utility.cn_time()} :卖单 {order_client_id}滚动止盈触发新卖单{profit_client_order_id},交易对:{config.symbol},新期望价格: {new_order_price_profit}, 数量: {order_orig_qty}")
+ # 移动止盈卖单挂成功的话,本地不再维护原卖单
+ sell_delete_orders.append(sell_order)
+ self.sell_orders.remove(sell_order)
+ # 本地记录新卖单的止损价格
+ deficit_scroll_map[profit_client_order_id] = new_order_price_deficit
+ else:
+ print(f"{utility.cn_time()} 移动止盈卖单创建失败 交易对:{config.symbol} 价格: {new_order_price_profit}, 数量: {order_orig_qty} ")
+ utility.alert(f"{utility.cn_time()} 移动止盈卖单创建失败 交易对:{config.symbol} 价格: {new_order_price_profit}, 数量: {order_orig_qty} ")
+ else:
+ print(f"止盈卖单取消失败:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+ # 当前卖单存在止损价,且最低卖价不大于止损价时候,止损处理掉当前卖单
+ # 其实所存的止损价是经过滚动止盈产生的,所以还是在盈利
+ elif deficit_scroll_map.has_key(order_client_id) and ask_price <= deficit_scroll_map[order_client_id]:
+ # 先删除本卖单,挂太高了,卖不出去
+ if self.cancel_open_orders(check_order):
+ # 市价卖出本订单待卖的币
+ self.create_sell_order_market(order_orig_qty)
+ else:
+ print(f"止损卖单取消失败:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+
+
+ # 部分成交的卖单 提示下就行了
+ elif order_status == OrderStatus.PARTIALLY_FILLED.value:
+ print(f"部分成交的卖单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
+
+ else:
+ utility.alert(f"挂卖单失败 交易对:{config.symbol}, 时间: {utility.cn_time()}, {order_status}")
+
+ # 过期或者拒绝的订单删除掉.
+ for delete_order in sell_delete_orders:
+ self.sell_orders.remove(delete_order)
+
+ # 买单数量未满 并且卖单也没满时候 usdt没有积压 可以新建买单
+ if len(self.buy_orders) < int(config.max_orders) and len(self.sell_orders) < int(config.max_sell_orders):
+ print("买单数量未满,卖单也没积压,开始创建一个新买单:")
+ print("------------------------------")
+ if bid_price > 0:
+ buy_price = round_to(bid_price * (1 - float(config.gap_percent)), float(config.min_price))
+ ##如果买价大于设定的抛仓价格price_stop_to_buy,将买价降低至抛仓价,才交易
+ if buy_price > price_stop_to_buy > 0:
+ buy_price = price_stop_to_buy
+ #动态价格保护,如果买价不高于15分钟k线最高值,说明价格在跌,适合买
+ if buy_price <= high_price_in_15m :
+ # 如果价格不大于5分钟内的平均价 说明价格还可以,能继续买入
+ if buy_price <= avg_price_5min:
+ print(f"开始创建买单:价格{buy_price} 数量 {quantity} ")
+ buy_client_order_id = BinanceSpotHttp.get_client_order_id()
+ if self.create_buy_order_by_client_id(quantity,buy_price,buy_client_order_id):
+ # 买入成功后记录买单的原价
+ buy_order_price_map[buy_client_order_id] = buy_price
+ else:
+ print(f"买单{buy_client_order_id}创建失败 数量 {quantity} 价格{buy_price}")
+ else:
+ print(f"买价 {buy_price}大于平均价 {avg_price_5min} 再等等")
+ else:
+ print(f"买价 {buy_price}大于15分钟最高价 {high_price_in_15m} 高位勿买")
+
+ # 买单数量过多,2min取消一次价格最离谱的买单
+ elif len(self.buy_orders) >= int(config.max_orders):
+ # 主循环6次 2min更新一次
+ print(f"当前买单数量达到{config.max_orders},将在{(6 - self.loop_counts % 6)*20}秒后删除一个价格最离谱的买单")
+ if self.loop_counts % 6 == 0:
+ print(f"开始删除价格最离谱的买单:")
+ print("------------------------------")
+ # 最离谱到最接近
+ self.buy_orders.sort(key=lambda x: abs(float(x['price'])-float(avg_price_5min)), reverse=True)
+ impossible_order=self.buy_orders[0]
+ if self.cancel_single_order(impossible_order):
+ self.buy_orders.remove(impossible_order)
+ print(f"买单列表已满 价格离谱的买单{impossible_order.get('clientOrderId')}删除 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
+ for fade_order in self.buy_orders:
+ if float(fade_order.get('price')) > high_price_in_15m or float(fade_order.get('price')) < low_price_in_15m:
+ self.buy_orders.remove(fade_order)
+ print(f"取消价格未在15m k线的买单{fade_order.get('clientOrderId')} 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
+
+
+
+ # 没有卖单的时候 暂时先不创建卖单,防止利润计算错误
+ if len(self.sell_orders) <= 0:
+ print(f"当前没有卖单,请注意查看{config.symbol}余额")
+ print("------------------------------")
+
+
+
+ #卖单过多,暂不处理
+ elif len(self.sell_orders) > int(config.max_orders):
+ print("卖单过多:")
+ print("------------------------------")
+ print(f"交易对{config.symbol}卖单较多,大于个{config.max_orders} 请处理")
+
+ print(f"{utility.cn_time()} 第{self.loop_counts}轮循环结束 休息20s")
\ No newline at end of file
diff --git a/conf/config-example.json b/conf/config-example.json
new file mode 100644
index 0000000..c9fa5a3
--- /dev/null
+++ b/conf/config-example.json
@@ -0,0 +1,28 @@
+ {
+ "sleep_time": 10,
+ "cancel_time": 120,
+ "platform": "binance_spot",
+ "symbol": "SHIBUSDT",
+ "api_key": "44444444444444444444444444",
+ "api_secret": "4444444444444444444444444444444444444444444444444444",
+ "profit_scroll": 1.005,
+ "profit_intend": 0.996,
+ "deficit_scroll": 0.985,
+ "gap_percent": 0.01,
+ "quantity": 500000,
+ "min_price": 0.00000001,
+ "price_stop_to_buy": 0.7,
+ "price_stop_to_sell": 0.5,
+ "min_qty": 100000,
+ "max_orders": 2,
+ "max_sell_orders": 2,
+ "proxy_host": "privoxy",
+ "proxy_port": 8118,
+ "dingding_robot_url": "https://oapi.dingtalk.com/robot/send?access_token=666666666666666666666666666666666d29c",
+ "dingding_user_phone": "1111111111",
+ "user_email": "me@me.me",
+ "SMTP_HOST": "smtp.me.me",
+ "SMTP_PORT": 80,
+ "SMTP_USER": "me2@me.me",
+ "SMTP_PASSWORD": "77777777777777777777777777777777777"
+ }
diff --git a/conf_huobi/config-example.json b/conf_huobi/config-example.json
new file mode 100644
index 0000000..402d747
--- /dev/null
+++ b/conf_huobi/config-example.json
@@ -0,0 +1,11 @@
+{
+ "account_id": 44444,
+ "api_key": "e7f555555555555555555b3",
+ "api_secret": "f065555555555555555555555555555aec",
+ "symbol_spot": ["btcusdt","btc3lusdt","eth3susdt"],
+ "symbol_margin": ["linkusdt","xmrusdt","bsvusdt","ethusdt"],
+ "symbol_c2c": ["linkusdt","xmrusdt","bsvusdt","ethusdt"],
+ "proxy_host": "172.16.0.137",
+ "proxy_port": 58591
+}
+
diff --git a/docker_build.md b/docker_build.md
new file mode 100644
index 0000000..bccc852
--- /dev/null
+++ b/docker_build.md
@@ -0,0 +1,26 @@
+# prod 机器上
+
+## docker build push ,k8s update img
+
+```bash
+
+CURRENT_VERSION=41
+CURRENT_IMG="me/grid_trader"
+cd /root
+rm -rf grid_trader
+git clone https://mmmm/grid_trader.git
+cd grid_trader
+docker build -t $CURRENT_IMG:$CURRENT_VERSION .
+docker push $CURRENT_IMG:$CURRENT_VERSION
+clear
+echo $CURRENT_VERSION
+
+
+DEPLOYMENTS=`kubectl -n sre get deployment |grep binance-trader-qy |awk '{print $1}' |xargs`
+for deployment in $DEPLOYMENTS;do
+ kubectl set image deployments $deployment *=$CURRENT_IMG:$CURRENT_VERSION -n sre
+done
+
+```
+
+
diff --git a/example/__init__.py b/example/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/example/account/get_account_asset_valuation.py b/example/account/get_account_asset_valuation.py
new file mode 100644
index 0000000..56e5484
--- /dev/null
+++ b/example/account/get_account_asset_valuation.py
@@ -0,0 +1,24 @@
+from huobi.client.account import AccountClient
+from example.api_key import *
+
+account_client = AccountClient(api_key=g_api_key,secret_key=g_secret_key)
+
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#bd9157656f
+spot:现货账户,
+margin:逐仓杠杆账户,
+otc:OTC 账户,
+point:点卡账户,
+super-margin:全仓杠杆账户,
+investment: C2C杠杆借出账户,
+borrow: C2C杠杆借入账户,
+矿池账户: minepool,
+ETF账户: etf,
+抵押借贷账户: crypto-loans
+
+逐仓/全仓/C2C杠杆账户(margin/super-margin/borrow)会在第一次划转资产时创建,如果未划转过资产则不会有杠杆账户。
+'''
+
+account_type = "etf" #币币账户余额
+asset_valuation = account_client.get_account_asset_valuation(account_type=account_type, valuation_currency="usd")
+asset_valuation.print_object()
diff --git a/example/account/get_account_balance.py b/example/account/get_account_balance.py
new file mode 100644
index 0000000..92b730b
--- /dev/null
+++ b/example/account/get_account_balance.py
@@ -0,0 +1,3522 @@
+from huobi.client.account import AccountClient
+from huobi.utils import LogInfo
+from example.api_key import *
+
+
+# 获取账户总余额-明细
+
+'''
+ssh://root@40.83.77.152:22/usr/local/bin/python3 -u /opt/binance_grid_trader/example/account/get_account_balance.py
+====== (SDK encapsulated api) not recommend for low performance and frequence limitation ======
+Account ID : 18788142
+Account Type : margin
+Account State : working
+Subtype : btcusdt
+
+ Currency : btc
+ Balance Type : trade
+ Balance : 0.000000593380222235
+
+ Currency : btc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btc
+ Balance Type : loan
+ Balance : 0
+
+ Currency : btc
+ Balance Type : interest
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0.000000000000011349
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0.000000000000000001
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18766866
+Account Type : margin
+Account State : working
+Subtype : bchusdt
+
+ Currency : bch
+ Balance Type : trade
+ Balance : 0.000066417266187046
+
+ Currency : bch
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bch
+ Balance Type : loan
+ Balance : 0
+
+ Currency : bch
+ Balance Type : interest
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 27.726710382000002202
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0.000000000000000001
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18688813
+Account Type : margin
+Account State : working
+Subtype : linkusdt
+
+ Currency : link
+ Balance Type : trade
+ Balance : 0.000872717632331562
+
+ Currency : link
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : link
+ Balance Type : loan
+ Balance : -926.06600784
+
+ Currency : link
+ Balance Type : interest
+ Balance : -1.41368408
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 12255.3867979586747305
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 18600.000000000000000004
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18716338
+Account Type : margin
+Account State : working
+Subtype : omgusdt
+
+ Currency : omg
+ Balance Type : trade
+ Balance : 1.95822488
+
+ Currency : omg
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : omg
+ Balance Type : loan
+ Balance : 0
+
+ Currency : omg
+ Balance Type : interest
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0.00000000748
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 9736507
+Account Type : spot
+Account State : working
+Subtype :
+
+ Currency : lun
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lun
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ksm
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ksm
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ring
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ring
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bsv3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bsv3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : fil
+ Balance Type : trade
+ Balance : 0
+
+ Currency : fil
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bag
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bag
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : kcash
+ Balance Type : trade
+ Balance : 0
+
+ Currency : kcash
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bal
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bal
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : theta
+ Balance Type : trade
+ Balance : 0
+
+ Currency : theta
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : npxs
+ Balance Type : trade
+ Balance : 0
+
+ Currency : npxs
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ssp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ssp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : fis
+ Balance Type : trade
+ Balance : 0
+
+ Currency : fis
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : trio
+ Balance Type : trade
+ Balance : 0
+
+ Currency : trio
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : egt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : egt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : stpt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : stpt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bat
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bat
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mgo
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mgo
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mxc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mxc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : let
+ Balance Type : trade
+ Balance : 0
+
+ Currency : let
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : band
+ Balance Type : trade
+ Balance : 0
+
+ Currency : band
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : near
+ Balance Type : trade
+ Balance : 0
+
+ Currency : near
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xmr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xmr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dock
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dock
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bt1
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bt1
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pearl
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pearl
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : waves
+ Balance Type : trade
+ Balance : 0
+
+ Currency : waves
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bt2
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bt2
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : reef
+ Balance Type : trade
+ Balance : 0
+
+ Currency : reef
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : iota
+ Balance Type : trade
+ Balance : 0
+
+ Currency : iota
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xmx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xmx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : iris
+ Balance Type : trade
+ Balance : 0
+
+ Currency : iris
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cdc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cdc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ae
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ae
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : stk
+ Balance Type : trade
+ Balance : 0
+
+ Currency : stk
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : husd
+ Balance Type : trade
+ Balance : 0
+
+ Currency : husd
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : node
+ Balance Type : trade
+ Balance : 0
+
+ Currency : node
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ar
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ar
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : musk
+ Balance Type : trade
+ Balance : 0
+
+ Currency : musk
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : iost
+ Balance Type : trade
+ Balance : 0
+
+ Currency : iost
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dot2l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dot2l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pnt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pnt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eth3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eth3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cova
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cova
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wnxm
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wnxm
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eth3s
+ Balance Type : trade
+ Balance : 0.0000342
+
+ Currency : eth3s
+ Balance Type : frozen
+ Balance : 8483
+
+ Currency : wing
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wing
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dgb
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dgb
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bsv3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bsv3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dot2s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dot2s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dgd
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dgd
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zrx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zrx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bcd
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bcd
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : aac
+ Balance Type : trade
+ Balance : 0
+
+ Currency : aac
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bch
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bch
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bsv
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bsv
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : omg
+ Balance Type : trade
+ Balance : 0
+
+ Currency : omg
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : jst
+ Balance Type : trade
+ Balance : 0
+
+ Currency : jst
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hot
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hot
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : sun
+ Balance Type : trade
+ Balance : 0
+
+ Currency : sun
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wbtc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wbtc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : appc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : appc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btc
+ Balance Type : trade
+ Balance : 0.000000526
+
+ Currency : btc
+ Balance Type : frozen
+ Balance : 0.026683
+
+ Currency : sc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : sc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rsr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rsr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nkn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nkn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : qash
+ Balance Type : trade
+ Balance : 0
+
+ Currency : qash
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cvc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cvc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ardr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ardr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btg
+ Balance Type : trade
+ Balance : 0
+
+ Currency : btg
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bcv
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bcv
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : link3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : link3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bcx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bcx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : matic
+ Balance Type : trade
+ Balance : 0
+
+ Currency : matic
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btm
+ Balance Type : trade
+ Balance : 0
+
+ Currency : btm
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cvcoin
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cvcoin
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rcn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rcn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cvp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cvp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : link3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : link3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : uip
+ Balance Type : trade
+ Balance : 0
+
+ Currency : uip
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mzk
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mzk
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bts
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bts
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rte
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rte
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : salt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : salt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : btt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rccc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rccc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : one
+ Balance Type : trade
+ Balance : 0
+
+ Currency : one
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ong
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ong
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lxt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lxt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nhbtc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nhbtc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : abl
+ Balance Type : trade
+ Balance : 0
+
+ Currency : abl
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nest
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nest
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : glm
+ Balance Type : trade
+ Balance : 0
+
+ Currency : glm
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : storj
+ Balance Type : trade
+ Balance : 0
+
+ Currency : storj
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hpt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hpt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gnx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gnx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dht
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dht
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : abt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : abt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ugas
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ugas
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mana
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mana
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ont
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ont
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bkbt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bkbt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ruff
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ruff
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : onx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : onx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ankr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ankr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rdn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rdn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : algo
+ Balance Type : trade
+ Balance : 0
+
+ Currency : algo
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eth1s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eth1s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rub
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rub
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gof
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gof
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : but
+ Balance Type : trade
+ Balance : 0
+
+ Currency : but
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : tt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : tt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lym
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lym
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zjlt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zjlt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : df
+ Balance Type : trade
+ Balance : 0
+
+ Currency : df
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ast
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ast
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wicc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wicc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : doge
+ Balance Type : trade
+ Balance : 0
+
+ Currency : doge
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nano
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nano
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ach
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ach
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eko
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eko
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : sexc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : sexc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bel
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bel
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : uc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : uc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : qsp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : qsp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ekt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ekt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : act
+ Balance Type : trade
+ Balance : 0
+
+ Currency : act
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : loom
+ Balance Type : trade
+ Balance : 0
+
+ Currency : loom
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mkr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mkr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : woo
+ Balance Type : trade
+ Balance : 0
+
+ Currency : woo
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ycc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ycc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zec
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zec
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : itc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : itc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pai
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pai
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : meetone
+ Balance Type : trade
+ Balance : 0
+
+ Currency : meetone
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ren
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ren
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ela
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ela
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hbar
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hbar
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : atp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : atp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ncash
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ncash
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : elf
+ Balance Type : trade
+ Balance : 0
+
+ Currency : elf
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ada
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ada
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : icx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : icx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : req
+ Balance Type : trade
+ Balance : 0
+
+ Currency : req
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : add
+ Balance Type : trade
+ Balance : 0
+
+ Currency : add
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : vsys
+ Balance Type : trade
+ Balance : 0
+
+ Currency : vsys
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usd01
+ Balance Type : trade
+ Balance : 0
+
+ Currency : usd01
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eos3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eos3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zen
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zen
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : tusd
+ Balance Type : trade
+ Balance : 0
+
+ Currency : tusd
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eoss
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eoss
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pax
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pax
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : em
+ Balance Type : trade
+ Balance : 0
+
+ Currency : em
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hb10
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hb10
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pay
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pay
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rbtc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rbtc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eos3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eos3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : rvn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : rvn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : propy
+ Balance Type : trade
+ Balance : 0
+
+ Currency : propy
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : chr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : chr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : egcc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : egcc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mln
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mln
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : adt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : adt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bft
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bft
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : she
+ Balance Type : trade
+ Balance : 0
+
+ Currency : she
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : sand
+ Balance Type : trade
+ Balance : 0
+
+ Currency : sand
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bch3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bch3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : kava
+ Balance Type : trade
+ Balance : 0
+
+ Currency : kava
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : adx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : adx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bch3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bch3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : chz
+ Balance Type : trade
+ Balance : 0
+
+ Currency : chz
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xrp
+ Balance Type : trade
+ Balance : 0.00562304308335422
+
+ Currency : xrp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wpr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wpr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : idt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : idt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : tfuel
+ Balance Type : trade
+ Balance : 0
+
+ Currency : tfuel
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dka
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dka
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xrt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xrt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : vidy
+ Balance Type : trade
+ Balance : 0
+
+ Currency : vidy
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : perp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : perp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mass
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mass
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : for
+ Balance Type : trade
+ Balance : 0
+
+ Currency : for
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : uma
+ Balance Type : trade
+ Balance : 0
+
+ Currency : uma
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : qun
+ Balance Type : trade
+ Balance : 0
+
+ Currency : qun
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : sbtc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : sbtc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : luna
+ Balance Type : trade
+ Balance : 0
+
+ Currency : luna
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pizza
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pizza
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bcha
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bcha
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : yfii
+ Balance Type : trade
+ Balance : 0
+
+ Currency : yfii
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gas
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gas
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : yee
+ Balance Type : trade
+ Balance : 0
+
+ Currency : yee
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nexo
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nexo
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lend
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lend
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : portal
+ Balance Type : trade
+ Balance : 0
+
+ Currency : portal
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eng
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eng
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bhd
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bhd
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : aave
+ Balance Type : trade
+ Balance : 0
+
+ Currency : aave
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wan
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wan
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : grt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : grt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : grs
+ Balance Type : trade
+ Balance : 0
+
+ Currency : grs
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : waxp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : waxp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hbc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hbc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : uni
+ Balance Type : trade
+ Balance : 0
+
+ Currency : uni
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gsc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gsc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ckb
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ckb
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : swftc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : swftc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : yfi
+ Balance Type : trade
+ Balance : 0
+
+ Currency : yfi
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xtz
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xtz
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : avax
+ Balance Type : trade
+ Balance : 0
+
+ Currency : avax
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eon
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eon
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xrp3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xrp3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eop
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eop
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eos
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eos
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : value
+ Balance Type : trade
+ Balance : 0
+
+ Currency : value
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : fair
+ Balance Type : trade
+ Balance : 0
+
+ Currency : fair
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ht
+ Balance Type : trade
+ Balance : 0.0024
+
+ Currency : ht
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bix
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bix
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ost
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ost
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : skm
+ Balance Type : trade
+ Balance : 0
+
+ Currency : skm
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : skl
+ Balance Type : trade
+ Balance : 0
+
+ Currency : skl
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gtc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gtc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : 1inch
+ Balance Type : trade
+ Balance : 0
+
+ Currency : 1inch
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ocn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ocn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zil
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zil
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xrp3l
+ Balance Type : trade
+ Balance : 0.0000534
+
+ Currency : xrp3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xem
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xem
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nas
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nas
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : comp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : comp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wtc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wtc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hvt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hvt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xvg
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xvg
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : iq
+ Balance Type : trade
+ Balance : 0
+
+ Currency : iq
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : tnb
+ Balance Type : trade
+ Balance : 0
+
+ Currency : tnb
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btc3s
+ Balance Type : trade
+ Balance : 0.0000704
+
+ Currency : btc3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : 18c
+ Balance Type : trade
+ Balance : 0
+
+ Currency : 18c
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : powr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : powr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pvt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pvt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btc3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : btc3l
+ Balance Type : frozen
+ Balance : 1.996
+
+ Currency : ctxc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ctxc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : tnt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : tnt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nbs
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nbs
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : fsn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : fsn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hive
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hive
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : iic
+ Balance Type : trade
+ Balance : 0
+
+ Currency : iic
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : kmd
+ Balance Type : trade
+ Balance : 0
+
+ Currency : kmd
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bags
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bags
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : steem
+ Balance Type : trade
+ Balance : 0
+
+ Currency : steem
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lol
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lol
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dot
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dot
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cmt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cmt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ncc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ncc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : top
+ Balance Type : trade
+ Balance : 0
+
+ Currency : top
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : get
+ Balance Type : trade
+ Balance : 0
+
+ Currency : get
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gve
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gve
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cvnt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cvnt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : tos
+ Balance Type : trade
+ Balance : 0
+
+ Currency : tos
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : fti
+ Balance Type : trade
+ Balance : 0
+
+ Currency : fti
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : smt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : smt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : man
+ Balance Type : trade
+ Balance : 0
+
+ Currency : man
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cnns
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cnns
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : knc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : knc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cnn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cnn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bifi
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bifi
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ftt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ftt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pha
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pha
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : snc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : snc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zla
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zla
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : blz
+ Balance Type : trade
+ Balance : 0
+
+ Currency : blz
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : api3
+ Balance Type : trade
+ Balance : 0
+
+ Currency : api3
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : chat
+ Balance Type : trade
+ Balance : 0
+
+ Currency : chat
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eosdac
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eosdac
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : yamv2
+ Balance Type : trade
+ Balance : 0
+
+ Currency : yamv2
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : snt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : snt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : atom
+ Balance Type : trade
+ Balance : 0
+
+ Currency : atom
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : snx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : snx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : soc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : soc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dac
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dac
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : seele
+ Balance Type : trade
+ Balance : 0
+
+ Currency : seele
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dai
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dai
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gusd
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gusd
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : sol
+ Balance Type : trade
+ Balance : 0
+
+ Currency : sol
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : hit
+ Balance Type : trade
+ Balance : 0
+
+ Currency : hit
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ltc3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ltc3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : etc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : etc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dat
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dat
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : etf
+ Balance Type : trade
+ Balance : 0
+
+ Currency : etf
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ogo
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ogo
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ogn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ogn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 3528.75950756236086578
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0.000000000000000002
+
+ Currency : eth
+ Balance Type : trade
+ Balance : 0
+
+ Currency : eth
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mco
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mco
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : topc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : topc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : aidoc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : aidoc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : titan
+ Balance Type : trade
+ Balance : 0
+
+ Currency : titan
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : neo
+ Balance Type : trade
+ Balance : 0
+
+ Currency : neo
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : sushi
+ Balance Type : trade
+ Balance : 0
+
+ Currency : sushi
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mta
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mta
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lrc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lrc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ltc3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ltc3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : renbtc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : renbtc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ven
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ven
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : etn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : etn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : swrv
+ Balance Type : trade
+ Balance : 0
+
+ Currency : swrv
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : firo
+ Balance Type : trade
+ Balance : 0
+
+ Currency : firo
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wgp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wgp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : new
+ Balance Type : trade
+ Balance : 0
+
+ Currency : new
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : gxc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : gxc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mtl
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mtl
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dbc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dbc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : vet
+ Balance Type : trade
+ Balance : 0
+
+ Currency : vet
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : trb
+ Balance Type : trade
+ Balance : 0
+
+ Currency : trb
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mtn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mtn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : fil3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : fil3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bnt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bnt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lba
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lba
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : oxt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : oxt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : usdc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : fil3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : fil3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : utk
+ Balance Type : trade
+ Balance : 0
+
+ Currency : utk
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nvt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nvt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mtx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mtx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : wxt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : wxt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : edu
+ Balance Type : trade
+ Balance : 0
+
+ Currency : edu
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : trx
+ Balance Type : trade
+ Balance : 0.002358
+
+ Currency : trx
+ Balance Type : frozen
+ Balance : 69069.31
+
+ Currency : dash
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dash
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mds
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mds
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nuls
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nuls
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mdx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mdx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pond
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pond
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lina
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lina
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bor
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bor
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lsk
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lsk
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : bot
+ Balance Type : trade
+ Balance : 0
+
+ Currency : bot
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : nsure
+ Balance Type : trade
+ Balance : 0
+
+ Currency : nsure
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : link
+ Balance Type : trade
+ Balance : 0.0099
+
+ Currency : link
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : box
+ Balance Type : trade
+ Balance : 0
+
+ Currency : box
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : akro
+ Balance Type : trade
+ Balance : 0
+
+ Currency : akro
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : meet
+ Balance Type : trade
+ Balance : 0
+
+ Currency : meet
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : qtum
+ Balance Type : trade
+ Balance : 0
+
+ Currency : qtum
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : uni2l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : uni2l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : arpa
+ Balance Type : trade
+ Balance : 0
+
+ Currency : arpa
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dta
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dta
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : dcr
+ Balance Type : trade
+ Balance : 0
+
+ Currency : dcr
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : uuu
+ Balance Type : trade
+ Balance : 0
+
+ Currency : uuu
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : uni2s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : uni2s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cre
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cre
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zec3l
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zec3l
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : kan
+ Balance Type : trade
+ Balance : 0
+
+ Currency : kan
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : beth
+ Balance Type : trade
+ Balance : 0
+
+ Currency : beth
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ltc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ltc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : zec3s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : zec3s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : inc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : inc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mex
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mex
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cro
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cro
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : mvl
+ Balance Type : trade
+ Balance : 0
+
+ Currency : mvl
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : inj
+ Balance Type : trade
+ Balance : 0
+
+ Currency : inj
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : evx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : evx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ant
+ Balance Type : trade
+ Balance : 0
+
+ Currency : ant
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : crv
+ Balance Type : trade
+ Balance : 0
+
+ Currency : crv
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : cru
+ Balance Type : trade
+ Balance : 0
+
+ Currency : cru
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : btc1s
+ Balance Type : trade
+ Balance : 0
+
+ Currency : btc1s
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : srn
+ Balance Type : trade
+ Balance : 0
+
+ Currency : srn
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : datx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : datx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : badger
+ Balance Type : trade
+ Balance : 0.91950210527285626
+
+ Currency : badger
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pols
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pols
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : pc
+ Balance Type : trade
+ Balance : 0
+
+ Currency : pc
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : poly
+ Balance Type : trade
+ Balance : 0
+
+ Currency : poly
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xlm
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xlm
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : lamb
+ Balance Type : trade
+ Balance : 0
+
+ Currency : lamb
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : phx
+ Balance Type : trade
+ Balance : 0
+
+ Currency : phx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : front
+ Balance Type : trade
+ Balance : 0
+
+ Currency : front
+ Balance Type : frozen
+ Balance : 0
+
+
+Account ID : 18781342
+Account Type : margin
+Account State : working
+Subtype : bsvusdt
+
+ Currency : bsv
+ Balance Type : trade
+ Balance : 0.000059073599142826
+
+ Currency : bsv
+ Balance Type : frozen
+ Balance : 50.8113
+
+ Currency : bsv
+ Balance Type : loan
+ Balance : 0
+
+ Currency : bsv
+ Balance Type : interest
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0.000900000000003071
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0.000000000000000003
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : -6688.376
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : -21.41935655
+
+
+Account ID : 18837978
+Account Type : margin
+Account State : working
+Subtype : htusdt
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0.00000000800000194
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+ Currency : ht
+ Balance Type : trade
+ Balance : 0.00947382462977359
+
+ Currency : ht
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ht
+ Balance Type : loan
+ Balance : 0
+
+ Currency : ht
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18739313
+Account Type : margin
+Account State : working
+Subtype : ethusdt
+
+ Currency : eth
+ Balance Type : trade
+ Balance : 0.000990739293469514
+
+ Currency : eth
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : eth
+ Balance Type : loan
+ Balance : -0.00158068
+
+ Currency : eth
+ Balance Type : interest
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 1053.900375716000003242
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0.000000000000000001
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18692354
+Account Type : margin
+Account State : working
+Subtype : yfiusdt
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+ Currency : yfi
+ Balance Type : trade
+ Balance : 0.000981342
+
+ Currency : yfi
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : yfi
+ Balance Type : loan
+ Balance : 0
+
+ Currency : yfi
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18821595
+Account Type : margin
+Account State : working
+Subtype : xrpusdt
+
+ Currency : xrp
+ Balance Type : trade
+ Balance : 0
+
+ Currency : xrp
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : xrp
+ Balance Type : loan
+ Balance : 0
+
+ Currency : xrp
+ Balance Type : interest
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18792019
+Account Type : margin
+Account State : working
+Subtype : vetusdt
+
+ Currency : vet
+ Balance Type : trade
+ Balance : 299.04182932
+
+ Currency : vet
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : vet
+ Balance Type : loan
+ Balance : 0
+
+ Currency : vet
+ Balance Type : interest
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0.0000000096
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18736093
+Account Type : margin
+Account State : working
+Subtype : trxusdt
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 5.843408887800000024
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0.000000000000000001
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+ Currency : trx
+ Balance Type : trade
+ Balance : 0.684836479013854132
+
+ Currency : trx
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : trx
+ Balance Type : loan
+ Balance : 0
+
+ Currency : trx
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 18767134
+Account Type : margin
+Account State : working
+Subtype : renusdt
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0.00000000702
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : 0
+
+ Currency : ren
+ Balance Type : trade
+ Balance : 8.30309806
+
+ Currency : ren
+ Balance Type : frozen
+ Balance : 0
+
+ Currency : ren
+ Balance Type : loan
+ Balance : 0
+
+ Currency : ren
+ Balance Type : interest
+ Balance : 0
+
+
+Account ID : 17863569
+Account Type : otc
+Account State : working
+Subtype :
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0
+
+
+Account ID : 18788774
+Account Type : margin
+Account State : working
+Subtype : xmrusdt
+
+ Currency : usdt
+ Balance Type : trade
+ Balance : 0.00000000800000496
+
+ Currency : usdt
+ Balance Type : frozen
+ Balance : 0.000000000000000001
+
+ Currency : usdt
+ Balance Type : loan
+ Balance : -7994.332
+
+ Currency : usdt
+ Balance Type : interest
+ Balance : -31.33522464
+
+ Currency : xmr
+ Balance Type : trade
+ Balance : 0.000074003175294476
+
+ Currency : xmr
+ Balance Type : frozen
+ Balance : 81.4258
+
+ Currency : xmr
+ Balance Type : loan
+ Balance : 0
+
+ Currency : xmr
+ Balance Type : interest
+ Balance : 0
+
+
+
+Process finished with exit code 0
+
+'''
+
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+LogInfo.output("====== (SDK encapsulated api) not recommend for low performance and frequence limitation ======")
+account_balance_list = account_client.get_account_balance()
+if account_balance_list and len(account_balance_list):
+ for account_obj in account_balance_list:
+ account_obj.print_object()
+ print()
\ No newline at end of file
diff --git a/example/account/get_account_balance_by_subuid.py b/example/account/get_account_balance_by_subuid.py
new file mode 100644
index 0000000..8d9014c
--- /dev/null
+++ b/example/account/get_account_balance_by_subuid.py
@@ -0,0 +1,12 @@
+from huobi.client.account import AccountClient
+from huobi.constant import *
+
+# get accounts
+from huobi.utils import *
+from example.api_key import *
+# 子账户财产
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+list_obj = account_client.get_account_balance_by_subuid(sub_uid=g_sub_uid)
+LogInfo.output_list(list_obj)
+
diff --git a/example/account/get_account_balance_sufficient.py b/example/account/get_account_balance_sufficient.py
new file mode 100644
index 0000000..fb46941
--- /dev/null
+++ b/example/account/get_account_balance_sufficient.py
@@ -0,0 +1,33 @@
+from huobi.client.account import AccountClient
+
+# get accounts
+from huobi.utils import *
+from example.api_key import *
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+# list_obj = account_client.get_accounts()
+# if list_obj and len(list_obj):
+# for account_obj in list_obj:
+# list_obj = account_client.get_balance(account_id=account_obj.id)
+# LogInfo.output("===== {account_id} , {account_type} =====".format(account_id=account_obj.id, account_type=account_obj.type))
+# if len(list_obj):
+# for obj in list_obj:
+# if float(obj.balance) > 0.1: # only show account with balance
+# obj.print_object()
+# print()
+
+LogInfo.output("====== (SDK encapsulated api) not recommend for low performance and frequence limitation ======")
+account_balance_list = account_client.get_account_balance()
+if account_balance_list and len(account_balance_list):
+ for account_balance_obj in account_balance_list:
+ if account_balance_obj and len(account_balance_obj.list):
+ PrintBasic.print_basic(account_balance_obj.id, "ID")
+ PrintBasic.print_basic(account_balance_obj.type, "Account Type")
+ PrintBasic.print_basic(account_balance_obj.state, "Account State")
+ PrintBasic.print_basic(account_balance_obj.subtype, "Subtype")
+ for balance_obj in account_balance_obj.list:
+ if float(balance_obj.balance) > 0.1: # only show account with balance
+ balance_obj.print_object("\t")
+ print()
+ print()
\ No newline at end of file
diff --git a/example/account/get_account_by_type_symbol.py b/example/account/get_account_by_type_symbol.py
new file mode 100644
index 0000000..f79e083
--- /dev/null
+++ b/example/account/get_account_by_type_symbol.py
@@ -0,0 +1,34 @@
+from huobi.client.account import AccountClient
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+
+# get accounts
+from huobi.utils import *
+from example.api_key import *
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+
+LogInfo.output("========= case 1 get spot (SDK encapsulated api) =========")
+account_obj = account_client.get_account_by_type_and_symbol(account_type=AccountType.SPOT, symbol=None)
+account_obj.print_object()
+
+LogInfo.output("========= case 2 get margin-linkusdt (SDK encapsulated api) =========")
+account_obj = account_client.get_account_by_type_and_symbol(account_type=AccountType.MARGIN, symbol="linkusdt")
+if account_obj:
+ account_obj.print_object()
+
+LogInfo.output("========= case 3 get margin-linkusdt (SDK encapsulated api) =========")
+account_obj = account_client.get_account_by_type_and_symbol(account_type=AccountType.SUPER_MARGIN, symbol=None)
+if account_obj:
+ account_obj.print_object()
+
+LogInfo.output("========= case 4 get margin-linkusdt (original api, supported) =========")
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = margin_client.get_margin_account_balance(symbol="linkusdt")
+LogInfo.output_list(list_obj)
+
+LogInfo.output("========= case 5 get cross-margin (original api, supported) =========")
+print(margin_client)
+account_balance = margin_client.get_cross_margin_account_balance()
+account_balance.print_object()
\ No newline at end of file
diff --git a/example/account/get_account_history.py b/example/account/get_account_history.py
new file mode 100644
index 0000000..e53e16e
--- /dev/null
+++ b/example/account/get_account_history.py
@@ -0,0 +1,11 @@
+from huobi.client.account import AccountClient
+
+# get accounts
+from huobi.utils import *
+from example.api_key import *
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+
+account_history = account_client.get_account_history(account_id=g_account_id, start_time=1600827872000, size=4)
+LogInfo.output_list(account_history["data"])
+LogInfo.output('Next Id: %s' % (account_history["next_id"]))
diff --git a/example/account/get_account_ledger.py b/example/account/get_account_ledger.py
new file mode 100644
index 0000000..a967a37
--- /dev/null
+++ b/example/account/get_account_ledger.py
@@ -0,0 +1,8 @@
+from huobi.client.account import AccountClient
+from huobi.utils import *
+from example.api_key import *
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+
+list_obj = account_client.get_account_ledger(account_id=g_account_id)
+LogInfo.output_list(list_obj)
diff --git a/example/account/get_account_point.py b/example/account/get_account_point.py
new file mode 100644
index 0000000..6da04ef
--- /dev/null
+++ b/example/account/get_account_point.py
@@ -0,0 +1,8 @@
+from huobi.client.account import AccountClient
+from example.api_key import *
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+
+account_point_result = account_client.get_account_point()
+account_point_result.print_object()
diff --git a/example/account/get_accounts.py b/example/account/get_accounts.py
new file mode 100644
index 0000000..ecaf373
--- /dev/null
+++ b/example/account/get_accounts.py
@@ -0,0 +1,12 @@
+from huobi.client.account import AccountClient
+
+# get accounts
+from huobi.utils import *
+from example.api_key import *
+
+
+account_client = AccountClient(api_key=g_api_key,secret_key=g_secret_key)
+
+list_obj = account_client.get_accounts()
+LogInfo.output_list(list_obj)
+
diff --git a/example/account/get_aggregated_subuser_balance.py b/example/account/get_aggregated_subuser_balance.py
new file mode 100644
index 0000000..5854f68
--- /dev/null
+++ b/example/account/get_aggregated_subuser_balance.py
@@ -0,0 +1,10 @@
+from huobi.client.account import AccountClient
+
+# get accounts
+from huobi.utils import *
+from example.api_key import *
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+list_obj = account_client.get_aggregated_subuser_balance()
+LogInfo.output_list(list_obj)
diff --git a/example/account/get_balance.py b/example/account/get_balance.py
new file mode 100644
index 0000000..c050d7f
--- /dev/null
+++ b/example/account/get_balance.py
@@ -0,0 +1,9 @@
+from huobi.client.account import AccountClient
+
+# get accounts
+from huobi.utils import *
+from example.api_key import *
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+list_obj = account_client.get_balance(account_id=g_account_id)
+LogInfo.output_list(list_obj)
\ No newline at end of file
diff --git a/example/account/post_account_transfer.py b/example/account/post_account_transfer.py
new file mode 100644
index 0000000..fecf2a8
--- /dev/null
+++ b/example/account/post_account_transfer.py
@@ -0,0 +1,12 @@
+from huobi.client.account import AccountClient
+from huobi.constant import *
+
+account_client = AccountClient(api_key=g_api_key, secret_key=g_secret_key)
+# transfer_result = account_client.post_account_transfer(111859319, 'spot', 10354000, 147509118, 'spot', 13956126, 'trx', 3600)
+# transfer_result.print_object()
+
+# transfer_result = account_client.post_account_transfer(111859319, 'spot', 10354000, 147991959, 'spot', 14026125, 'usdt', 3600)
+# transfer_result.print_object()
+
+transfer_result = account_client.post_account_transfer(111859319, 'spot', 10354000, 141403659, 'spot', 13082796, 'usdt', 32.21)
+transfer_result.print_object()
diff --git a/example/account/post_point_transfer.py b/example/account/post_point_transfer.py
new file mode 100644
index 0000000..f4db684
--- /dev/null
+++ b/example/account/post_point_transfer.py
@@ -0,0 +1,8 @@
+from huobi.client.account import AccountClient
+from huobi.constant import *
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+
+point_transfer_result = account_client.post_point_transfer(from_uid=111859319, to_uid=124409916, group_id=0, amount=10)
+point_transfer_result.print_object()
diff --git a/example/account/post_sub_user_management.py b/example/account/post_sub_user_management.py
new file mode 100644
index 0000000..679b338
--- /dev/null
+++ b/example/account/post_sub_user_management.py
@@ -0,0 +1,13 @@
+
+from huobi.client.account import AccountClient
+from huobi.constant import *
+
+# get accounts
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+
+ret = account_client.post_sub_uid_management(sub_uid=g_sub_uid, action=SubUidAction.LOCK)
+ret.print_object()
+
+ret = account_client.post_sub_uid_management(sub_uid=g_sub_uid, action=SubUidAction.UNLOCK)
+ret.print_object()
\ No newline at end of file
diff --git a/example/account/post_transfer_between_futures_and_pro.py b/example/account/post_transfer_between_futures_and_pro.py
new file mode 100644
index 0000000..33ab7cc
--- /dev/null
+++ b/example/account/post_transfer_between_futures_and_pro.py
@@ -0,0 +1,19 @@
+
+from huobi.client.account import AccountClient
+from huobi.constant import *
+from huobi.utils import *
+import time
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+transfer_order_id = account_client.transfer_between_futures_and_pro(currency="trx", amount=200, transfer_type=TransferFuturesPro.TO_FUTURES)
+LogInfo.output("transfer from pro to future Order Id : {id}".format(id=transfer_order_id))
+
+# need wait a minute
+time.sleep(2)
+
+transfer_order_id = account_client.transfer_between_futures_and_pro(currency="trx", amount=200, transfer_type=TransferFuturesPro.TO_PRO)
+LogInfo.output("transfer from future to pro Order Id : {id}".format(id=transfer_order_id))
+
+
+
diff --git a/example/account/post_transfer_between_parent_and_subuser.py b/example/account/post_transfer_between_parent_and_subuser.py
new file mode 100644
index 0000000..a0a959f
--- /dev/null
+++ b/example/account/post_transfer_between_parent_and_subuser.py
@@ -0,0 +1,15 @@
+
+from huobi.client.account import AccountClient
+from huobi.constant import *
+from huobi.utils import *
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key)
+transfer_order_id = account_client.transfer_between_parent_and_subuser(sub_uid=g_sub_uid, currency="usdt", amount=10, transfer_type=TransferMasterType.OUT)
+LogInfo.output("transfer from master to subuser Order Id : {id}".format(id=transfer_order_id))
+
+transfer_order_id = account_client.transfer_between_parent_and_subuser(sub_uid=g_sub_uid, currency="usdt", amount=10, transfer_type=TransferMasterType.IN)
+LogInfo.output("transfer from subuser to master Order Id : {id}".format(id=transfer_order_id))
+
+
+
diff --git a/example/account/req_account_balance_list.py b/example/account/req_account_balance_list.py
new file mode 100644
index 0000000..5423ae5
--- /dev/null
+++ b/example/account/req_account_balance_list.py
@@ -0,0 +1,10 @@
+from huobi.client.account import AccountClient
+from huobi.constant import *
+
+
+def callback(account_balance_req: 'AccountBalanceReq'):
+ account_balance_req.print_object()
+
+
+account_client = AccountClient(api_key=g_api_key, secret_key=g_secret_key)
+account_client.req_account_balance(callback=callback, client_req_id=None)
diff --git a/example/account/sub_account_update.py b/example/account/sub_account_update.py
new file mode 100644
index 0000000..fdd9a2f
--- /dev/null
+++ b/example/account/sub_account_update.py
@@ -0,0 +1,15 @@
+from huobi.client.account import AccountClient
+from huobi.constant import *
+
+
+def callback(account_change_event: 'AccountChangeEvent'):
+ account_change_event.print_object()
+ print()
+
+
+account_client = AccountClient(api_key=g_api_key,
+ secret_key=g_secret_key,
+ init_log=True)
+# account_client.sub_account_update(AccountBalanceMode.TOTAL, callback)
+account_client.sub_account_update(AccountBalanceMode.BALANCE, callback)
+
diff --git a/example/algo/get_open_orders.py b/example/algo/get_open_orders.py
new file mode 100644
index 0000000..17161c1
--- /dev/null
+++ b/example/algo/get_open_orders.py
@@ -0,0 +1,10 @@
+from huobi.client.algo import AlgoClient
+from huobi.constant import *
+from huobi.utils import *
+
+symbol_test = "adausdt"
+account_id = g_account_id
+
+algo_client = AlgoClient(api_key=g_api_key, secret_key=g_secret_key)
+result = algo_client.get_open_orders()
+LogInfo.output_list(result)
diff --git a/example/algo/get_order.py b/example/algo/get_order.py
new file mode 100644
index 0000000..d606d92
--- /dev/null
+++ b/example/algo/get_order.py
@@ -0,0 +1,10 @@
+from huobi.client.algo import AlgoClient
+from huobi.constant import *
+
+account_id = g_account_id
+client_order_id = "test002"
+
+# get specific order by clientOrderId
+algo_client = AlgoClient(api_key=g_api_key, secret_key=g_secret_key)
+result = algo_client.get_order(client_order_id)
+result.print_object()
diff --git a/example/algo/get_order_history.py b/example/algo/get_order_history.py
new file mode 100644
index 0000000..896d5f9
--- /dev/null
+++ b/example/algo/get_order_history.py
@@ -0,0 +1,10 @@
+from huobi.client.algo import AlgoClient
+from huobi.constant import *
+from huobi.utils import *
+
+symbol_test = "adausdt"
+account_id = g_account_id
+
+algo_client = AlgoClient(api_key=g_api_key, secret_key=g_secret_key)
+result = algo_client.get_order_history(symbol_test, AlgoOrderStatus.TRIGGERED)
+LogInfo.output_list(result)
diff --git a/example/algo/post_cancel_algo_orders.py b/example/algo/post_cancel_algo_orders.py
new file mode 100644
index 0000000..272ba81
--- /dev/null
+++ b/example/algo/post_cancel_algo_orders.py
@@ -0,0 +1,17 @@
+from huobi.client.algo import AlgoClient
+from huobi.constant import *
+from huobi.utils import *
+
+symbol_test = "adausdt"
+account_id = g_account_id
+
+orders_to_cancel = ["test003", "test001"]
+algo_client = AlgoClient(api_key=g_api_key, secret_key=g_secret_key)
+result = algo_client.cancel_orders(orders_to_cancel)
+result.print_object()
+
+# order_id = algo_client.create_order(symbol=symbol_test, account_id=account_id, order_type=OrderType.BUY_MARKET, source=OrderSource.API, amount=5.0, price=1.292)
+# LogInfo.output("created order id : {id}".format(id=order_id))
+#
+# order_id = algo_client.create_order(symbol=symbol_test, account_id=account_id, order_type=OrderType.SELL_MARKET, source=OrderSource.API, amount=1.77, price=None)
+# LogInfo.output("created order id : {id}".format(id=order_id))
diff --git a/example/algo/post_create_algo_order.py b/example/algo/post_create_algo_order.py
new file mode 100644
index 0000000..c14670a
--- /dev/null
+++ b/example/algo/post_create_algo_order.py
@@ -0,0 +1,12 @@
+from huobi.client.algo import AlgoClient
+from huobi.constant import *
+from huobi.utils import *
+
+symbol_test = "adausdt"
+account_id = g_account_id
+
+algo_client = AlgoClient(api_key=g_api_key, secret_key=g_secret_key)
+order_id = algo_client.create_order(symbol=symbol_test, account_id=account_id, order_side=OrderSide.BUY,
+ order_type=AlgoOrderType.LIMIT, order_size=65, order_price=0.08, stop_price=0.085,
+ client_order_id="test004")
+LogInfo.output("created order id : {id}".format(id=order_id))
diff --git a/example/api_key.py b/example/api_key.py
new file mode 100644
index 0000000..7ca78bb
--- /dev/null
+++ b/example/api_key.py
@@ -0,0 +1,3 @@
+g_api_key = "e7f941dd-4167913c-mjlpdje3ld-f97b3"
+g_secret_key = "f06af3d0-eaf3e4ff-9f0e931f-43aec"
+g_account_id = 18788142
\ No newline at end of file
diff --git a/example/etf/get_etf_swap_config.py b/example/etf/get_etf_swap_config.py
new file mode 100644
index 0000000..4d88659
--- /dev/null
+++ b/example/etf/get_etf_swap_config.py
@@ -0,0 +1,16 @@
+from huobi.client.etf import EtfClient
+
+
+etf_client = EtfClient()
+etf_config = etf_client.get_etf_swap_config("hb10")
+etf_config.print_object()
+
+
+
+
+
+
+
+
+
+
diff --git a/example/etf/get_etf_swap_list.py b/example/etf/get_etf_swap_list.py
new file mode 100644
index 0000000..2e355d7
--- /dev/null
+++ b/example/etf/get_etf_swap_list.py
@@ -0,0 +1,24 @@
+from huobi.client.etf import EtfClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+etf_client = EtfClient(api_key=g_api_key, secret_key=g_secret_key)
+etf_list = etf_client.get_etf_swap_list(etf_name="hb10", offset=0, size=20)
+LogInfo.output_list(etf_list)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/etf/post_etf_swap_in.py b/example/etf/post_etf_swap_in.py
new file mode 100644
index 0000000..a1df864
--- /dev/null
+++ b/example/etf/post_etf_swap_in.py
@@ -0,0 +1,23 @@
+from huobi.client.etf import EtfClient
+from huobi.constant import *
+
+etf_client = EtfClient(api_key=g_api_key, secret_key=g_secret_key)
+etf_swap_ret = etf_client.post_etf_swap_in("hb10", 1000)
+etf_swap_ret.print_object()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/etf/post_etf_swap_out.py b/example/etf/post_etf_swap_out.py
new file mode 100644
index 0000000..7ebe90b
--- /dev/null
+++ b/example/etf/post_etf_swap_out.py
@@ -0,0 +1,22 @@
+from huobi.client.etf import EtfClient
+from huobi.constant import *
+
+etf_client = EtfClient(api_key=g_api_key, secret_key=g_secret_key)
+etf_swap_ret = etf_client.post_etf_swap_out("hb10", 1000)
+etf_swap_ret.print_object()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/generic/get_exchange_currencies.py b/example/generic/get_exchange_currencies.py
new file mode 100644
index 0000000..9f055bc
--- /dev/null
+++ b/example/generic/get_exchange_currencies.py
@@ -0,0 +1,9 @@
+from huobi.client.generic import GenericClient
+from huobi.utils import *
+
+
+generic_client = GenericClient()
+list_obj = generic_client.get_exchange_currencies()
+LogInfo.output("---- Supported currency ----")
+for currency in list_obj:
+ LogInfo.output(currency)
\ No newline at end of file
diff --git a/example/generic/get_exchange_info.py b/example/generic/get_exchange_info.py
new file mode 100644
index 0000000..4897240
--- /dev/null
+++ b/example/generic/get_exchange_info.py
@@ -0,0 +1,13 @@
+from huobi.client.generic import GenericClient
+from huobi.utils import *
+
+
+generic_client = GenericClient()
+list_obj = generic_client.get_exchange_info()
+LogInfo.output("---- Supported symbols ----")
+for symbol in list_obj.symbol_list:
+ LogInfo.output(symbol.symbol)
+
+LogInfo.output("---- Supported currencies ----");
+for currency in list_obj.currencies:
+ LogInfo.output(currency)
diff --git a/example/generic/get_exchange_symbols.py b/example/generic/get_exchange_symbols.py
new file mode 100644
index 0000000..7c996c4
--- /dev/null
+++ b/example/generic/get_exchange_symbols.py
@@ -0,0 +1,13 @@
+from huobi.client.generic import GenericClient
+from huobi.utils import *
+
+
+generic_client = GenericClient()
+list_obj = generic_client.get_exchange_symbols()
+if len(list_obj):
+ for idx, row in enumerate(list_obj):
+ LogInfo.output("------- number " + str(idx) + " -------")
+ row.print_object()
+
+
+
diff --git a/example/generic/get_exchange_timestamp.py b/example/generic/get_exchange_timestamp.py
new file mode 100644
index 0000000..6ffad7c
--- /dev/null
+++ b/example/generic/get_exchange_timestamp.py
@@ -0,0 +1,8 @@
+from huobi.client.generic import GenericClient
+from huobi.utils import *
+
+
+generic_client = GenericClient()
+ts = generic_client.get_exchange_timestamp()
+LogInfo.output(ts)
+
diff --git a/example/generic/get_market_status.py b/example/generic/get_market_status.py
new file mode 100644
index 0000000..2b8c2ff
--- /dev/null
+++ b/example/generic/get_market_status.py
@@ -0,0 +1,8 @@
+from huobi.client.generic import GenericClient
+
+generic_client = GenericClient()
+market_status = generic_client.get_market_status()
+print(market_status)
+
+
+
diff --git a/example/generic/get_reference_currencies.py b/example/generic/get_reference_currencies.py
new file mode 100644
index 0000000..1d0379a
--- /dev/null
+++ b/example/generic/get_reference_currencies.py
@@ -0,0 +1,13 @@
+from huobi.client.generic import GenericClient
+from huobi.utils import *
+
+
+generic_client = GenericClient()
+
+list_obj = generic_client.get_reference_currencies()
+LogInfo.output_list(list_obj)
+
+
+list_obj = generic_client.get_reference_currencies(currency="usdt")
+LogInfo.output_list(list_obj)
+
diff --git a/example/generic/get_system_status.py b/example/generic/get_system_status.py
new file mode 100644
index 0000000..7f315db
--- /dev/null
+++ b/example/generic/get_system_status.py
@@ -0,0 +1,12 @@
+from huobi.client.generic import GenericClient
+
+
+"""
+GET https://status.huobigroup.com/api/v2/summary.json
+"""
+generic_client = GenericClient()
+system_status = generic_client.get_system_status()
+print(system_status)
+
+
+
diff --git a/example/margin/get_cross_margin_account_balance.py b/example/margin/get_cross_margin_account_balance.py
new file mode 100644
index 0000000..8e6ccc1
--- /dev/null
+++ b/example/margin/get_cross_margin_account_balance.py
@@ -0,0 +1,9 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+account_balance = margin_client.get_cross_margin_account_balance()
+account_balance.print_object()
+
+
diff --git a/example/margin/get_cross_margin_loan_info.py b/example/margin/get_cross_margin_loan_info.py
new file mode 100644
index 0000000..0b626f3
--- /dev/null
+++ b/example/margin/get_cross_margin_loan_info.py
@@ -0,0 +1,8 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = margin_client.get_cross_margin_loan_info()
+LogInfo.output_list(list_obj)
+
diff --git a/example/margin/get_cross_margin_loan_orders.py b/example/margin/get_cross_margin_loan_orders.py
new file mode 100644
index 0000000..8b9ced7
--- /dev/null
+++ b/example/margin/get_cross_margin_loan_orders.py
@@ -0,0 +1,16 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+
+# no filter
+list_obj = margin_client.get_cross_margin_loan_orders()
+LogInfo.output_list(list_obj)
+
+# filter by state
+list_obj = margin_client.get_cross_margin_loan_orders(state=LoanOrderState.ACCRUAL)
+LogInfo.output_list(list_obj)
+
+
diff --git a/example/margin/get_general_repayment_loan_records.py b/example/margin/get_general_repayment_loan_records.py
new file mode 100644
index 0000000..d37c0c2
--- /dev/null
+++ b/example/margin/get_general_repayment_loan_records.py
@@ -0,0 +1,12 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import LogInfo
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+
+list_result = margin_client.get_general_repayment_loan_records(limit=10)
+LogInfo.output_list(list_result)
+
+
+
+
diff --git a/example/margin/get_margin_account_balance.py b/example/margin/get_margin_account_balance.py
new file mode 100644
index 0000000..5bf1a7f
--- /dev/null
+++ b/example/margin/get_margin_account_balance.py
@@ -0,0 +1,8 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = margin_client.get_margin_account_balance(symbol="eosusdt")
+LogInfo.output_list(list_obj)
+
diff --git a/example/margin/get_margin_loan_info.py b/example/margin/get_margin_loan_info.py
new file mode 100644
index 0000000..ff519f9
--- /dev/null
+++ b/example/margin/get_margin_loan_info.py
@@ -0,0 +1,8 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = margin_client.get_margin_loan_info(symbols="btcusdt,ethusdt,eosusdt")
+LogInfo.output_list(list_obj)
+
diff --git a/example/margin/get_margin_loan_orders.py b/example/margin/get_margin_loan_orders.py
new file mode 100644
index 0000000..0ae699f
--- /dev/null
+++ b/example/margin/get_margin_loan_orders.py
@@ -0,0 +1,8 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = margin_client.get_margin_loan_orders(symbol="eosusdt")
+LogInfo.output_list(list_obj)
+
diff --git a/example/margin/post_cross_margin_create_loan_orders.py b/example/margin/post_cross_margin_create_loan_orders.py
new file mode 100644
index 0000000..741409c
--- /dev/null
+++ b/example/margin/post_cross_margin_create_loan_orders.py
@@ -0,0 +1,29 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+amount = 100
+currency = "usdt"
+
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+# order_id = margin_client.post_cross_margin_create_loan_orders(currency=currency, amount=amount)
+# LogInfo.output("step 1: create loan order {id}".format(id=order_id))
+
+# to check not clearing loan orders
+list_obj = margin_client.get_cross_margin_loan_orders(currency=currency, state=LoanOrderState.ACCRUAL)
+LogInfo.output("step 2: loaning order information ")
+LogInfo.output_list(list_obj)
+
+if list_obj and len(list_obj):
+ for loan_order in list_obj:
+ # pay attention to Scientific Notation
+ repay_amount = float(loan_order.loan_balance) + float(loan_order.interest_balance)
+ LogInfo.output("repay loan order {id}, repay amount : {amount}".format(id=loan_order.id, amount=repay_amount))
+ result = margin_client.post_cross_margin_loan_order_repay(order_id=loan_order.id, amount=repay_amount)
+ LogInfo.output("step 3: repay loan order {id}, status : {status}, repay amount : {amount}".format(id=loan_order.id, status=result, amount=repay_amount))
+
+list_obj = margin_client.get_cross_margin_loan_orders(currency=currency, state=LoanOrderState.ACCRUAL)
+LogInfo.output("step 4: loaning order history ")
+LogInfo.output_list(list_obj)
diff --git a/example/margin/post_cross_margin_loan_order_repay.py b/example/margin/post_cross_margin_loan_order_repay.py
new file mode 100644
index 0000000..5f0a1d3
--- /dev/null
+++ b/example/margin/post_cross_margin_loan_order_repay.py
@@ -0,0 +1,19 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+
+# to check not clearing loan orders
+list_obj = margin_client.get_cross_margin_loan_orders(currency="usdt", state=LoanOrderState.ACCRUAL)
+LogInfo.output_list(list_obj)
+
+if list_obj and len(list_obj):
+ for loan_order in list_obj:
+ repay_amount = float(loan_order.loan_balance) + float(loan_order.interest_balance)
+ LogInfo.output("repay loan order {id} and amount {amount}".format(id=loan_order.id, amount=repay_amount))
+ order_id = margin_client.post_cross_margin_loan_order_repay(order_id=loan_order.id, amount=repay_amount)
+
+# to check not clearing loan orders
+list_obj = margin_client.get_cross_margin_loan_orders(currency="usdt", state=LoanOrderState.ACCRUAL)
+LogInfo.output_list(list_obj)
\ No newline at end of file
diff --git a/example/margin/post_cross_margin_transfer.py b/example/margin/post_cross_margin_transfer.py
new file mode 100644
index 0000000..44ebdc4
--- /dev/null
+++ b/example/margin/post_cross_margin_transfer.py
@@ -0,0 +1,12 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+transfer_id = margin_client.post_cross_margin_transfer_in(currency="eos", amount=5)
+LogInfo.output(transfer_id)
+
+transfer_id = margin_client.post_cross_margin_transfer_out(currency="eos", amount=5)
+LogInfo.output(transfer_id)
+
+
diff --git a/example/margin/post_general_repay_loan.py b/example/margin/post_general_repay_loan.py
new file mode 100644
index 0000000..cf2f918
--- /dev/null
+++ b/example/margin/post_general_repay_loan.py
@@ -0,0 +1,8 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import LogInfo
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+
+list_result = margin_client.post_general_repay_loan(account_id=g_account_id, currency="usdt", amount=1)
+LogInfo.output_list(list_result)
diff --git a/example/margin/post_margin_create_order.py b/example/margin/post_margin_create_order.py
new file mode 100644
index 0000000..69c0efe
--- /dev/null
+++ b/example/margin/post_margin_create_order.py
@@ -0,0 +1,25 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+import time
+
+loan_amount = 100
+interest_amount = 0.004083
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+loan_id = margin_client.post_create_margin_order(symbol="eosusdt", currency="usdt", amount=loan_amount)
+LogInfo.output("step 1: loan id : {id}".format(id=loan_id))
+
+time.sleep(2)
+
+repay_id = margin_client.post_repay_margin_order(loan_id=loan_id, amount=loan_amount + interest_amount)
+LogInfo.output("step 2: repay id : {id}".format(id=repay_id))
+
+list_obj = margin_client.get_margin_loan_orders(symbol="eosusdt", states=LoanOrderState.ACCRUAL)
+LogInfo.output("step 3: loaning order information")
+LogInfo.output_list(list_obj)
+
+
+list_obj = margin_client.get_margin_loan_orders(symbol="eosusdt")
+LogInfo.output("step 4: loan order history")
+LogInfo.output_list(list_obj)
\ No newline at end of file
diff --git a/example/margin/post_margin_repay_order.py b/example/margin/post_margin_repay_order.py
new file mode 100644
index 0000000..386f29a
--- /dev/null
+++ b/example/margin/post_margin_repay_order.py
@@ -0,0 +1,8 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+transfer_id = margin_client.post_repay_margin_order(loan_id=7440184, amount=100.004083)
+LogInfo.output("transfer id : {id}".format(id=transfer_id))
+
diff --git a/example/margin/post_margin_transfer_in.py b/example/margin/post_margin_transfer_in.py
new file mode 100644
index 0000000..5b8f787
--- /dev/null
+++ b/example/margin/post_margin_transfer_in.py
@@ -0,0 +1,8 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+transfer_id = margin_client.post_transfer_in_margin(symbol="eosusdt", currency="usdt", amount=20)
+LogInfo.output("transfer id : {id}".format(id=transfer_id))
+
diff --git a/example/margin/post_margin_transfer_out.py b/example/margin/post_margin_transfer_out.py
new file mode 100644
index 0000000..6d23f81
--- /dev/null
+++ b/example/margin/post_margin_transfer_out.py
@@ -0,0 +1,7 @@
+from huobi.client.margin import MarginClient
+from huobi.constant import *
+from huobi.utils import *
+
+margin_client = MarginClient(api_key=g_api_key, secret_key=g_secret_key)
+transfer_id = margin_client.post_transfer_out_margin(symbol="eosusdt", currency="usdt", amount=20)
+LogInfo.output("transfer id : {id}".format(id=transfer_id))
diff --git a/example/market/get_candlestick.py b/example/market/get_candlestick.py
new file mode 100644
index 0000000..9b22f16
--- /dev/null
+++ b/example/market/get_candlestick.py
@@ -0,0 +1,26 @@
+from huobi.client.market import MarketClient
+from huobi.constant import *
+from huobi.utils import *
+
+market_client = MarketClient(init_log=True)
+interval = CandlestickInterval.MIN5
+symbol = "ethusdt"
+list_obj = market_client.get_candlestick(symbol, interval, 10)
+LogInfo.output("---- {interval} candlestick for {symbol} ----".format(interval=interval, symbol=symbol))
+LogInfo.output_list(list_obj)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/market/get_history_trade.py b/example/market/get_history_trade.py
new file mode 100644
index 0000000..e039c34
--- /dev/null
+++ b/example/market/get_history_trade.py
@@ -0,0 +1,5 @@
+from huobi.client.market import MarketClient
+from huobi.utils import *
+market_client = MarketClient()
+list_obj = market_client.get_history_trade("btcusdt", 6)
+LogInfo.output_list(list_obj)
diff --git a/example/market/get_market_detail.py b/example/market/get_market_detail.py
new file mode 100644
index 0000000..b7f1307
--- /dev/null
+++ b/example/market/get_market_detail.py
@@ -0,0 +1,8 @@
+from huobi.client.market import MarketClient
+
+market_client = MarketClient()
+obj = market_client.get_market_detail("btcusdt")
+obj.print_object()
+
+
+
diff --git a/example/market/get_market_detail_merged.py b/example/market/get_market_detail_merged.py
new file mode 100644
index 0000000..a247be0
--- /dev/null
+++ b/example/market/get_market_detail_merged.py
@@ -0,0 +1,9 @@
+from huobi.client.market import MarketClient
+from huobi.constant import *
+
+market_client = MarketClient()
+obj = market_client.get_market_detail_merged("btcusdt")
+obj.print_object()
+
+
+
diff --git a/example/market/get_market_tickers.py b/example/market/get_market_tickers.py
new file mode 100644
index 0000000..4270436
--- /dev/null
+++ b/example/market/get_market_tickers.py
@@ -0,0 +1,23 @@
+from huobi.client.market import MarketClient
+from huobi.constant import *
+from huobi.utils import *
+
+market_client = MarketClient(init_log=True)
+list_obj = market_client.get_market_tickers()
+LogInfo.output_list(list_obj)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/market/get_market_trade.py b/example/market/get_market_trade.py
new file mode 100644
index 0000000..6f4d89d
--- /dev/null
+++ b/example/market/get_market_trade.py
@@ -0,0 +1,18 @@
+from huobi.client.market import MarketClient
+from huobi.utils import *
+
+market_client = MarketClient()
+list_obj = market_client.get_market_trade(symbol="eosusdt")
+LogInfo.output_list(list_obj)
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/market/get_pricedepth.py b/example/market/get_pricedepth.py
new file mode 100644
index 0000000..a15468c
--- /dev/null
+++ b/example/market/get_pricedepth.py
@@ -0,0 +1,21 @@
+
+from huobi.client.market import MarketClient
+from huobi.utils import *
+
+
+market_client = MarketClient()
+symbol = "btcusdt"
+depth_size = 6
+depth = market_client.get_pricedepth(symbol, DepthStep.STEP0, depth_size)
+LogInfo.output("---- Top {size} bids ----".format(size=len(depth.bids)))
+i = 0
+for entry in depth.bids:
+ i = i + 1
+ LogInfo.output(str(i) + ": price: " + str(entry.price) + ", amount: " + str(entry.amount))
+
+LogInfo.output("---- Top {size} asks ----".format(size=len(depth.asks)))
+
+i = 0
+for entry in depth.asks:
+ i = i + 1
+ LogInfo.output(str(i) + ": price: " + str(entry.price) + ", amount: " + str(entry.amount))
\ No newline at end of file
diff --git a/example/market/req_candlestick.py b/example/market/req_candlestick.py
new file mode 100644
index 0000000..bee2823
--- /dev/null
+++ b/example/market/req_candlestick.py
@@ -0,0 +1,16 @@
+from huobi.client.market import MarketClient
+from huobi.constant import *
+from huobi.exception.huobi_api_exception import HuobiApiException
+
+def callback(candlestick_req: 'CandlestickReq'):
+ candlestick_req.print_object()
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+sub_client = MarketClient(init_log=True)
+#sub_client.request_candlestick_event("btcusdt", CandlestickInterval.MIN1, callback, from_ts_second=None, end_ts_second=None, error_handler=None)
+#sub_client.request_candlestick_event("btcusdt", CandlestickInterval.MIN1, callback, from_ts_second=1571124360, end_ts_second=1571129820)
+#sub_client.request_candlestick_event("btcusdt", CandlestickInterval.MIN1, callback, from_ts_second=1569361140, end_ts_second=0)
+#sub_client.request_candlestick_event("btcusdt", CandlestickInterval.MIN1, callback, from_ts_second=1569379980)
+sub_client.req_candlestick("btcusdt", CandlestickInterval.MIN1, callback)
diff --git a/example/market/req_market_detail.py b/example/market/req_market_detail.py
new file mode 100644
index 0000000..bc63b99
--- /dev/null
+++ b/example/market/req_market_detail.py
@@ -0,0 +1,12 @@
+from huobi.client.market import MarketClient
+from huobi.model.market import *
+
+
+
+def callback(obj_event: 'MarketDetailReq'):
+ obj_event.print_object()
+ print()
+
+
+sub_client = MarketClient()
+sub_client.req_market_detail("btcusdt", callback)
diff --git a/example/market/req_mbp.py b/example/market/req_mbp.py
new file mode 100644
index 0000000..b2c3232
--- /dev/null
+++ b/example/market/req_mbp.py
@@ -0,0 +1,14 @@
+from huobi.client.market import MarketClient
+from huobi.constant import *
+
+
+def callback(mbp_event: 'MbpIncreaseEvent'):
+ mbp_event.print_object()
+
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+
+market_client = MarketClient(init_log=True)
+market_client.req_mbp("btcusdt", MbpLevel.MBP5, callback, error)
diff --git a/example/market/req_pricedepth.py b/example/market/req_pricedepth.py
new file mode 100644
index 0000000..3cc938a
--- /dev/null
+++ b/example/market/req_pricedepth.py
@@ -0,0 +1,15 @@
+
+from huobi.client.market import MarketClient
+from huobi.constant import DepthStep
+
+
+def callback(price_depth_req: 'PriceDepthReq'):
+ price_depth_req.print_object()
+
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+
+sub_client = MarketClient()
+sub_client.req_pricedepth("btcusdt", DepthStep.STEP0, callback, error)
diff --git a/example/market/req_trade_detail.py b/example/market/req_trade_detail.py
new file mode 100644
index 0000000..cc67fe9
--- /dev/null
+++ b/example/market/req_trade_detail.py
@@ -0,0 +1,15 @@
+from huobi.client.market import MarketClient
+
+
+
+def callback(trade_req: 'TradeDetailReq'):
+ print("---- trade_event: ----")
+ trade_req.print_object()
+ print()
+
+
+
+market_client = MarketClient()
+market_client.req_trade_detail("btcusdt,eosusdt", callback)
+
+
diff --git a/example/market/sub_candlestick.py b/example/market/sub_candlestick.py
new file mode 100644
index 0000000..732b44c
--- /dev/null
+++ b/example/market/sub_candlestick.py
@@ -0,0 +1,18 @@
+
+from huobi.client.market import MarketClient
+from huobi.constant import *
+from huobi.exception.huobi_api_exception import HuobiApiException
+from huobi.model.market.candlestick_event import CandlestickEvent
+
+
+def callback(candlestick_event: 'CandlestickEvent'):
+ candlestick_event.print_object()
+ print("\n")
+
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+market_client = MarketClient()
+market_client.sub_candlestick("btcusdt,ethusdt", CandlestickInterval.MIN1, callback, error)
+
diff --git a/example/market/sub_market_detail.py b/example/market/sub_market_detail.py
new file mode 100644
index 0000000..a567fba
--- /dev/null
+++ b/example/market/sub_market_detail.py
@@ -0,0 +1,11 @@
+from huobi.client.market import MarketClient
+from huobi.model.market import *
+
+
+def callback(obj_event: 'MarketDetailEvent'):
+ obj_event.print_object()
+ print()
+
+
+market_client = MarketClient()
+market_client.sub_market_detail("btcusdt", callback)
\ No newline at end of file
diff --git a/example/market/sub_mbp_full.py b/example/market/sub_mbp_full.py
new file mode 100644
index 0000000..e1cd3cb
--- /dev/null
+++ b/example/market/sub_mbp_full.py
@@ -0,0 +1,14 @@
+from huobi.client.market import MarketClient
+from huobi.constant import *
+
+
+def callback(mbp_event: 'MbpFullEvent'):
+ mbp_event.print_object()
+
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+
+market_client = MarketClient(init_log=True)
+market_client.sub_mbp_full("btcusdt,eosusdt", MbpLevel.MBP5, callback, error)
diff --git a/example/market/sub_mbp_increase.py b/example/market/sub_mbp_increase.py
new file mode 100644
index 0000000..044781e
--- /dev/null
+++ b/example/market/sub_mbp_increase.py
@@ -0,0 +1,14 @@
+from huobi.client.market import MarketClient
+from huobi.constant import *
+
+
+def callback(mbp_event: 'MbpIncreaseEvent'):
+ mbp_event.print_object()
+
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+
+market_client = MarketClient(init_log=True)
+market_client.sub_mbp_increase("btcusdt,eosusdt", MbpLevel.MBP5, callback, error)
diff --git a/example/market/sub_pricedepth.py b/example/market/sub_pricedepth.py
new file mode 100644
index 0000000..bca45d9
--- /dev/null
+++ b/example/market/sub_pricedepth.py
@@ -0,0 +1,18 @@
+
+from huobi.client.market import MarketClient
+from huobi.constant import *
+
+
+
+def callback(price_depth_event: 'PriceDepthEvent'):
+ price_depth_event.print_object()
+
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+
+market_client = MarketClient()
+market_client.sub_pricedepth("btcusdt", DepthStep.STEP0, callback, error)
+market_client.sub_pricedepth("eosusdt", DepthStep.STEP0, callback, error)
+market_client.sub_pricedepth("ethusdt", DepthStep.STEP0, callback, error)
diff --git a/example/market/sub_pricedepth_bbo.py b/example/market/sub_pricedepth_bbo.py
new file mode 100644
index 0000000..f1a9704
--- /dev/null
+++ b/example/market/sub_pricedepth_bbo.py
@@ -0,0 +1,15 @@
+
+from huobi.client.market import MarketClient
+
+
+def callback(price_depth_event: 'PriceDepthBboEvent'):
+ price_depth_event.print_object()
+ print()
+
+
+
+def error(e: 'HuobiApiException'):
+ print(e.error_code + e.error_message)
+
+market_client = MarketClient()
+market_client.sub_pricedepth_bbo("btcusdt", callback, error)
diff --git a/example/market/sub_trade_detail.py b/example/market/sub_trade_detail.py
new file mode 100644
index 0000000..a80e3a9
--- /dev/null
+++ b/example/market/sub_trade_detail.py
@@ -0,0 +1,13 @@
+from huobi.client.market import MarketClient
+
+
+def callback(trade_event: 'TradeDetailEvent'):
+ print("---- trade_event: ----")
+ trade_event.print_object()
+ print()
+
+
+
+market_client = MarketClient(init_log=True)
+market_client.sub_trade_detail("btcusdt,eosusdt", callback)
+
diff --git a/example/subuser/get_uid.py b/example/subuser/get_uid.py
new file mode 100644
index 0000000..9bea859
--- /dev/null
+++ b/example/subuser/get_uid.py
@@ -0,0 +1,10 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+from huobi.utils import *
+
+from example.api_key import *
+
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+uid = subuser_client.get_uid()
+LogInfo.output(uid)
diff --git a/example/subuser/get_user_apikey_info.py b/example/subuser/get_user_apikey_info.py
new file mode 100644
index 0000000..870dd67
--- /dev/null
+++ b/example/subuser/get_user_apikey_info.py
@@ -0,0 +1,7 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+from huobi.utils import *
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+apikey_info = subuser_client.get_user_apikey_info(122946475)
+LogInfo.output_list(apikey_info)
diff --git a/example/subuser/post_create_subuser.py b/example/subuser/post_create_subuser.py
new file mode 100644
index 0000000..49b3909
--- /dev/null
+++ b/example/subuser/post_create_subuser.py
@@ -0,0 +1,17 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+from huobi.utils import *
+import string
+import random
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+userName = ''.join(random.choices(string.ascii_uppercase + string.digits, k=7))
+params = {"userList": [
+ {
+ "userName": userName,
+ "note": "huobi"
+ }
+]}
+
+userList = subuser_client.post_create_subuser(params)
+LogInfo.output_list(userList)
diff --git a/example/subuser/post_set_subuser_transferability.py b/example/subuser/post_set_subuser_transferability.py
new file mode 100644
index 0000000..39f6f7b
--- /dev/null
+++ b/example/subuser/post_set_subuser_transferability.py
@@ -0,0 +1,9 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+from huobi.utils import *
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+sub_uids = '122946475'
+
+transferability_result = subuser_client.post_set_subuser_transferability(sub_uids, False)
+LogInfo.output_list(transferability_result)
diff --git a/example/subuser/post_subuser_apikey_deletion.py b/example/subuser/post_subuser_apikey_deletion.py
new file mode 100644
index 0000000..b493619
--- /dev/null
+++ b/example/subuser/post_subuser_apikey_deletion.py
@@ -0,0 +1,10 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+from huobi.utils import *
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+sub_uid = '122946475'
+access_key = '7ab679d7-b9fee8ed-9cd4cd8a-bgbfh5tv3f'
+
+result = subuser_client.post_subuser_apikey_deletion(sub_uid, access_key)
+LogInfo.output(result)
diff --git a/example/subuser/post_subuser_apikey_generation.py b/example/subuser/post_subuser_apikey_generation.py
new file mode 100644
index 0000000..f2d65b2
--- /dev/null
+++ b/example/subuser/post_subuser_apikey_generation.py
@@ -0,0 +1,13 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+
+otp_token = '746316'
+sub_uid = 122946475
+note = "huobi_subuser"
+permission = 'readOnly'
+# ip_addresses = ''
+
+result = subuser_client.post_subuser_apikey_generate(otp_token, sub_uid, note, permission)
+result.print_object()
diff --git a/example/subuser/post_subuser_apikey_modification.py b/example/subuser/post_subuser_apikey_modification.py
new file mode 100644
index 0000000..63a1c51
--- /dev/null
+++ b/example/subuser/post_subuser_apikey_modification.py
@@ -0,0 +1,12 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+
+subUid = 122946475
+access_key = "abc"
+note = "test"
+permission = 'readOnly,trade'
+
+result = subuser_client.post_subuser_apikey_modification(subUid, access_key, permission=permission, note=note)
+result.print_object()
diff --git a/example/subuser/post_trade_market.py b/example/subuser/post_trade_market.py
new file mode 100644
index 0000000..dd0df19
--- /dev/null
+++ b/example/subuser/post_trade_market.py
@@ -0,0 +1,12 @@
+from huobi.client.subuser import SubuserClient
+from huobi.constant import *
+from huobi.utils import *
+
+subuser_client = SubuserClient(api_key=g_api_key, secret_key=g_secret_key)
+
+subUids = '159284259'
+accountType = SubuserTradePrivilegeType.MARGIN
+activation = SubUserTradeStatus.DEACTIVATED
+
+subUserList = subuser_client.post_set_tradable_market(subUids, accountType, activation)
+LogInfo.output_list(subUserList)
diff --git a/example/trade/get_feerate.py b/example/trade/get_feerate.py
new file mode 100644
index 0000000..d605e6e
--- /dev/null
+++ b/example/trade/get_feerate.py
@@ -0,0 +1,10 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+from example.api_key import *
+
+
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = trade_client.get_feerate(symbols="htusdt,btcusdt,eosusdt")
+LogInfo.output_list(list_obj)
\ No newline at end of file
diff --git a/example/trade/get_history_orders_in_48hours.py b/example/trade/get_history_orders_in_48hours.py
new file mode 100644
index 0000000..248e65a
--- /dev/null
+++ b/example/trade/get_history_orders_in_48hours.py
@@ -0,0 +1,26 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+from example.api_key import *
+
+symbol_test_list = ["linkusdt"]
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+for symbol_test in symbol_test_list:
+ list_obj = trade_client.get_history_orders(symbol=symbol_test, start_time=None, end_time=None, size=20, direct=None)
+ LogInfo.output_list(list_obj)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/trade/get_matchresult.py b/example/trade/get_matchresult.py
new file mode 100644
index 0000000..ece70cf
--- /dev/null
+++ b/example/trade/get_matchresult.py
@@ -0,0 +1,11 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = trade_client.get_match_result(symbol="trxusdt", size=5, direct=QueryDirection.NEXT)
+LogInfo.output_list(list_obj)
+
+list_obj = trade_client.get_match_result(symbol="eosusdt", size=5)
+LogInfo.output_list(list_obj)
\ No newline at end of file
diff --git a/example/trade/get_matchresult_by_order_id.py b/example/trade/get_matchresult_by_order_id.py
new file mode 100644
index 0000000..843680f
--- /dev/null
+++ b/example/trade/get_matchresult_by_order_id.py
@@ -0,0 +1,11 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+
+order_id_test = 87939085540
+
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = trade_client.get_match_results_by_order_id(order_id=order_id_test)
+LogInfo.output_list(list_obj)
\ No newline at end of file
diff --git a/example/trade/get_open_orders.py b/example/trade/get_open_orders.py
new file mode 100644
index 0000000..246dd97
--- /dev/null
+++ b/example/trade/get_open_orders.py
@@ -0,0 +1,25 @@
+from huobi.client.account import AccountClient
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+from example.api_key import *
+
+
+symbol = "btcusdt"
+
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+account_client = AccountClient(api_key=g_api_key,secret_key=g_secret_key)
+
+account_spot = account_client.get_account_by_type_and_symbol(account_type=AccountType.SPOT, symbol=None)
+account_id_test = account_spot.id
+
+direct_tmp = QueryDirection.NEXT
+LogInfo.output("==============test case 1 for {direct}===============".format(direct=direct_tmp))
+list_obj = trade_client.get_open_orders(symbol=symbol, account_id=account_id_test, direct=direct_tmp)
+LogInfo.output_list(list_obj)
+
+direct_tmp = QueryDirection.PREV
+LogInfo.output("==============test case 2 for {direct}===============".format(direct=direct_tmp))
+list_obj = trade_client.get_open_orders(symbol=symbol, account_id=account_id_test, direct=direct_tmp)
+LogInfo.output_list(list_obj)
diff --git a/example/trade/get_order.py b/example/trade/get_order.py
new file mode 100644
index 0000000..fedfe56
--- /dev/null
+++ b/example/trade/get_order.py
@@ -0,0 +1,10 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+order_id = 25943298415466
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+orderObj = trade_client.get_order(order_id=order_id)
+LogInfo.output("======= get order by order id : {order_id} =======".format(order_id=order_id))
+orderObj.print_object()
+
diff --git a/example/trade/get_order_by_client_order_id.py b/example/trade/get_order_by_client_order_id.py
new file mode 100644
index 0000000..9c5bdda
--- /dev/null
+++ b/example/trade/get_order_by_client_order_id.py
@@ -0,0 +1,12 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+client_order_id_test = "xxxxxx" # unique id in 24hours
+
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+orderObj = trade_client.get_order_by_client_order_id(client_order_id=client_order_id_test)
+LogInfo.output("======= get order by client order id : {client_id} =======".format(client_id=client_order_id_test))
+orderObj.print_object()
+
+
diff --git a/example/trade/get_orders.py b/example/trade/get_orders.py
new file mode 100644
index 0000000..7ddd666
--- /dev/null
+++ b/example/trade/get_orders.py
@@ -0,0 +1,27 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+from example.api_key import *
+
+symbol = "linkusdt"
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = trade_client.get_orders(symbol=symbol, order_state=OrderState.FILLED,
+ order_type=OrderType.BUY_LIMIT, start_date=None, end_date=None,
+ start_id=None, size=None, direct=QueryDirection.PREV)
+
+LogInfo.output("===== step 1 ==== {symbol} {count} orders found".format(symbol=symbol, count=len(list_obj)))
+LogInfo.output_list(list_obj)
+
+symbol = "linkusdt"
+list_obj = trade_client.get_orders(symbol=symbol, order_state=OrderState.CANCELED,
+ order_type=OrderType.BUY_LIMIT, start_date="2021-01-21", end_date=None,
+ start_id=None, size=None, direct=QueryDirection.PREV)
+LogInfo.output("===== step 2 ==== {symbol} {count} canceled buy limit orders found".format(symbol=symbol, count=len(list_obj)))
+LogInfo.output_list(list_obj)
+
+list_obj = trade_client.get_orders(symbol=symbol, order_state=OrderState.FILLED,
+ order_type=None, start_date=None, end_date=None,
+ start_id=None, size=None, direct=QueryDirection.PREV)
+LogInfo.output("===== step 3 ==== {symbol} {count} filled orders found".format(symbol=symbol, count=len(list_obj)))
+LogInfo.output_list(list_obj)
diff --git a/example/trade/get_transact_feerate.py b/example/trade/get_transact_feerate.py
new file mode 100644
index 0000000..6563a7e
--- /dev/null
+++ b/example/trade/get_transact_feerate.py
@@ -0,0 +1,9 @@
+
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+list_obj = trade_client.get_transact_feerate(symbols="htusdt,btcusdt,eosusdt")
+LogInfo.output_list(list_obj)
\ No newline at end of file
diff --git a/example/trade/post_batch_cancel_open_order.py b/example/trade/post_batch_cancel_open_order.py
new file mode 100644
index 0000000..464a703
--- /dev/null
+++ b/example/trade/post_batch_cancel_open_order.py
@@ -0,0 +1,8 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+
+
+# cancel all the open orders under account
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+result = trade_client.cancel_open_orders(account_id=g_account_id)
+result.print_object()
diff --git a/example/trade/post_batch_cancel_order.py b/example/trade/post_batch_cancel_order.py
new file mode 100644
index 0000000..ea2d982
--- /dev/null
+++ b/example/trade/post_batch_cancel_order.py
@@ -0,0 +1,21 @@
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+trade_client = TradeClient(api_key=g_api_key, secret_key=g_secret_key)
+symbol_test = "eosusdt"
+
+i = 0
+n = 3
+order_id_list = []
+while(i list:
+ from huobi.service.account.get_balance import GetBalanceService
+ """
+ Get the balance of a all accounts.
+
+ :return: The information of all account balance.
+ """
+ server_url = get_default_server_url(self.__kwargs.get("url"))
+ tasks = []
+ account_obj_map = {}
+ accounts = self.get_accounts()
+ account_balance_list = []
+ account_balance_json_map = {}
+ for account_item in accounts:
+ account_obj_map[account_item.id] = account_item
+ balance_params = {"account-id": account_item.id}
+ balance_request = GetBalanceService(balance_params).get_request(**self.__kwargs)
+ balance_url = server_url + balance_request.url
+ tasks.append(asyncio.ensure_future(
+ self.async_get_account_balance(balance_url, account_item.id, account_balance_json_map)))
+
+ loop = asyncio.get_event_loop()
+ try:
+ loop.run_until_complete(asyncio.wait(tasks))
+ except Exception as ee:
+ print(ee)
+ finally:
+ # loop.close() #for thread safe, the event loop can't be closed
+ pass
+
+ for account_id, account_balance_json in account_balance_json_map.items():
+ account_balance = AccountBalance.json_parse(account_balance_json.get("data", {}))
+ account_obj_tmp = account_obj_map.get(account_id, None)
+ account_balance.subtype = None if account_obj_tmp is None else account_obj_tmp.subtype
+ account_balance_list.append(account_balance)
+
+ del account_balance_json_map
+ del tasks
+ return account_balance_list
+
+ def get_account_balance_by_subuid(self, sub_uid):
+ """
+ Get account balance of a sub-account.
+
+ :param sub_uid: the specified sub account id to get balance for.
+ :return: the balance of a sub-account specified by sub-account uid.
+ """
+ check_should_not_none(sub_uid, "sub-uid")
+ params = {
+ "sub-uid": sub_uid
+ }
+ from huobi.service.account.get_account_balance_by_subuid import GetAccountBalanceBySubUidService
+ return GetAccountBalanceBySubUidService(params).request(**self.__kwargs)
+
+ def get_aggregated_subuser_balance(self):
+ """
+ Get the aggregated balance of all sub-accounts of the current user.
+
+ :return: The balance of all the sub-account aggregated.
+ """
+ params = {}
+ from huobi.service.account.get_aggregate_subuser_balance import GetAggregateSubUserBalanceService
+ return GetAggregateSubUserBalanceService(params).request(**self.__kwargs)
+
+ def transfer_between_parent_and_subuser(self, sub_uid: 'int', currency: 'str', amount: 'float',
+ transfer_type: 'TransferMasterType'):
+ """
+ Transfer Asset between Parent and Sub Account.
+
+ :param sub_uid: The target sub account uid to transfer to or from. (mandatory)
+ :param currency: The crypto currency to transfer. (mandatory)
+ :param amount: The amount of asset to transfer. (mandatory)
+ :param transfer_type: The type of transfer, see {@link TransferMasterType} (mandatory)
+ :return: The order id.
+ """
+ check_currency(currency)
+ check_should_not_none(sub_uid, "sub-uid")
+ check_should_not_none(amount, "amount")
+ check_should_not_none(transfer_type, "type")
+
+ params = {
+ "sub-uid": sub_uid,
+ "currency": currency,
+ "amount": amount,
+ "type": transfer_type
+ }
+ from huobi.service.account.post_subaccount_transfer import PostSubaccountTransferService
+ return PostSubaccountTransferService(params).request(**self.__kwargs)
+
+ def sub_account_update(self, mode: 'AccountBalanceMode', callback, error_handler=None):
+ """
+ Subscribe accounts update
+
+ :param mode: subscribe mode
+ "0" : for balance
+ "1" : for available and balance
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+
+ check_should_not_none(callback, "callback")
+ if str(mode) == AccountBalanceMode.TOTAL:
+ mode = AccountBalanceMode.TOTAL
+ else:
+ mode = AccountBalanceMode.BALANCE
+
+ params = {
+ "mode": mode,
+ }
+
+ from huobi.service.account.sub_account_update_v2 import SubAccountUpdateV2Service
+ SubAccountUpdateV2Service(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_account_balance(self, callback, client_req_id=None, error_handler=None):
+ """
+ Subscribe account changing event. If the balance is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param client_req_id: client request ID
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(account_event: 'AccountEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+
+ check_should_not_none(callback, "callback")
+ params = {
+ "client_req_id": client_req_id
+ }
+
+ from huobi.service.account.req_account_balance import ReqAccountBalanceService
+ ReqAccountBalanceService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def transfer_between_futures_and_pro(self, currency: 'str', amount: 'float',
+ transfer_type: 'TransferFuturesPro') -> int:
+ """
+ Transfer Asset between Futures and Contract.
+
+ :param currency: The crypto currency to transfer. (mandatory)
+ :param amount: The amount of asset to transfer. (mandatory)
+ :param transfer_type: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (mandatory)
+ :return: The order id.
+ """
+
+ check_currency(currency)
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+ check_should_not_none(transfer_type, "transfer_type")
+ params = {
+ "currency": currency,
+ "amount": amount,
+ "type": transfer_type
+ }
+
+ from huobi.service.account.post_futures_and_pro_transfer import PostTransferBetweenFuturesAndProService
+ return PostTransferBetweenFuturesAndProService(params).request(**self.__kwargs)
+
+ def get_account_balance_by_subuid(self, sub_uid):
+ """
+ Get account balance of a sub-account.
+
+ :param sub_uid: the specified sub account id to get balance for.
+ :return: the balance of a sub-account specified by sub-account uid.
+ """
+ check_should_not_none(sub_uid, "sub-uid")
+ params = {
+ "sub-uid": sub_uid
+ }
+ from huobi.service.account.get_account_balance_by_subuid import GetAccountBalanceBySubUidService
+ return GetAccountBalanceBySubUidService(params).request(**self.__kwargs)
+
+ def get_account_history(self, account_id: 'int', currency: 'str' = None,
+ transact_types: 'str' = None, start_time: 'int' = None, end_time: 'int' = None,
+ sort: 'str' = None, size: 'int' = None):
+ """
+ get account change record
+ :param account_id: account id (mandatory)
+ :param currency: currency as "btc,eth" (optional)
+ :param transact_types: see AccountTransactType, the value can be "trade" (交易),"etf"(ETF申购), "transact-fee"(交易手续费), "deduction"(手续费抵扣), "transfer"(划转), "credit"(借币), "liquidation"(清仓), "interest"(币息), "deposit"(充币),"withdraw"(提币), "withdraw-fee"(提币手续费), "exchange"(兑换), "other-types"(其他) (optional)
+ :param start_time&end_time: for time range to search (optional)
+ :param sort: see SortDesc, "asc" or "desc" (optional)
+ :param size: page size (optional)
+
+ :return: account change record list.
+ """
+ check_should_not_none(account_id, "account-id")
+ params = {
+ "account-id": account_id,
+ "currency": currency,
+ "transact-types": transact_types,
+ "start-time": start_time,
+ "end-time": end_time,
+ "sort": sort,
+ "size": size
+ }
+ from huobi.service.account.get_account_history import GetAccountHistoryService
+ return GetAccountHistoryService(params).request(**self.__kwargs)
+
+ def post_sub_uid_management(self, sub_uid: 'int', action: 'str'):
+ """
+ use to freeze or unfreeze the sub uid
+
+ :return: user and status.
+ """
+
+ check_should_not_none(sub_uid, "subUid")
+ check_should_not_none(action, "action")
+
+ params = {
+ "subUid": sub_uid,
+ "action": action
+ }
+ from huobi.service.account.post_sub_uid_management import PostSubUidManagementService
+ return PostSubUidManagementService(params).request(**self.__kwargs)
+
+ def get_account_ledger(self, account_id: 'int', currency: 'str' = None, transact_types: 'str' = None,
+ start_time: 'int' = None, end_time: 'int' = None, sort: 'str' = None, limit: 'int' = None,
+ from_id: 'int' = None) -> list:
+ """
+ get account ledger
+ :param account_id: account id (mandatory)
+ :param currency: currency as "btc,eth" (optional)
+ :param transact_types: see AccountTransactType, the value can be "trade" (交易),"etf"(ETF申购), "transact-fee"(交易手续费), "deduction"(手续费抵扣), "transfer"(划转), "credit"(借币), "liquidation"(清仓), "interest"(币息), "deposit"(充币),"withdraw"(提币), "withdraw-fee"(提币手续费), "exchange"(兑换), "other-types"(其他) (optional)
+ :param start_time&end_time: for time range to search (optional)
+ :param sort: see SortDesc, "asc" or "desc" (optional)
+ :param size: page size (optional)
+ :return: account ledger list.
+ """
+
+ check_should_not_none(account_id, "accountId")
+
+ params = {
+ "accountId": account_id,
+ "currency": currency,
+ "transactTypes": transact_types,
+ "startTime": start_time,
+ "endTime": end_time,
+ "sort": sort,
+ "limit": limit,
+ "fromId": from_id
+ }
+ from huobi.service.account.get_account_ledger import GetAccountLedgerService
+ return GetAccountLedgerService(params).request(**self.__kwargs)
+
+ def post_account_transfer(self, from_user: 'int', from_account_type: 'str', from_account: 'int', to_user: 'int',
+ to_account_type: 'str', to_account: 'int', currency: 'str', amount: 'str'):
+ check_should_not_none(from_user, "from-user")
+ check_should_not_none(from_account_type, "from-account-type")
+ check_should_not_none(from_account, "from_account")
+ check_should_not_none(to_user, "to-user")
+ check_should_not_none(to_account, "to-account")
+ check_should_not_none(to_account_type, "to-account")
+ check_should_not_none(currency, "currency")
+
+ check_in_list(from_account_type, [AccountType.SPOT], "from_account_type")
+ check_in_list(to_account_type, [AccountType.SPOT], "to_account_type")
+
+ params = {
+ "from-user": from_user,
+ "from-account-type": from_account_type,
+ "from-account": from_account,
+ "to-user": to_user,
+ "to-account-type": to_account_type,
+ "to-account": to_account,
+ "currency": currency,
+ "amount": amount
+ }
+ from huobi.service.account.post_account_transfer import PostAccountTransferService
+ return PostAccountTransferService(params).request(**self.__kwargs)
+
+ def get_account_asset_valuation(self, account_type, valuation_currency: 'str' = None, sub_uid: 'str' = None):
+ check_should_not_none(account_type, "account-type")
+
+ params = {
+ "accountType": account_type,
+ "valuationCurrency": valuation_currency.upper(),
+ "subUid": sub_uid
+ }
+ from huobi.service.account.get_account_asset_valuation import GetAccountAssetValuationService
+ return GetAccountAssetValuationService(params).request(**self.__kwargs)
+
+ def get_account_point(self, sub_uid: 'str' = None):
+ params = {
+ "subUid": sub_uid
+ }
+
+ from huobi.service.account.get_account_point import GetAccountPointService
+ return GetAccountPointService(params).request(**self.__kwargs)
+
+ def post_point_transfer(self, from_uid: 'str', to_uid: 'str', group_id: 'str', amount: 'str'):
+
+ params = {
+ "fromUid": from_uid,
+ "toUid": to_uid,
+ "groupId": group_id,
+ "amount": amount
+ }
+
+ from huobi.service.account.post_point_transfer import PostPointTransferService
+ return PostPointTransferService(params).request(**self.__kwargs)
diff --git a/huobi/client/algo.py b/huobi/client/algo.py
new file mode 100644
index 0000000..b684096
--- /dev/null
+++ b/huobi/client/algo.py
@@ -0,0 +1,127 @@
+from huobi.model.algo import *
+from huobi.utils.input_checker import *
+
+
+class AlgoClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def create_order(self, account_id: 'int', symbol: 'str', order_side: 'OrderSide', order_type: 'OrderType',
+ client_order_id: 'str', stop_price: 'str', order_price: 'str' = None, order_size: 'str' = None,
+ order_value: 'str' = None, time_in_force: 'str' = None, trailing_rate: 'str' = None) -> int:
+ """
+ Make an algo order in huobi.
+ :param account_id: Account id. (mandatory)
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param order_side: the Order side, possible values: buy,sell. (mandatory)
+ :param order_type: The order type, possible values: limit, market. (mandatory)
+ :param stop_price: The stop price. (mandatory)
+ :param order_price: The limit price of limit order, only needed for limit order. (mandatory for buy-limit, sell-limit, buy-limit-maker and sell-limit-maker)
+ :param order_size: The amount of market order only
+ :param order_value: for market buy order only
+ :param stop_price: Price for auto sell to get the max benefit
+ :param time_in_force: gtc(invalid for orderType=market), boc(invalid orderType=market),ioc,fok(invalid for orderType=market)
+ :param trailing_rate: for trailing orders only
+ :param client_order_id: unique Id which is user defined and must be unique in recent 24 hours
+ """
+
+ params = self.create_order_param_check(symbol, account_id, order_side, order_type, stop_price, order_price,
+ order_size, order_value, time_in_force, trailing_rate, client_order_id)
+ from huobi.service.algo.post_create_order import PostCreateOrderService
+ return PostCreateOrderService(params).request(**self.__kwargs)
+
+ def cancel_orders(self, client_order_ids) -> CancelOrderResult:
+ check_should_not_none(client_order_ids, "clientOrderIds")
+
+ params = {
+ "clientOrderIds": client_order_ids
+ }
+ from huobi.service.algo.post_cancel_orders import PostCancelOrderService
+ return PostCancelOrderService(params).request(**self.__kwargs)
+
+ def get_open_orders(self, account_id: 'str' = None, symbol: 'str' = None, order_side: 'OrderSide' = None,
+ order_type: 'AlgoOrderType' = None, sort: 'SortDesc' = None, limit: 'int' = 100,
+ from_id: 'int' = None):
+
+ params = {
+ "accountId": account_id,
+ "symbol": symbol,
+ "orderSide": order_side,
+ "orderType": order_type,
+ "sort": sort,
+ "limit": limit,
+ "fromId": from_id
+ }
+ from huobi.service.algo.get_open_orders import GetOpenOrdersService
+ return GetOpenOrdersService(params).request(**self.__kwargs)
+
+ def get_order_history(self, symbol: 'str', order_status: 'AlgoOrderStatus', account_id: 'str' = None,
+ order_side: 'OrderSide' = None, order_type: 'AlgoOrderType' = None, start_time: 'int' = None,
+ end_time: 'int' = None, sort: 'SortDesc' = SortDesc.DESC, limit: 'int' = 100,
+ from_id: 'int' = None):
+
+ params = {
+ "symbol": symbol,
+ "accountId": account_id,
+ "orderSide": order_side,
+ "orderType": order_type,
+ "orderStatus": order_status,
+ "startTime": start_time,
+ "endTime": end_time,
+ "sort": sort,
+ "limit": limit,
+ "fromId": from_id
+ }
+ from huobi.service.algo.get_order_history import GetOrderHistoryService
+ return GetOrderHistoryService(params).request(**self.__kwargs)
+
+ def get_order(self, client_order_id: 'str'):
+ params = {
+ "clientOrderId": client_order_id
+ }
+ from huobi.service.algo.get_order_by_cid import GetOrderByClientOrderIdService
+ return GetOrderByClientOrderIdService(params).request(**self.__kwargs)
+
+ def create_order_param_check(self, symbol, account_id, order_side, order_type, stop_price, order_price,
+ order_size, order_value, time_in_force, trailing_rate, client_order_id):
+ check_symbol(symbol)
+ check_should_not_none(account_id, "accountId")
+ check_should_not_none(order_type, "orderType")
+ check_should_not_none(order_side, "orderSide")
+
+ if order_type == OrderType.SELL_LIMIT \
+ or order_type == OrderType.BUY_LIMIT \
+ or order_type == OrderType.BUY_LIMIT_MAKER \
+ or order_type == OrderType.SELL_LIMIT_MAKER:
+ check_should_not_none(order_price, "orderPrice")
+
+ if time_in_force is not None:
+ check_time_in_force(time_in_force)
+
+ if order_type in [OrderType.SELL_MARKET, OrderType.BUY_MARKET]:
+ order_price = None
+
+ params = {
+ "accountId": account_id,
+ "symbol": symbol,
+ "orderPrice": order_price,
+ "orderSide": order_side,
+ "orderSize": order_size,
+ "orderValue": order_value,
+ "timeInForce": time_in_force,
+ "orderType": order_type,
+ "clientOrderId": client_order_id,
+ "stopPrice": stop_price,
+ "trailingRate": trailing_rate
+ }
+
+ return params
diff --git a/huobi/client/etf.py b/huobi/client/etf.py
new file mode 100644
index 0000000..3f1668c
--- /dev/null
+++ b/huobi/client/etf.py
@@ -0,0 +1,93 @@
+from huobi.constant import *
+from huobi.model.etf import *
+from huobi.utils import *
+
+
+class EtfClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def get_etf_swap_config(self, etf_name: 'str') -> EtfSwapConfig:
+ """
+ Get the basic information of ETF creation and redemption, as well as ETF constituents,
+ including max amount of creation, min amount of creation, max amount of redemption, min amount
+ of redemption, creation fee rate, redemption fee rate, eft create/redeem status.
+
+ :param etf_symbol: The symbol, currently only support hb10. (mandatory)
+ :return: The etf configuration information.
+ """
+ check_symbol(etf_name)
+ params={
+ "etf_name":etf_name
+ }
+
+ from huobi.service.etf.get_etf_swap_config import GetEtfSwapConfigService
+ return GetEtfSwapConfigService(params).request(**self.__kwargs)
+
+ def get_etf_swap_list(self, etf_name: 'str', offset: 'int', size: 'int') -> list:
+ """
+ Get past creation and redemption.(up to 100 records)
+
+ :param etf_symbol: The symbol, currently only support hb10. (mandatory)
+ :param offset: The offset of the records, set to 0 for the latest records. (mandatory)
+ :param size: The number of records to return, the range is [1, 100]. (mandatory)
+ :return: The swap history.
+ """
+ check_symbol(etf_name)
+ params={
+ "etf_name":etf_name,
+ "offset" : offset,
+ "limit" : size
+ }
+
+ from huobi.service.etf.get_etf_swap_list import GetEtfSwapListService
+ return GetEtfSwapListService(params).request(**self.__kwargs)
+
+ def post_etf_swap_in(self, etf_name: 'str', amount: 'int') -> None:
+ """
+ Order creation or redemption of ETF.
+
+ :param etf_name: The symbol, currently only support hb10. (mandatory)
+ :param amount: The amount to create or redemption. (mandatory)
+ :return: No return
+ """
+ check_symbol(etf_name)
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "etf_name" : etf_name,
+ "amount" : amount
+ }
+
+ from huobi.service.etf.post_etf_swap_in import PostEftSwapInService
+ return PostEftSwapInService(params).request(**self.__kwargs)
+
+ def post_etf_swap_out(self, etf_name: 'str', amount: 'int') -> None:
+ """
+ Order creation or redemption of ETF.
+
+ :param etf_name: The symbol, currently only support hb10. (mandatory)
+ :param amount: The amount to create or redemption. (mandatory)
+ :return: No return
+ """
+
+ check_symbol(etf_name)
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "etf_name": etf_name,
+ "amount": amount
+ }
+
+ from huobi.service.etf.post_etf_swap_out import PostEtfSwapOutService
+ return PostEtfSwapOutService(params).request(**self.__kwargs)
+
diff --git a/huobi/client/generic.py b/huobi/client/generic.py
new file mode 100644
index 0000000..ddc0ce1
--- /dev/null
+++ b/huobi/client/generic.py
@@ -0,0 +1,102 @@
+from huobi.connection.impl.restapi_request import RestApiRequest
+from huobi.constant import *
+from huobi.model.generic import *
+
+
+class GenericClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def get_exchange_timestamp(self) -> int:
+ """
+ Get the timestamp from Huobi server. The timestamp is the Unix timestamp in millisecond.
+ The count shows how many milliseconds passed from Jan 1st 1970, 00:00:00.000 at UTC.
+ e.g. 1546300800000 is Thu, 1st Jan 2019 00:00:00.000 UTC.
+
+ :return: The timestamp in UTC
+ """
+
+ params = {}
+
+ from huobi.service.generic.get_exchange_timestamp import GetExchangeTimestampService
+ return GetExchangeTimestampService(params).request(**self.__kwargs)
+
+ def get_exchange_currencies(self) -> list():
+ """
+ Get all the trading assets and currencies supported in huobi.
+ The information of trading instrument, including base currency, quote precision, etc.
+
+ :return: The information of trading currencies.
+ """
+
+ params = {}
+
+ from huobi.service.generic.get_exchange_currencies import GetExchangeCurrenciesService
+ return GetExchangeCurrenciesService(params).request(**self.__kwargs)
+
+ def get_exchange_symbols(self) -> list():
+ """
+ Get all the trading assets and currencies supported in huobi.
+ The information of trading instrument etc.
+
+ :return: The information of trading instrument.
+ """
+
+ params = {}
+
+ from huobi.service.generic.get_exchange_symbols import GetExchangeSymbolsService
+ return GetExchangeSymbolsService(params).request(**self.__kwargs)
+
+ def get_exchange_info(self) -> ExchangeInfo:
+ """
+ Get all the trading assets and currencies supported in huobi.
+ The information of trading instrument, including base currency, quote precision, etc.
+
+ :return: The information of trading instrument and currencies.
+ """
+
+ ret = ExchangeInfo()
+ ret.symbol_list = self.get_exchange_symbols()
+ ret.currencies = self.get_exchange_currencies()
+ return ret
+
+ def get_reference_currencies(self, currency: 'str' = None, is_authorized_user: 'bool' = None) -> list:
+ """
+ Get all the trading assets and currencies supported in huobi.
+ The information of trading instrument, including base currency, quote precision, etc.
+
+ :param currency: btc, ltc, bch, eth, etc ...(available currencies in Huobi Global)
+ :param is_authorized_user: is Authorized user? True or False
+ :return: The information of trading instrument and currencies.
+ """
+
+ params = {
+ "currency": currency,
+ "authorizedUser": is_authorized_user
+ }
+
+ from huobi.service.generic.get_reference_currencies import GetReferenceCurrenciesService
+ return GetReferenceCurrenciesService(params).request(**self.__kwargs)
+
+ def get_system_status(self) -> str:
+ """
+ get system status
+
+ :return: system status.
+ """
+
+ from huobi.service.generic.get_system_status import GetSystemStatusService
+ return GetSystemStatusService({}).request(**self.__kwargs)
+
+ def get_market_status(self):
+ from huobi.service.generic.get_market_status import GetMarketStatusService
+ return GetMarketStatusService({}).request(**self.__kwargs)
diff --git a/huobi/client/linear_swap.py b/huobi/client/linear_swap.py
new file mode 100644
index 0000000..5e586c1
--- /dev/null
+++ b/huobi/client/linear_swap.py
@@ -0,0 +1,540 @@
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils.input_checker import *
+
+
+class TradeClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def get_feerate(self, symbols: 'str') -> list:
+ """
+ Get the candlestick/kline for the specified symbol. The data number is 150 as default.
+
+ :param symbol: The symbol, like "btcusdt". To query hb10, put "hb10" at here. (mandatory)
+ :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. (mandatory)
+ :param size: The start time of of requested candlestick/kline data. (optional)
+ :param start_time: The start time of of requested candlestick/kline data. (optional)
+ :param end_time: The end time of of requested candlestick/kline data. (optional)
+ :return: The list of candlestick/kline data.
+ """
+ check_symbol(symbols)
+
+ params = {
+ "symbols": symbols
+ }
+
+ from huobi.service.trade.get_feerate import GetFeeRateService
+ return GetFeeRateService(params).request(**self.__kwargs)
+
+ def get_transact_feerate(self, symbols: 'str') -> list:
+ """
+ The request of get transact fee rate list.
+
+ :param symbols: The symbol, like "btcusdt,htusdt". (mandatory)
+ :return: The transact fee rate list.
+ """
+ check_symbol(symbols)
+
+ params = {
+ "symbols": symbols
+ }
+
+ from huobi.service.trade.get_transact_feerate import GetTransactFeeRateService
+ return GetTransactFeeRateService(params).request(**self.__kwargs)
+
+ def sub_order_update(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe order changing event. If a order is created, canceled etc, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(order_update_event: 'OrderUpdateEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list" : symbol_list,
+ }
+
+ from huobi.service.trade.sub_order_update_v2 import SubOrderUpdateV2Service
+ SubOrderUpdateV2Service(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_order_list(self, symbol: 'str', account_id: int, callback, order_states:'str',
+ order_types:'str'=None, start_date:'str'=None, end_date:'str'=None, from_id=None,
+ direct=None, size=None, client_req_id:'str'=None, error_handler=None):
+ """
+ request order list.
+
+ :param symbol: The symbol, like "btcusdt".
+ :param order_states: order status, can be one state or many state sepearted by comma, such as "submitted,partial-filled,partial-canceled,filled,canceled,created"
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(candlestick_event: 'CandlestickEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ check_should_not_none(symbol, "symbol")
+ check_should_not_none(order_states, "states")
+ check_should_not_none(account_id, "account-d")
+ check_should_not_none(callback, "callback")
+ params = {
+ "symbol": symbol,
+ "account-id" : account_id,
+ "states" : order_states,
+ "types" : order_types,
+ "start-date" : start_date,
+ "end-date" : end_date,
+ "from": from_id,
+ "direct" : direct,
+ "size" : size,
+ "client-req-id" : client_req_id
+ }
+
+ from huobi.service.trade.req_order_list import ReqOrderListService
+ ReqOrderListService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_order_detail(self, order_id: 'str', callback,
+ client_req_id:'str'=None, error_handler=None):
+ """
+ Subscribe candlestick/kline event. If the candlestick/kline is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc.
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(candlestick_event: 'CandlestickEvent'):
+ pass
+ :param client_req_id: client request ID
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ check_should_not_none(order_id, "order_id")
+ check_should_not_none(callback, "callback")
+ params = {
+ "order-id": order_id,
+ "cid": client_req_id,
+ }
+
+ from huobi.service.trade.req_order_detail import ReqOrderDetailService
+ ReqOrderDetailService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def get_order(self, order_id: 'int') -> Order:
+ """
+ Get the details of an order.
+
+ :param order_id: The order id. (mandatory)
+ :return: The information of order.
+ """
+ check_should_not_none(order_id, "order_id")
+
+ params = {
+ "order_id": order_id,
+ }
+
+ from huobi.service.trade.get_order_by_id import GetOrderByIdService
+ return GetOrderByIdService(params).request(**self.__kwargs)
+
+ def get_order_by_client_order_id(self, client_order_id):
+ check_should_not_none(client_order_id, "clientOrderId")
+
+ params = {
+ "clientOrderId": client_order_id,
+ }
+
+ from huobi.service.trade.get_order_by_client_order_id import GetOrderByClientOrderIdService
+ return GetOrderByClientOrderIdService(params).request(**self.__kwargs)
+
+ def get_orders(self, symbol: 'str', order_state: 'OrderState', order_type: 'OrderType' = None,
+ start_date: 'str' = None, end_date: 'str' = None, start_id: 'int' = None,
+ size: 'int' = None, direct=None) -> list:
+ check_symbol(symbol)
+ check_should_not_none(order_state, "order_state")
+ start_date = format_date(start_date, "start_date")
+ end_date = format_date(end_date, "end_date")
+
+ params = {
+ "symbol" : symbol,
+ "types" : order_type,
+ "start-date" : start_date,
+ "end-date" : end_date,
+ "from" : start_id,
+ "states" : order_state,
+ "size" : size,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_orders import GetOrdersService
+ return GetOrdersService(params).request(**self.__kwargs)
+
+ def get_open_orders(self, symbol: 'str', account_id: 'int', side: 'OrderSide' = None,
+ size: 'int' = None, from_id=None, direct=None) -> list:
+ """
+ The request of get open orders.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param account_id: account id (mandatory)
+ :param side: The order side, buy or sell. If no side defined, will return all open orders of the account. (optional)
+ :param size: The number of orders to return. Range is [1, 500]. (optional)
+ :param direct: 1:prev order by ID asc from from_id, 2:next order by ID desc from from_id
+ :param from_id: start ID for search
+ :return: The orders information.
+ """
+ check_symbol(symbol)
+ check_range(size, 1, 500, "size")
+ check_should_not_none(account_id, "account_id")
+ params = {
+ "symbol" : symbol,
+ "account-id" : account_id,
+ "side" : side,
+ "size" : size,
+ "from" : from_id,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_open_orders import GetOpenOrdersService
+ return GetOpenOrdersService(params).request(**self.__kwargs)
+
+ def get_history_orders(self, symbol=None, start_time=None, end_time=None, size=None, direct=None)-> list:
+ """
+ Transfer Asset between Futures and Contract.
+
+ :param direct:
+ :param symbol: The target sub account uid to transfer to or from. (optional)
+ :param start_time: The crypto currency to transfer. (optional)
+ :param end_time: The amount of asset to transfer. (optional)
+ :param size: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (optional)
+ :return: The Order list.
+ """
+ params = {
+ "symbol" :symbol,
+ "start-time" : start_time,
+ "end-time" : end_time,
+ "size" : size,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_history_orders import GetHistoryOrdersService
+ return GetHistoryOrdersService(params).request(**self.__kwargs)
+
+ def get_match_result(self, symbol: 'str', order_type: 'OrderSide' = None, start_date: 'str' = None,
+ end_date: 'str' = None,
+ size: 'int' = None,
+ from_id: 'int' = None,
+ direct:'str'=None):
+ """
+ Search for the trade records of an account.
+
+ :param symbol: The symbol, like "btcusdt" (mandatory).
+ :param order_type: The types of order to include in the search (optional).
+ :param start_date: Search starts date in format yyyy-mm-dd. (optional).
+ :param end_date: Search ends date in format yyyy-mm-dd. (optional).
+ :param size: The number of orders to return, range [1-100]. (optional).
+ :param from_id: Search order id to begin with. (optional).
+ :return:
+ """
+
+ check_symbol(symbol)
+ start_date = format_date(start_date, "start_date")
+ end_date = format_date(end_date, "end_date")
+ check_range(size, 1, 100, "size")
+
+ params = {
+ "symbol" : symbol,
+ "start-date" : start_date,
+ "end-date" : end_date,
+ "types" : order_type,
+ "size" : size,
+ "from" : from_id,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_match_results import GetMatchResultsService
+ return GetMatchResultsService(params).request(**self.__kwargs)
+
+ def get_match_results_by_order_id(self, order_id: 'int') -> list:
+ """
+ Get detail match results of an order.
+
+ :param order_id: The order id. (mandatory)
+ :return: The list of match result.
+ """
+ check_should_not_none(order_id, "order_id")
+
+ params = {
+ "order_id": order_id
+ }
+
+ from huobi.service.trade.get_match_results_by_order_id import GetMatchResultsByOrderIdService
+ return GetMatchResultsByOrderIdService(params).request(**self.__kwargs)
+
+ def order_source_desc(self, account_type):
+ default_source = "api"
+ if account_type:
+ if account_type == AccountType.MARGIN:
+ return "margin-api"
+ return default_source
+
+ def create_order_param_check(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', source:'str', client_order_id=None, stop_price=None, operator=None):
+ check_symbol(symbol)
+ check_should_not_none(account_id, "account_id")
+ check_should_not_none(order_type, "order_type")
+ check_should_not_none(amount, "amount")
+ check_should_not_none(source, "source")
+
+ if order_type == OrderType.SELL_LIMIT \
+ or order_type == OrderType.BUY_LIMIT \
+ or order_type == OrderType.BUY_LIMIT_MAKER \
+ or order_type == OrderType.SELL_LIMIT_MAKER:
+ check_should_not_none(price, "price")
+ if order_type in [OrderType.SELL_MARKET, OrderType.BUY_MARKET]:
+ price = None
+
+ params = {
+ "account-id" : account_id,
+ "amount" : amount,
+ "price": price,
+ "symbol": symbol,
+ "type": order_type,
+ "source": source,
+ "client-order-id": client_order_id,
+ "stop-price": stop_price,
+ "operator": operator
+ }
+
+ return params
+
+ def create_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', source:'str', client_order_id=None, stop_price=None, operator=None) -> int:
+ """
+ Make an order in huobi.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param account_id: Account id. (mandatory)
+ :param order_type: The order type. (mandatory)
+ :param source: The order source. (mandatory)
+ for spot, it's "api", see OrderSource.API
+ for margin, it's "margin-api", see OrderSource.MARGIN_API
+ for super margin, it's "super-margin-api", see OrderSource.SUPER_MARGIN_API
+ :param amount: The amount to buy (quote currency) or to sell (base currency). (mandatory)
+ :param price: The limit price of limit order, only needed for limit order. (mandatory for buy-limit, sell-limit, buy-limit-maker and sell-limit-maker)
+ :param client_order_id: unique Id which is user defined and must be unique in recent 24 hours
+ :param stop_price: Price for auto sell to get the max benefit
+ :param operator: the condition for stop_price, value can be "gte" or "lte", gte – greater than and equal (>=), lte – less than and equal (<=)
+ :return: The order id.
+ """
+
+ params = self.create_order_param_check(symbol, account_id, order_type, amount,
+ price, source, client_order_id, stop_price, operator)
+ from huobi.service.trade.post_create_order import PostCreateOrderService
+ return PostCreateOrderService(params).request(**self.__kwargs)
+
+ def create_spot_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', client_order_id=None, stop_price=None,
+ operator=None) -> int:
+ order_source = OrderSource.API
+ return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount,
+ price=price, source=order_source, client_order_id=client_order_id, stop_price=stop_price,
+ operator=operator)
+
+ def create_margin_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', client_order_id=None, stop_price=None,
+ operator=None) -> int:
+ order_source = OrderSource.MARGIN_API
+ return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount,
+ price=price, source=order_source, client_order_id=client_order_id,
+ stop_price=stop_price,
+ operator=operator)
+
+ def create_super_margin_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', client_order_id=None, stop_price=None,
+ operator=None) -> int:
+ order_source = OrderSource.SUPER_MARGIN_API
+ return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount,
+ price=price, source=order_source, client_order_id=client_order_id,
+ stop_price=stop_price,
+ operator=operator)
+
+ def cancel_order(self, symbol, order_id):
+ check_symbol(symbol)
+ check_should_not_none(order_id, "order_id")
+
+ params = {
+ "order_id" : order_id
+ }
+
+ from huobi.service.trade.post_cancel_order import PostCancelOrderService
+ return PostCancelOrderService(params).request(**self.__kwargs)
+
+ def cancel_orders(self, symbol, order_id_list)->BatchCancelResult:
+ """
+ Submit cancel request for cancelling multiple orders.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param order_id_list: The list of order id. the max size is 50. (mandatory)
+ :return: No return
+ """
+ check_symbol(symbol)
+ check_should_not_none(order_id_list, "order_id_list")
+ check_list(order_id_list, 1, 50, "order_id_list")
+
+ string_list = list()
+ for order_id in order_id_list:
+ string_list.append(str(order_id))
+
+ params = {
+ "order-ids" : string_list
+ }
+
+ from huobi.service.trade.post_batch_cancel_order import PostBatchCancelOrderService
+ return PostBatchCancelOrderService(params).request(**self.__kwargs)
+
+ def cancel_open_orders(self, account_id, symbols: 'str'=None , side=None, size=None)->BatchCancelCount:
+ """
+ Request to cancel open orders.
+
+ :param symbols: The symbol, like "btcusdt".
+ :param account_type: Account type. (mandatory)
+ :param side: The order side, buy or sell. If no side defined, will cancel all open orders of the account. (optional)
+ :param size: The number of orders to cancel. Range is [1, 100]. (optional)
+ :return: Status of batch cancel result.
+ """
+ check_should_not_none(account_id, "account_id")
+
+ params = {
+ "account-id": account_id,
+ "symbol" : symbols,
+ "side" : side,
+ "size" : size
+ }
+
+ from huobi.service.trade.post_batch_cancel_open_order import PostBatchCancelOpenOrderService
+ return PostBatchCancelOpenOrderService(params).request(**self.__kwargs)
+
+ def cancel_client_order(self, client_order_id)->int:
+ """
+ Request to cancel open orders.
+
+ :param client_order_id: user defined unique order id
+ """
+ check_should_not_none(client_order_id, "client-order-id")
+
+ params = {
+ "client-order-id" : client_order_id
+ }
+
+ from huobi.service.trade.post_cancel_client_order import PostCancelClientOrderService
+ return PostCancelClientOrderService(params).request(**self.__kwargs)
+
+ def transfer_between_futures_and_pro(self, currency: 'str', amount: 'float',
+ transfer_type: 'TransferFuturesPro')-> int:
+ """
+ Transfer Asset between Futures and Contract.
+
+ :param sub_uid: The target sub account uid to transfer to or from. (mandatory)
+ :param currency: The crypto currency to transfer. (mandatory)
+ :param amount: The amount of asset to transfer. (mandatory)
+ :param transfer_type: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (mandatory)
+ :return: The order id.
+ """
+ check_currency(currency)
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+ check_should_not_none(transfer_type, "transfer_type")
+ params = {
+ "currency" : currency,
+ "amount" : amount,
+ "type" : transfer_type
+
+ }
+
+ from huobi.service.trade.post_transfer_futures_pro import PostTransferFuturesProService
+ return PostTransferFuturesProService(params).request(**self.__kwargs)
+
+ def batch_create_order(self, order_config_list) -> int:
+ """
+ Make an order in huobi.
+ :param order_config_list: order config list, it can batch create orders, and each order config check as below
+ : items as below
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param account_type: Account type. (mandatory)
+ :param order_type: The order type. (mandatory)
+ :param amount: The amount to buy (quote currency) or to sell (base currency). (mandatory)
+ :param price: The limit price of limit order, only needed for limit order. (mandatory for buy-limit, sell-limit, buy-limit-maker and sell-limit-maker)
+ :param client_order_id: unique Id which is user defined and must be unique in recent 24 hours
+ :param stop_price: Price for auto sell to get the max benefit
+ :param operator: the condition for stop_price, value can be "gte" or "lte", gte – greater than and equal (>=), lte – less than and equal (<=)
+ :return: The order id.
+ """
+
+ check_should_not_none(order_config_list, "order_config_list")
+ check_list(order_config_list, 1, 10, "create order config list")
+
+ new_config_list = list()
+ for item in order_config_list:
+ new_item = self.create_order_param_check(
+ item.get("symbol", None),
+ item.get("account_id", None),
+ item.get("order_type", None),
+ item.get("amount", None),
+ item.get("price", None),
+ item.get("source", None),
+ item.get("client_order_id", None),
+ item.get("stop-price", None),
+ item.get("operator", None))
+
+ new_config_list.append(new_item)
+
+ from huobi.service.trade.post_batch_create_order import PostBatchCreateOrderService
+ return PostBatchCreateOrderService(new_config_list).request(**self.__kwargs)
+
+ def sub_trade_clearing(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe trade clearing by symbol
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ "*" for all symbols
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ check_should_not_none(symbols, "symbols")
+ symbol_list = symbols.split(",")
+ if ("*" in symbol_list):
+ symbol_list = ["*"]
+ else:
+ check_symbol_list(symbol_list)
+
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ }
+
+ from huobi.service.trade.sub_trade_clearing_v2 import SubTradeClearingV2Service
+ SubTradeClearingV2Service(params).subscribe(callback, error_handler, **self.__kwargs)
diff --git a/huobi/client/margin.py b/huobi/client/margin.py
new file mode 100644
index 0000000..0fe3596
--- /dev/null
+++ b/huobi/client/margin.py
@@ -0,0 +1,363 @@
+from huobi.utils.input_checker import *
+
+
+class MarginClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def post_transfer_in_margin(self, symbol: 'str', currency: 'str', amount: 'float') -> int:
+ """
+ Transfer asset from spot account to margin account.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param currency: The currency of transfer. (mandatory)
+ :param amount: The amount of transfer. (mandatory)
+ :return:
+ """
+ check_symbol(symbol)
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "symbol": symbol,
+ "currency": currency,
+ "amount": amount
+ }
+
+ from huobi.service.margin.post_transfer_in_margin import PostTransferInMarginService
+ return PostTransferInMarginService(params).request(**self.__kwargs)
+
+ def post_transfer_out_margin(self, symbol: 'str', currency: 'str', amount: 'float') -> int:
+ """
+ Transfer asset from margin account to spot account.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param currency: The currency of transfer. (mandatory)
+ :param amount: The amount of transfer. (mandatory)
+ :return:
+ """
+ check_symbol(symbol)
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "symbol": symbol,
+ "currency": currency,
+ "amount": amount
+ }
+
+ from huobi.service.margin.post_transfer_out_margin import PostTransferOutMarginService
+ return PostTransferOutMarginService(params).request(**self.__kwargs)
+
+ def get_margin_account_balance(self, symbol: 'str') -> list:
+ """
+ Get the Balance of the Margin Loan Account.
+
+ :param symbol: The currency, like "btc". (mandatory)
+ :return: The margin loan account detail list.
+ """
+ check_symbol(symbol)
+
+ params = {
+ "symbol": symbol
+ }
+
+ from huobi.service.margin.get_margin_account_balance import GetMarginAccountBalanceService
+ return GetMarginAccountBalanceService(params).request(**self.__kwargs)
+
+ def post_create_margin_order(self, symbol: 'str', currency: 'str', amount: 'float') -> int:
+ """
+ Submit a request to borrow with margin account.
+
+ :param symbol: The trading symbol to borrow margin, e.g. "btcusdt", "bccbtc". (mandatory)
+ :param currency: The currency to borrow,like "btc". (mandatory)
+ :param amount: The amount of currency to borrow. (mandatory)
+ :return: The margin order id.
+ """
+ check_symbol(symbol)
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "symbol": symbol,
+ "currency": currency,
+ "amount": amount
+ }
+
+ from huobi.service.margin.post_create_margin_order import PostCreateMarginOrderService
+ return PostCreateMarginOrderService(params).request(**self.__kwargs)
+
+ def post_repay_margin_order(self, loan_id: 'int', amount: 'float') -> int:
+ """
+ Get the margin loan records.
+
+ :param load_id: The previously returned order id when loan order was created. (mandatory)
+ :param amount: The amount of currency to repay. (mandatory)
+ :return: The margin order id.
+ """
+ check_should_not_none(loan_id, "loan_id")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "loan_id": loan_id,
+ "amount": amount
+ }
+
+ from huobi.service.margin.post_repay_margin_order import PostRepayMarginOrderService
+ return PostRepayMarginOrderService(params).request(**self.__kwargs)
+
+ def get_margin_loan_orders(self, symbol: 'str', start_date: 'str' = None, end_date: 'str' = None,
+ states: 'LoanOrderState' = None, from_id: 'int' = None,
+ size: 'int' = None, direction: 'QueryDirection' = None) -> list:
+ """
+ Get the margin loan records.
+
+ :param symbol: The symbol, like "btcusdt" (mandatory).
+ :param start_date: The search starts date in format yyyy-mm-dd. (optional).
+ :param end_date: The search end date in format yyyy-mm-dd.(optional, can be null).
+ :param states: The loan order states, it could be created, accrual, cleared or invalid. (optional)
+ :param from_id: Search order id to begin with. (optional)
+ :param size: The number of orders to return.. (optional)
+ :param direction: The query direction, prev or next. (optional)
+ :return: The list of the margin loan records.
+ """
+
+ check_symbol(symbol)
+ start_date = format_date(start_date, "start_date")
+ end_date = format_date(end_date, "end_date")
+
+ params = {
+ "symbol": symbol,
+ "start-date": start_date,
+ "end-date": end_date,
+ "states": states,
+ "from": from_id,
+ "size": size,
+ "direct": direction
+ }
+
+ from huobi.service.margin.get_margin_loan_orders import GetMarginLoanOrdersService
+ return GetMarginLoanOrdersService(params).request(**self.__kwargs)
+
+ def get_margin_loan_info(self, symbols: 'str' = None) -> list:
+ """
+ The request of get margin loan info, can return currency loan info list.
+
+ :param symbols: The symbol, like "btcusdt,htusdt". (optional)
+ :return: The cross margin loan info.
+ """
+
+ check_symbol(symbols)
+ params = {
+ "symbols": symbols
+ }
+
+ from huobi.service.margin.get_margin_loan_info import GetMarginLoanInfoService
+ return GetMarginLoanInfoService(params).request(**self.__kwargs)
+
+ def get_cross_margin_loan_info(self) -> list:
+ """
+ The request of currency loan info list.
+
+ :return: The cross margin loan info list.
+ """
+ params = {}
+
+ from huobi.service.margin.get_cross_margin_loan_info import GetCrossMarginLoanInfoService
+ return GetCrossMarginLoanInfoService(params).request(**self.__kwargs)
+
+ def post_cross_margin_transfer_in(self, currency: 'str', amount: 'float') -> int:
+ """
+ transfer currency to cross account.
+
+ :param currency: currency name (mandatory)
+ :param amount: transfer amount (mandatory)
+ :return: return transfer id.
+ """
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "amount": amount,
+ "currency": currency
+ }
+
+ from huobi.service.margin.post_cross_margin_transfer_in import PostCrossMarginTransferInService
+ return PostCrossMarginTransferInService(params).request(**self.__kwargs)
+
+ def post_cross_margin_transfer_out(self, currency: 'str', amount: 'float') -> int:
+ """
+ transfer currency to cross account.
+
+ :param currency: currency name (mandatory)
+ :param amount: transfer amount (mandatory)
+ :return: return transfer id.
+ """
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "amount": amount,
+ "currency": currency
+ }
+
+ from huobi.service.margin.post_cross_margin_transfer_out import PostCrossMarginTransferOutService
+ return PostCrossMarginTransferOutService(params).request(**self.__kwargs)
+
+ def post_cross_margin_create_loan_orders(self, currency: 'str', amount: 'float') -> int:
+ """
+ create cross margin loan orders
+
+ :param currency: currency name (mandatory)
+ :param amount: transfer amount (mandatory)
+ :return: return order id.
+ """
+
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "amount": amount,
+ "currency": currency
+ }
+
+ from huobi.service.margin.post_cross_margin_create_loan_orders import PostCrossMarginCreateLoanOrdersService
+ return PostCrossMarginCreateLoanOrdersService(params).request(**self.__kwargs)
+
+ def post_cross_margin_loan_order_repay(self, order_id: 'str', amount: 'float'):
+ """
+ repay cross margin loan orders
+
+ :param order_id: order_id for loan (mandatory)
+ :param amount: transfer amount (mandatory)
+ :return: return order id.
+ """
+
+ check_should_not_none(order_id, "order-id")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "amount": amount,
+ "order-id": order_id
+ }
+
+ from huobi.service.margin.post_cross_margin_loan_order_repay import PostCrossMarginLoanOrderRepayService
+ return PostCrossMarginLoanOrderRepayService(params).request(**self.__kwargs)
+
+ def get_cross_margin_loan_orders(self, currency: 'str' = None, state: 'str' = None,
+ start_date: 'str' = None, end_date: 'str' = None,
+ from_id: 'int' = None, size: 'int' = None, direct: 'str' = None,
+ sub_uid: 'int' = None) -> list:
+ """
+ get cross margin loan orders
+
+ :return: return list.
+ """
+ params = {
+ "currency": currency,
+ "state": state,
+ "start-date": start_date,
+ "end-date": end_date,
+ "from": from_id,
+ "size": size,
+ "direct": direct,
+ "sub-uid": sub_uid
+ }
+
+ from huobi.service.margin.get_cross_margin_loan_orders import GetCrossMarginLoanOrdersService
+ return GetCrossMarginLoanOrdersService(params).request(**self.__kwargs)
+
+ def get_cross_margin_account_balance(self, sub_uid: 'int' = None):
+ """
+ get cross margin account balance
+
+ :return: cross-margin account.
+ """
+ params = {
+ "sub-uid": sub_uid
+ }
+
+ from huobi.service.margin.get_cross_margin_account_balance import GetCrossMarginAccountBalanceService
+ return GetCrossMarginAccountBalanceService(params).request(**self.__kwargs)
+
+ def post_general_repay_loan(self, account_id: 'str', currency: 'str', amount: 'float',
+ transact_id: 'str' = None) -> list:
+ """
+ Repay Margin Loan(Cross).
+
+ :param account_id: repayment account ID .(mandatory)
+ :param currency: repayment currency. (mandatory).
+ :param amount: repayment amount.(mandatory).
+ :param transact_id: loan transaction ID. (optional)
+ """
+ check_should_not_none(account_id, "account_id")
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+
+ params = {
+ "accountId": account_id,
+ "currency": currency,
+ "amount": amount,
+ "transact_id": transact_id
+ }
+
+ from huobi.service.margin.post_general_repay_loan import PostGeneralRepayLoanService
+ return PostGeneralRepayLoanService(params).request(**self.__kwargs)
+
+ def get_general_repayment_loan_records(self, repay_id: 'str' = None, account_id: 'str' = None,
+ currency: 'str' = None, start_time: 'int' = None, end_time: 'int' = None,
+ sort: 'str' = None, limit: 'int' = None, from_id: 'int' = None) -> list:
+
+ """
+ Get Repayment Record Reference(Cross).
+
+ :param repay_id: repayment transaction ID. (optional)
+ :param account_id: account ID (default value: all accounts) (optional).
+ :param currency: borrowing/lending currency (default value: all currencies). (optional)
+ :param start_time: start time (unix time in millisecond; range: [(endTime – x D), endTime]; default value: (endTime – x D) (optional)
+ :param end_time: end time (unix time in millisecond;range: [(present time – y D), present time]; default value: present time) (optional)
+ :param sort: sort direction (virtual value: asc, desc; default value: desc) (optional)
+ :param limit: max return items per page (range: [1,100]; default value: 50) (optional)
+ :param from_id: search original ID (only available when searching for the next page) (optional)
+ """
+
+ params = {
+
+ }
+ if repay_id is not None:
+ params['repayId'] = repay_id
+
+ if account_id is not None:
+ params['accountId'] = account_id
+
+ if account_id is not None:
+ params['currency'] = currency
+
+ if start_time is not None:
+ params['startTime'] = start_time
+
+ if end_time is not None:
+ params['endTime'] = end_time
+
+ if sort is not None:
+ params['sort'] = sort
+
+ if limit is not None:
+ params['limit'] = limit
+
+ if from_id is not None:
+ params['fromId'] = from_id
+
+ from huobi.service.margin.get_general_repayment_loan_records import GetGeneralRepaymentLoanRecordsService
+ return GetGeneralRepaymentLoanRecordsService(params).request(**self.__kwargs)
+
+ return
diff --git a/huobi/client/market.py b/huobi/client/market.py
new file mode 100644
index 0000000..7d281be
--- /dev/null
+++ b/huobi/client/market.py
@@ -0,0 +1,496 @@
+from huobi.constant import *
+from huobi.model.market import *
+from huobi.utils import *
+from huobi.utils.input_checker import check_in_list
+
+
+class MarketClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def get_candlestick(self, symbol, period, size=200):
+ """
+ Get the candlestick/kline for the specified symbol. The data number is 150 as default.
+
+ :param symbol: The symbol, like "btcusdt". To query hb10, put "hb10" at here. (mandatory)
+ :param period: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. (mandatory)
+ :param size: The start time of of requested candlestick/kline data. (optional)
+ :return: The list of candlestick/kline data.
+ """
+ check_symbol(symbol)
+ check_should_not_none(period, "period")
+ check_range(size, 1, 2000, "size")
+
+ params = {
+ "symbol": symbol,
+ "period": period,
+ "size": size
+ }
+ from huobi.service.market.get_candlestick import GetCandleStickService
+ return GetCandleStickService(params).request(**self.__kwargs)
+
+ def sub_candlestick(self, symbols: 'str', interval: 'CandlestickInterval', callback, error_handler):
+
+ """
+ Subscribe candlestick/kline event. If the candlestick/kline is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc.
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(candlestick_event: 'CandlestickEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(interval, "interval")
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ "interval": interval,
+ }
+
+ from huobi.service.market.sub_candlestick import SubCandleStickService
+ SubCandleStickService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_candlestick(self, symbols: 'str', interval: 'CandlestickInterval', callback,
+ from_ts_second=None, end_ts_second=None, error_handler=None):
+ """
+ Subscribe candlestick/kline event. If the candlestick/kline is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc.
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(candlestick_event: 'CandlestickEvent'):
+ pass
+ :param from_ts_second : data from timestamp [it's second]
+ :param end_ts_second : data util timestamp [it's second]
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(interval, "interval")
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ "interval": interval,
+ "from_ts_second": from_ts_second,
+ "end_ts_second": end_ts_second
+ }
+
+ from huobi.service.market.req_candlestick import ReqCandleStickService
+ ReqCandleStickService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def get_pricedepth(self, symbol: 'str', depth_type: 'str', depth_size: 'int' = None) -> PriceDepth:
+ """
+ Get the Market Depth of a symbol.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param depth_type: The tpye, like "step0" to "step5". (mandatory)
+ :param depth_size(optional): The maximum number of Market Depth step0 requested. range [1 - 150], default is 150
+ The maximum number of Market Depth step1,step2,step3,step4,step5 requested. size is in [5, 10, 20], default is 20.
+ :return: Market Depth data.
+ """
+
+ check_symbol(symbol)
+ check_in_list(depth_type, [DepthStep.STEP0, DepthStep.STEP1, DepthStep.STEP2, DepthStep.STEP3, DepthStep.STEP4,
+ DepthStep.STEP5], "depth_type")
+ params = {
+ "symbol": symbol,
+ "type": depth_type,
+ # "depth": depth_size
+ }
+
+ from huobi.service.market.get_pricedepth import GetPriceDepthService
+ ret_data = GetPriceDepthService(params).request(**self.__kwargs)
+
+ if depth_size is not None:
+ if (ret_data.bids is not None) and (len(ret_data.bids) > depth_size):
+ ret_data.bids = ret_data.bids[0:depth_size]
+
+ if (ret_data.asks is not None) and (len(ret_data.asks) > depth_size):
+ ret_data.asks = ret_data.asks[0:depth_size]
+
+ return ret_data
+
+ @staticmethod
+ def get_depth_step_list():
+ return [DepthStep.STEP0,
+ DepthStep.STEP1,
+ DepthStep.STEP2,
+ DepthStep.STEP3,
+ DepthStep.STEP4,
+ DepthStep.STEP5]
+
+ @staticmethod
+ def get_valid_depth_step(value, defalut_value):
+ step_list = MarketClient.get_depth_step_list()
+ if value in step_list:
+ return value
+ else:
+ return defalut_value
+
+ def sub_pricedepth(self, symbols: 'str', depth_step: 'str', callback, error_handler=None):
+ """
+ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param depth_step: The depth precision, string from step0 to step5.
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ new_step = MarketClient.get_valid_depth_step(value=depth_step, defalut_value=DepthStep.STEP0)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ "step": new_step,
+ }
+
+ from huobi.service.market.sub_pricedepth import SubPriceDepthService
+ SubPriceDepthService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def sub_pricedepth_bbo(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ }
+
+ from huobi.service.market.sub_pricedepth_bbo import SubPriceDepthBboService
+ SubPriceDepthBboService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_pricedepth(self, symbols: 'str', depth_step: 'str', callback, error_handler=None):
+ """
+ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param depth_step: The depth precision, string from step0 to step5.
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ new_step = MarketClient.get_valid_depth_step(value=depth_step, defalut_value=DepthStep.STEP0)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ "step": new_step,
+ }
+
+ from huobi.service.market.req_pricedepth import ReqPriceDepthService
+ ReqPriceDepthService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def get_market_detail(self, symbol: 'str') -> MarketDetail:
+ """
+ Get trade statistics in 24 hours.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :return: Trade statistics.
+ """
+
+ check_symbol(symbol)
+
+ params = {
+ "symbol": symbol,
+ }
+
+ from huobi.service.market.get_market_detail import GetMarketDetailService
+ return GetMarketDetailService(params).request(**self.__kwargs)
+
+ def sub_market_detail(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe 24 hours trade statistics event. If statistics is generated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(trade_statistics_event: 'TradeStatisticsEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ }
+
+ from huobi.service.market.sub_market_detail import SubMarketDetailService
+ SubMarketDetailService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_market_detail(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe 24 hours trade statistics event. If statistics is generated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(trade_statistics_event: 'TradeStatisticsEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ }
+
+ from huobi.service.market.req_market_detail import ReqMarketDetailService
+ ReqMarketDetailService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def get_market_trade(self, symbol: 'str') -> list:
+ """
+ Get the most recent trades with their price, volume and direction.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :return: The list of trade.
+ """
+
+ check_symbol(symbol)
+
+ params = {
+ "symbol": symbol,
+ }
+
+ from huobi.service.market.get_market_trade import GetMarketTradeService
+ return GetMarketTradeService(params).request(**self.__kwargs)
+
+ def get_history_trade(self, symbol: 'str', size: 'int' = None) -> list:
+ """
+ Get the most recent trades with their price, volume and direction.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param size: The number of historical trade requested, range [1 - 2000] (optional)
+ :return: The list of trade.
+ """
+
+ check_symbol(symbol)
+ check_range(size, 1, 2000, "size")
+
+ params = {
+ "symbol": symbol,
+ "size": size
+ }
+
+ from huobi.service.market.get_history_trade import GetHistoryTradeService
+ return GetHistoryTradeService(params).request(**self.__kwargs)
+
+ def sub_trade_detail(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(trade_event: 'TradeEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ }
+
+ from huobi.service.market.sub_trade_detail import SubTradeDetailService
+ SubTradeDetailService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_trade_detail(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(trade_event: 'TradeEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ }
+
+ from huobi.service.market.req_trade_detail import ReqTradeDetailService
+ ReqTradeDetailService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def get_market_detail_merged(self, symbol):
+ check_symbol(symbol)
+ params = {
+ "symbol": symbol
+ }
+
+ from huobi.service.market.get_market_detail_merged import GetMarketDetailMergedService
+ return GetMarketDetailMergedService(params).request(**self.__kwargs)
+
+ def get_market_tickers(self) -> list:
+ """
+ get market tickers
+
+ :return: market ticker list.
+ """
+
+ params = {}
+ from huobi.service.market.get_market_tickers import GetMarketTickersService
+ return GetMarketTickersService(params).request(**self.__kwargs)
+
+ """
+ increase mbp(market by price)
+ """
+
+ def sub_mbp_increase(self, symbols: 'str', levels: 'int', callback, error_handler=None):
+ """
+ Subscribe mbp event. If the mbp is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param levels: level, 5,10,20,150. current only support 150
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ check_should_not_none(symbols, "symbol")
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(levels, "levels")
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ "levels": levels
+ }
+
+ from huobi.service.market.sub_mbp_increase import SubMbpIncreaseService
+ SubMbpIncreaseService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ """
+ subscribe full mbp(market by price)
+ """
+
+ def sub_mbp_full(self, symbols: 'str', levels: 'int', callback, error_handler=None):
+ """
+ Subscribe full mbp event. If the mbp is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param levels: level, 5,10,20
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ check_should_not_none(symbols, "symbol")
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(levels, "levels")
+ check_in_list(levels, [MbpLevel.MBP5, MbpLevel.MBP10, MbpLevel.MBP20], "levels")
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ "levels": levels
+ }
+
+ from huobi.service.market.sub_mbp_full import SubMbpFullService
+ SubMbpFullService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_mbp(self, symbols: 'str', levels: 'int', callback, auto_close=True, error_handler=None):
+ """
+ Subscribe mbp event. If the mbp is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param levels: level, 5,10,20,150. current only support 150
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param auto_close : close websocket connection after get data
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ check_should_not_none(symbols, "symbol")
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(levels, "levels")
+ check_should_not_none(callback, "callback")
+ params = {
+ "symbol_list": symbol_list,
+ "levels": levels
+ }
+ from huobi.service.market.req_mbp import ReqMbpService
+ ReqMbpService(params).subscribe(callback, error_handler, **self.__kwargs)
diff --git a/huobi/client/subuser.py b/huobi/client/subuser.py
new file mode 100644
index 0000000..bda5809
--- /dev/null
+++ b/huobi/client/subuser.py
@@ -0,0 +1,115 @@
+from huobi.utils import *
+from huobi.constant import *
+from huobi.utils.input_checker import check_in_list
+
+
+class SubuserClient(object):
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: Init logger, default is False, True will init logger handler
+ """
+ self.__kwargs = kwargs
+
+ def post_create_subuser(self, user_list):
+ check_should_not_none(user_list, 'userList')
+
+ params = user_list
+ from huobi.service.subuser.post_create_subuser import PostSubuserCreationService
+ return PostSubuserCreationService(params).request(**self.__kwargs)
+
+ def post_set_tradable_market(self, sub_uids, account_type: 'SubuserTradePrivilegeType',
+ activation: 'SubUserTradeStatus'):
+ check_should_not_none(sub_uids, 'subUids')
+ check_should_not_none(account_type, 'accountType')
+ check_should_not_none(activation, 'activation')
+
+ check_in_list(account_type,
+ [SubuserTradePrivilegeType.MARGIN, SubuserTradePrivilegeType.SUPER_MARGIN], "accountType")
+ check_in_list(activation, [SubUserTradeStatus.ACTIVATED, SubUserTradeStatus.DEACTIVATED], "activation")
+
+ params = {
+ 'subUids': sub_uids,
+ 'accountType': account_type,
+ 'activation': activation
+ }
+ from huobi.service.subuser.post_tradable_market import PostTradableMarketService
+ return PostTradableMarketService(params).request(**self.__kwargs)
+
+ def post_set_subuser_transferability(self, sub_uids: 'str', transferrable: 'bool',
+ account_type: 'AccountType' = AccountType.SPOT):
+ check_should_not_none(sub_uids, 'subUids')
+ check_should_not_none(transferrable, 'transferrable')
+ check_in_list(account_type, [AccountType.SPOT], 'accountType')
+
+ params = {
+ "subUids": sub_uids,
+ "accountType": account_type,
+ "transferrable": transferrable
+ }
+ from huobi.service.subuser.post_set_transferability import PostSetSubuserTransferability
+ return PostSetSubuserTransferability(params).request(**self.__kwargs)
+
+ def post_subuser_apikey_generate(self, otp_token: 'str', sub_uid: 'int', note: 'str', permission: 'bool',
+ ip_addresses: 'str' = None):
+ check_should_not_none(otp_token, 'otpToken')
+ check_should_not_none(sub_uid, 'subUid')
+ check_should_not_none(note, 'note')
+ check_should_not_none(permission, 'permission')
+ # check_in_list(permission, [AccountType.SPOT], 'accountType')
+
+ params = {
+ "otpToken": otp_token,
+ "subUid": sub_uid,
+ "note": note,
+ "permission": permission,
+ "ipAddresses": ip_addresses
+ }
+ from huobi.service.subuser.post_subuser_apikey_generation import PostSubuserApikeyGenerationService
+ return PostSubuserApikeyGenerationService(params).request(**self.__kwargs)
+
+ def get_user_apikey_info(self, uid: 'str', access_key: 'str' = None):
+ check_should_not_none(uid, 'uid')
+
+ params = {
+ "uid": uid,
+ "accessKey": access_key
+ }
+ from huobi.service.subuser.get_user_apikey_info import GetUserApikeyInfoService
+ return GetUserApikeyInfoService(params).request(**self.__kwargs)
+
+ def post_subuser_apikey_modification(self, sub_uid: 'str', access_key: 'str', note: 'str' = None,
+ permission: 'str' = None, ip_addresses: 'str' = None):
+ check_should_not_none(sub_uid, 'subUid')
+ check_should_not_none(access_key, 'accessKey')
+
+ params = {
+ "subUid": sub_uid,
+ "accessKey": access_key,
+ "note": note,
+ "permission": permission,
+ "ipAddresses": ip_addresses
+ }
+ from huobi.service.subuser.post_subuser_apikey_modification import PostSubuserApikeyModificationService
+ return PostSubuserApikeyModificationService(params).request(**self.__kwargs)
+
+ def post_subuser_apikey_deletion(self, sub_uid: 'str', access_key: 'str'):
+ check_should_not_none(sub_uid, 'subUid')
+ check_should_not_none(access_key, 'accessKey')
+
+ params = {
+ "subUid": sub_uid,
+ "accessKey": access_key
+ }
+ from huobi.service.subuser.post_subuser_apikey_deletion import PostSubuserApikeyDeletionService
+ return PostSubuserApikeyDeletionService(params).request(**self.__kwargs)
+
+ def get_uid(self):
+ params = {
+ }
+ from huobi.service.subuser.get_uid import GetUidService
+ return GetUidService(params).request(**self.__kwargs)
diff --git a/huobi/client/trade.py b/huobi/client/trade.py
new file mode 100644
index 0000000..5e586c1
--- /dev/null
+++ b/huobi/client/trade.py
@@ -0,0 +1,540 @@
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils.input_checker import *
+
+
+class TradeClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def get_feerate(self, symbols: 'str') -> list:
+ """
+ Get the candlestick/kline for the specified symbol. The data number is 150 as default.
+
+ :param symbol: The symbol, like "btcusdt". To query hb10, put "hb10" at here. (mandatory)
+ :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. (mandatory)
+ :param size: The start time of of requested candlestick/kline data. (optional)
+ :param start_time: The start time of of requested candlestick/kline data. (optional)
+ :param end_time: The end time of of requested candlestick/kline data. (optional)
+ :return: The list of candlestick/kline data.
+ """
+ check_symbol(symbols)
+
+ params = {
+ "symbols": symbols
+ }
+
+ from huobi.service.trade.get_feerate import GetFeeRateService
+ return GetFeeRateService(params).request(**self.__kwargs)
+
+ def get_transact_feerate(self, symbols: 'str') -> list:
+ """
+ The request of get transact fee rate list.
+
+ :param symbols: The symbol, like "btcusdt,htusdt". (mandatory)
+ :return: The transact fee rate list.
+ """
+ check_symbol(symbols)
+
+ params = {
+ "symbols": symbols
+ }
+
+ from huobi.service.trade.get_transact_feerate import GetTransactFeeRateService
+ return GetTransactFeeRateService(params).request(**self.__kwargs)
+
+ def sub_order_update(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe order changing event. If a order is created, canceled etc, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(order_update_event: 'OrderUpdateEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ symbol_list = symbols.split(",")
+ check_symbol_list(symbol_list)
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list" : symbol_list,
+ }
+
+ from huobi.service.trade.sub_order_update_v2 import SubOrderUpdateV2Service
+ SubOrderUpdateV2Service(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_order_list(self, symbol: 'str', account_id: int, callback, order_states:'str',
+ order_types:'str'=None, start_date:'str'=None, end_date:'str'=None, from_id=None,
+ direct=None, size=None, client_req_id:'str'=None, error_handler=None):
+ """
+ request order list.
+
+ :param symbol: The symbol, like "btcusdt".
+ :param order_states: order status, can be one state or many state sepearted by comma, such as "submitted,partial-filled,partial-canceled,filled,canceled,created"
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(candlestick_event: 'CandlestickEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ check_should_not_none(symbol, "symbol")
+ check_should_not_none(order_states, "states")
+ check_should_not_none(account_id, "account-d")
+ check_should_not_none(callback, "callback")
+ params = {
+ "symbol": symbol,
+ "account-id" : account_id,
+ "states" : order_states,
+ "types" : order_types,
+ "start-date" : start_date,
+ "end-date" : end_date,
+ "from": from_id,
+ "direct" : direct,
+ "size" : size,
+ "client-req-id" : client_req_id
+ }
+
+ from huobi.service.trade.req_order_list import ReqOrderListService
+ ReqOrderListService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def req_order_detail(self, order_id: 'str', callback,
+ client_req_id:'str'=None, error_handler=None):
+ """
+ Subscribe candlestick/kline event. If the candlestick/kline is updated, server will send the data to client and onReceive in callback will be called.
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc.
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(candlestick_event: 'CandlestickEvent'):
+ pass
+ :param client_req_id: client request ID
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+ :return: No return
+ """
+ check_should_not_none(order_id, "order_id")
+ check_should_not_none(callback, "callback")
+ params = {
+ "order-id": order_id,
+ "cid": client_req_id,
+ }
+
+ from huobi.service.trade.req_order_detail import ReqOrderDetailService
+ ReqOrderDetailService(params).subscribe(callback, error_handler, **self.__kwargs)
+
+ def get_order(self, order_id: 'int') -> Order:
+ """
+ Get the details of an order.
+
+ :param order_id: The order id. (mandatory)
+ :return: The information of order.
+ """
+ check_should_not_none(order_id, "order_id")
+
+ params = {
+ "order_id": order_id,
+ }
+
+ from huobi.service.trade.get_order_by_id import GetOrderByIdService
+ return GetOrderByIdService(params).request(**self.__kwargs)
+
+ def get_order_by_client_order_id(self, client_order_id):
+ check_should_not_none(client_order_id, "clientOrderId")
+
+ params = {
+ "clientOrderId": client_order_id,
+ }
+
+ from huobi.service.trade.get_order_by_client_order_id import GetOrderByClientOrderIdService
+ return GetOrderByClientOrderIdService(params).request(**self.__kwargs)
+
+ def get_orders(self, symbol: 'str', order_state: 'OrderState', order_type: 'OrderType' = None,
+ start_date: 'str' = None, end_date: 'str' = None, start_id: 'int' = None,
+ size: 'int' = None, direct=None) -> list:
+ check_symbol(symbol)
+ check_should_not_none(order_state, "order_state")
+ start_date = format_date(start_date, "start_date")
+ end_date = format_date(end_date, "end_date")
+
+ params = {
+ "symbol" : symbol,
+ "types" : order_type,
+ "start-date" : start_date,
+ "end-date" : end_date,
+ "from" : start_id,
+ "states" : order_state,
+ "size" : size,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_orders import GetOrdersService
+ return GetOrdersService(params).request(**self.__kwargs)
+
+ def get_open_orders(self, symbol: 'str', account_id: 'int', side: 'OrderSide' = None,
+ size: 'int' = None, from_id=None, direct=None) -> list:
+ """
+ The request of get open orders.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param account_id: account id (mandatory)
+ :param side: The order side, buy or sell. If no side defined, will return all open orders of the account. (optional)
+ :param size: The number of orders to return. Range is [1, 500]. (optional)
+ :param direct: 1:prev order by ID asc from from_id, 2:next order by ID desc from from_id
+ :param from_id: start ID for search
+ :return: The orders information.
+ """
+ check_symbol(symbol)
+ check_range(size, 1, 500, "size")
+ check_should_not_none(account_id, "account_id")
+ params = {
+ "symbol" : symbol,
+ "account-id" : account_id,
+ "side" : side,
+ "size" : size,
+ "from" : from_id,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_open_orders import GetOpenOrdersService
+ return GetOpenOrdersService(params).request(**self.__kwargs)
+
+ def get_history_orders(self, symbol=None, start_time=None, end_time=None, size=None, direct=None)-> list:
+ """
+ Transfer Asset between Futures and Contract.
+
+ :param direct:
+ :param symbol: The target sub account uid to transfer to or from. (optional)
+ :param start_time: The crypto currency to transfer. (optional)
+ :param end_time: The amount of asset to transfer. (optional)
+ :param size: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (optional)
+ :return: The Order list.
+ """
+ params = {
+ "symbol" :symbol,
+ "start-time" : start_time,
+ "end-time" : end_time,
+ "size" : size,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_history_orders import GetHistoryOrdersService
+ return GetHistoryOrdersService(params).request(**self.__kwargs)
+
+ def get_match_result(self, symbol: 'str', order_type: 'OrderSide' = None, start_date: 'str' = None,
+ end_date: 'str' = None,
+ size: 'int' = None,
+ from_id: 'int' = None,
+ direct:'str'=None):
+ """
+ Search for the trade records of an account.
+
+ :param symbol: The symbol, like "btcusdt" (mandatory).
+ :param order_type: The types of order to include in the search (optional).
+ :param start_date: Search starts date in format yyyy-mm-dd. (optional).
+ :param end_date: Search ends date in format yyyy-mm-dd. (optional).
+ :param size: The number of orders to return, range [1-100]. (optional).
+ :param from_id: Search order id to begin with. (optional).
+ :return:
+ """
+
+ check_symbol(symbol)
+ start_date = format_date(start_date, "start_date")
+ end_date = format_date(end_date, "end_date")
+ check_range(size, 1, 100, "size")
+
+ params = {
+ "symbol" : symbol,
+ "start-date" : start_date,
+ "end-date" : end_date,
+ "types" : order_type,
+ "size" : size,
+ "from" : from_id,
+ "direct" : direct
+ }
+
+ from huobi.service.trade.get_match_results import GetMatchResultsService
+ return GetMatchResultsService(params).request(**self.__kwargs)
+
+ def get_match_results_by_order_id(self, order_id: 'int') -> list:
+ """
+ Get detail match results of an order.
+
+ :param order_id: The order id. (mandatory)
+ :return: The list of match result.
+ """
+ check_should_not_none(order_id, "order_id")
+
+ params = {
+ "order_id": order_id
+ }
+
+ from huobi.service.trade.get_match_results_by_order_id import GetMatchResultsByOrderIdService
+ return GetMatchResultsByOrderIdService(params).request(**self.__kwargs)
+
+ def order_source_desc(self, account_type):
+ default_source = "api"
+ if account_type:
+ if account_type == AccountType.MARGIN:
+ return "margin-api"
+ return default_source
+
+ def create_order_param_check(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', source:'str', client_order_id=None, stop_price=None, operator=None):
+ check_symbol(symbol)
+ check_should_not_none(account_id, "account_id")
+ check_should_not_none(order_type, "order_type")
+ check_should_not_none(amount, "amount")
+ check_should_not_none(source, "source")
+
+ if order_type == OrderType.SELL_LIMIT \
+ or order_type == OrderType.BUY_LIMIT \
+ or order_type == OrderType.BUY_LIMIT_MAKER \
+ or order_type == OrderType.SELL_LIMIT_MAKER:
+ check_should_not_none(price, "price")
+ if order_type in [OrderType.SELL_MARKET, OrderType.BUY_MARKET]:
+ price = None
+
+ params = {
+ "account-id" : account_id,
+ "amount" : amount,
+ "price": price,
+ "symbol": symbol,
+ "type": order_type,
+ "source": source,
+ "client-order-id": client_order_id,
+ "stop-price": stop_price,
+ "operator": operator
+ }
+
+ return params
+
+ def create_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', source:'str', client_order_id=None, stop_price=None, operator=None) -> int:
+ """
+ Make an order in huobi.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param account_id: Account id. (mandatory)
+ :param order_type: The order type. (mandatory)
+ :param source: The order source. (mandatory)
+ for spot, it's "api", see OrderSource.API
+ for margin, it's "margin-api", see OrderSource.MARGIN_API
+ for super margin, it's "super-margin-api", see OrderSource.SUPER_MARGIN_API
+ :param amount: The amount to buy (quote currency) or to sell (base currency). (mandatory)
+ :param price: The limit price of limit order, only needed for limit order. (mandatory for buy-limit, sell-limit, buy-limit-maker and sell-limit-maker)
+ :param client_order_id: unique Id which is user defined and must be unique in recent 24 hours
+ :param stop_price: Price for auto sell to get the max benefit
+ :param operator: the condition for stop_price, value can be "gte" or "lte", gte – greater than and equal (>=), lte – less than and equal (<=)
+ :return: The order id.
+ """
+
+ params = self.create_order_param_check(symbol, account_id, order_type, amount,
+ price, source, client_order_id, stop_price, operator)
+ from huobi.service.trade.post_create_order import PostCreateOrderService
+ return PostCreateOrderService(params).request(**self.__kwargs)
+
+ def create_spot_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', client_order_id=None, stop_price=None,
+ operator=None) -> int:
+ order_source = OrderSource.API
+ return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount,
+ price=price, source=order_source, client_order_id=client_order_id, stop_price=stop_price,
+ operator=operator)
+
+ def create_margin_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', client_order_id=None, stop_price=None,
+ operator=None) -> int:
+ order_source = OrderSource.MARGIN_API
+ return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount,
+ price=price, source=order_source, client_order_id=client_order_id,
+ stop_price=stop_price,
+ operator=operator)
+
+ def create_super_margin_order(self, symbol: 'str', account_id: 'int', order_type: 'OrderType', amount: 'float',
+ price: 'float', client_order_id=None, stop_price=None,
+ operator=None) -> int:
+ order_source = OrderSource.SUPER_MARGIN_API
+ return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount,
+ price=price, source=order_source, client_order_id=client_order_id,
+ stop_price=stop_price,
+ operator=operator)
+
+ def cancel_order(self, symbol, order_id):
+ check_symbol(symbol)
+ check_should_not_none(order_id, "order_id")
+
+ params = {
+ "order_id" : order_id
+ }
+
+ from huobi.service.trade.post_cancel_order import PostCancelOrderService
+ return PostCancelOrderService(params).request(**self.__kwargs)
+
+ def cancel_orders(self, symbol, order_id_list)->BatchCancelResult:
+ """
+ Submit cancel request for cancelling multiple orders.
+
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param order_id_list: The list of order id. the max size is 50. (mandatory)
+ :return: No return
+ """
+ check_symbol(symbol)
+ check_should_not_none(order_id_list, "order_id_list")
+ check_list(order_id_list, 1, 50, "order_id_list")
+
+ string_list = list()
+ for order_id in order_id_list:
+ string_list.append(str(order_id))
+
+ params = {
+ "order-ids" : string_list
+ }
+
+ from huobi.service.trade.post_batch_cancel_order import PostBatchCancelOrderService
+ return PostBatchCancelOrderService(params).request(**self.__kwargs)
+
+ def cancel_open_orders(self, account_id, symbols: 'str'=None , side=None, size=None)->BatchCancelCount:
+ """
+ Request to cancel open orders.
+
+ :param symbols: The symbol, like "btcusdt".
+ :param account_type: Account type. (mandatory)
+ :param side: The order side, buy or sell. If no side defined, will cancel all open orders of the account. (optional)
+ :param size: The number of orders to cancel. Range is [1, 100]. (optional)
+ :return: Status of batch cancel result.
+ """
+ check_should_not_none(account_id, "account_id")
+
+ params = {
+ "account-id": account_id,
+ "symbol" : symbols,
+ "side" : side,
+ "size" : size
+ }
+
+ from huobi.service.trade.post_batch_cancel_open_order import PostBatchCancelOpenOrderService
+ return PostBatchCancelOpenOrderService(params).request(**self.__kwargs)
+
+ def cancel_client_order(self, client_order_id)->int:
+ """
+ Request to cancel open orders.
+
+ :param client_order_id: user defined unique order id
+ """
+ check_should_not_none(client_order_id, "client-order-id")
+
+ params = {
+ "client-order-id" : client_order_id
+ }
+
+ from huobi.service.trade.post_cancel_client_order import PostCancelClientOrderService
+ return PostCancelClientOrderService(params).request(**self.__kwargs)
+
+ def transfer_between_futures_and_pro(self, currency: 'str', amount: 'float',
+ transfer_type: 'TransferFuturesPro')-> int:
+ """
+ Transfer Asset between Futures and Contract.
+
+ :param sub_uid: The target sub account uid to transfer to or from. (mandatory)
+ :param currency: The crypto currency to transfer. (mandatory)
+ :param amount: The amount of asset to transfer. (mandatory)
+ :param transfer_type: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (mandatory)
+ :return: The order id.
+ """
+ check_currency(currency)
+ check_should_not_none(currency, "currency")
+ check_should_not_none(amount, "amount")
+ check_should_not_none(transfer_type, "transfer_type")
+ params = {
+ "currency" : currency,
+ "amount" : amount,
+ "type" : transfer_type
+
+ }
+
+ from huobi.service.trade.post_transfer_futures_pro import PostTransferFuturesProService
+ return PostTransferFuturesProService(params).request(**self.__kwargs)
+
+ def batch_create_order(self, order_config_list) -> int:
+ """
+ Make an order in huobi.
+ :param order_config_list: order config list, it can batch create orders, and each order config check as below
+ : items as below
+ :param symbol: The symbol, like "btcusdt". (mandatory)
+ :param account_type: Account type. (mandatory)
+ :param order_type: The order type. (mandatory)
+ :param amount: The amount to buy (quote currency) or to sell (base currency). (mandatory)
+ :param price: The limit price of limit order, only needed for limit order. (mandatory for buy-limit, sell-limit, buy-limit-maker and sell-limit-maker)
+ :param client_order_id: unique Id which is user defined and must be unique in recent 24 hours
+ :param stop_price: Price for auto sell to get the max benefit
+ :param operator: the condition for stop_price, value can be "gte" or "lte", gte – greater than and equal (>=), lte – less than and equal (<=)
+ :return: The order id.
+ """
+
+ check_should_not_none(order_config_list, "order_config_list")
+ check_list(order_config_list, 1, 10, "create order config list")
+
+ new_config_list = list()
+ for item in order_config_list:
+ new_item = self.create_order_param_check(
+ item.get("symbol", None),
+ item.get("account_id", None),
+ item.get("order_type", None),
+ item.get("amount", None),
+ item.get("price", None),
+ item.get("source", None),
+ item.get("client_order_id", None),
+ item.get("stop-price", None),
+ item.get("operator", None))
+
+ new_config_list.append(new_item)
+
+ from huobi.service.trade.post_batch_create_order import PostBatchCreateOrderService
+ return PostBatchCreateOrderService(new_config_list).request(**self.__kwargs)
+
+ def sub_trade_clearing(self, symbols: 'str', callback, error_handler=None):
+ """
+ Subscribe trade clearing by symbol
+
+ :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt".
+ "*" for all symbols
+ :param callback: The implementation is required. onReceive will be called if receive server's update.
+ example: def callback(price_depth_event: 'PriceDepthEvent'):
+ pass
+ :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server
+ example: def error_handler(exception: 'HuobiApiException')
+ pass
+
+ :return: No return
+ """
+ check_should_not_none(symbols, "symbols")
+ symbol_list = symbols.split(",")
+ if ("*" in symbol_list):
+ symbol_list = ["*"]
+ else:
+ check_symbol_list(symbol_list)
+
+ check_should_not_none(callback, "callback")
+
+ params = {
+ "symbol_list": symbol_list,
+ }
+
+ from huobi.service.trade.sub_trade_clearing_v2 import SubTradeClearingV2Service
+ SubTradeClearingV2Service(params).subscribe(callback, error_handler, **self.__kwargs)
diff --git a/huobi/client/wallet.py b/huobi/client/wallet.py
new file mode 100644
index 0000000..519b822
--- /dev/null
+++ b/huobi/client/wallet.py
@@ -0,0 +1,179 @@
+from huobi.utils.input_checker import *
+from huobi.model.wallet import *
+
+
+class WalletClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ init_log: to init logger
+ """
+ self.__kwargs = kwargs
+
+ def get_deposit_withdraw(self, op_type: 'str', currency: 'str' = None, from_id: 'int' = None, size: 'int' = None,
+ direct: 'str' = None) -> list:
+ """
+ Get the withdraw records of an account.
+
+ :param currency: The currency, like "btc". (optional)
+ :param from_id: The beginning withdraw record id. (optional)
+ :param op_type: deposit or withdraw, see defination DepositWithdraw (mandatory)
+ :param size: The size of record. (optional)
+ :param direct: "prev" is order by asc, "next" is order by desc, default as "prev"(optional)
+ :return: The list of withdraw records.
+ """
+ check_should_not_none(op_type, "operate type")
+
+ params = {
+ "currency": currency,
+ "type": op_type,
+ "from": from_id,
+ "direct": direct,
+ "size": size
+ }
+
+ from huobi.service.wallet.get_deposit_withdraw import GetDepositWithdrawService
+ return GetDepositWithdrawService(params).request(**self.__kwargs)
+
+ def post_create_withdraw(self, address: 'str', amount: 'float', currency: 'str', fee: 'float',
+ chain: 'str' = None, address_tag: 'str' = None) -> int:
+ """
+ Submit a request to withdraw some asset from an account.
+
+ :param address: The destination address of this withdraw. (mandatory)
+ :param amount: The amount of currency to withdraw. (mandatory)
+ :param currency: The crypto currency to withdraw. (mandatory)
+ :param fee: The fee to pay with this withdraw. (mandatory)
+ :param address_tag: A tag specified for this address. (optional)
+ :param chain: set as "usdt" to withdraw USDT to OMNI, set as "trc20usdt" to withdraw USDT to TRX. (optional)
+ :return: Withdraw id
+ """
+ check_symbol(currency)
+ check_should_not_none(address, "address")
+ check_should_not_none(amount, "amount")
+ check_should_not_none(fee, "fee")
+
+ params = {
+ "currency": currency,
+ "address": address,
+ "amount": amount,
+ "fee": fee,
+ "chain": chain,
+ "addr-tag": address_tag
+ }
+
+ from huobi.service.wallet.post_create_withdraw import PostCreateWithdrawService
+ return PostCreateWithdrawService(params).request(**self.__kwargs)
+
+ def post_cancel_withdraw(self, withdraw_id: 'int') -> int:
+ """
+ Cancel an withdraw request.
+
+ :param withdraw_id: withdraw id (mandatory)
+ :return: No return.
+ """
+ params = {
+ "withdraw-id": withdraw_id
+ }
+
+ from huobi.service.wallet.post_cancel_withdraw import PostCancelWithdrawService
+ return PostCancelWithdrawService(params).request(**self.__kwargs)
+
+ def get_account_deposit_address(self, currency: 'str') -> list:
+ """
+ Get deposit address of corresponding chain, for a specific crypto currency (except IOTA)
+
+ :param currency: The currency, like "btc". (optional)
+ :return:
+ """
+ check_should_not_none(currency, "currency")
+
+ params = {
+ "currency": currency
+ }
+
+ from huobi.service.wallet.get_account_deposit_address import GetAccountDepositAddressService
+ return GetAccountDepositAddressService(params).request(**self.__kwargs)
+
+ def get_account_withdraw_quota(self, currency: 'str') -> list:
+ """
+ Get the withdraw quota for currencies
+
+ :param currency: The currency, like "btc". (mandatory)
+ :return:
+ """
+ check_should_not_none(currency, "currency")
+
+ params = {
+ "currency": currency,
+ }
+
+ from huobi.service.wallet.get_account_withdraw_quota import GetAccountWithdrawQuotaService
+ return GetAccountWithdrawQuotaService(params).request(**self.__kwargs)
+
+ def get_sub_user_deposit_history(self, sub_uid: 'int', currency: 'str' = None,
+ start_time: 'int' = None, end_time: 'int' = None,
+ sort: 'str' = None, limit: 'int' = None, from_id: 'int' = None) -> DepositHistory:
+ """
+ Parent get sub user depoist history.
+
+ :param sub_uid: Sub user id. (mandatory)
+ :param currency: Cryptocurrency.
+ :param start_time: Farthest time
+ :param end_time: Nearest time
+ :param sort: Sorting order
+ :param limit: Maximum number of items in one page
+ :param from_id: First record Id in this query
+ """
+ check_should_not_none(sub_uid, "sub_uid")
+
+ params = {
+ "subUid": sub_uid,
+ "currency": currency,
+ "startTime": start_time,
+ "endTime": end_time,
+ "sort": sort,
+ "limit": limit,
+ "fromId": from_id
+ }
+
+ from huobi.service.wallet.get_sub_user_deposit_history import GetSubUserDepositHistoryService
+ return GetSubUserDepositHistoryService(params).request(**self.__kwargs)
+
+ def get_sub_user_deposit_address(self, sub_uid: 'int', currency: 'str') -> list:
+ """
+ Parent get sub user deposit address
+
+ :param sub_uid: Sub user id
+ :param currency: Cryptocurrency, like "btc". (mandatory)
+ :return:
+ """
+
+ check_should_not_none(sub_uid, "subUid")
+ check_should_not_none(currency, "currency")
+ params = {
+ "subUid": sub_uid,
+ "currency": currency
+ }
+
+ from huobi.service.wallet.get_sub_user_deposit_address import GetSubUserDepositAddressService
+ return GetSubUserDepositAddressService(params).request(**self.__kwargs)
+
+ def get_account_withdraw_address(self, currency: 'str', chain: 'str'=None, note: 'str'=None, limit: 'int' = 100,
+ fromid: 'int' = None):
+ check_should_not_none(currency, "currency")
+ params = {
+ "currency": currency,
+ "chain": chain,
+ "note": note,
+ "limit": limit,
+ "fromid": fromid
+ }
+ from huobi.service.wallet.get_account_withdraw_address import GetAccountWithdrawAddressService
+ return GetAccountWithdrawAddressService(params).request(**self.__kwargs)
+
diff --git a/huobi/connection/__init__.py b/huobi/connection/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/connection/impl/__init__.py b/huobi/connection/impl/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/huobi/connection/impl/__init__.py
@@ -0,0 +1 @@
+
diff --git a/huobi/connection/impl/private_def.py b/huobi/connection/impl/private_def.py
new file mode 100644
index 0000000..a64c805
--- /dev/null
+++ b/huobi/connection/impl/private_def.py
@@ -0,0 +1,11 @@
+
+
+class ConnectionState:
+ IDLE = 0
+ CONNECTED = 1
+ WAIT_RECONNECT = 2
+ CLOSED_ON_ERROR = 3
+ CLOSED = 4
+
+CONNECT_HEART_BEAT_LIMIT_MS = 60000 # max interval between two package
+RECONNECT_AFTER_TIME_MS = 63000 # if not need connect immediately, it will enter delay connect status and will connect after setting times
\ No newline at end of file
diff --git a/huobi/connection/impl/restapi_invoker.py b/huobi/connection/impl/restapi_invoker.py
new file mode 100644
index 0000000..6c23d5f
--- /dev/null
+++ b/huobi/connection/impl/restapi_invoker.py
@@ -0,0 +1,94 @@
+import requests
+from huobi.exception.huobi_api_exception import HuobiApiException
+from huobi.utils.etf_result import etf_result_check
+from huobi.utils import *
+import time
+
+from huobi.utils.print_mix_object import TypeCheck
+
+session = requests.Session()
+
+def check_response(dict_data):
+ status = dict_data.get("status", None)
+ code = dict_data.get("code", None)
+ success = dict_data.get("success", None)
+ if status and len(status):
+ if TypeCheck.is_basic(status): # for normal case
+ if status == "error":
+ err_code = dict_data.get("err-code", 0)
+ err_msg = dict_data.get("err-msg", "")
+ raise HuobiApiException(HuobiApiException.EXEC_ERROR,
+ "[Executing] " + str(err_code) + ": " + err_msg)
+ elif status != "ok":
+ raise HuobiApiException(HuobiApiException.RUNTIME_ERROR,
+ "[Invoking] Response is not expected: " + status)
+ elif TypeCheck.is_dict(status): # for https://status.huobigroup.com/api/v2/summary.json in example example/generic/get_system_status.py
+ if dict_data.get("page") and dict_data.get("components"):
+ pass
+ else:
+ raise HuobiApiException(HuobiApiException.EXEC_ERROR,
+ "[Executing] System is in maintenances")
+ elif code:
+ code_int = int(code)
+ if code_int != 200:
+ err_code = dict_data.get("code", 0)
+ err_msg = dict_data.get("message", "")
+ raise HuobiApiException(HuobiApiException.EXEC_ERROR,
+ "[Executing] " + str(err_code) + ": " + err_msg)
+ elif success is not None:
+ if bool(success) is False:
+ err_code = etf_result_check(dict_data.get("code"))
+ err_msg = dict_data.get("message", "")
+ if err_code == "":
+ raise HuobiApiException(HuobiApiException.EXEC_ERROR, "[Executing] " + err_msg)
+ else:
+ raise HuobiApiException(HuobiApiException.EXEC_ERROR, "[Executing] " + str(err_code) + ": " + err_msg)
+ else:
+ raise HuobiApiException(HuobiApiException.RUNTIME_ERROR, "[Invoking] Status cannot be found in response.")
+
+
+def call_sync(request, is_checked=False):
+ if request.method == "GET":
+ # print("call_sync url : " , request.host + request.url)
+ response = session.get(request.host + request.url, headers=request.header)
+ if is_checked is True:
+ return response.text
+ #dict_data = json.loads(response.text, encoding="utf-8")
+ dict_data = json.loads(response.text)
+ # print("call_sync === recv data : ", dict_data)
+ check_response(dict_data)
+ return request.json_parser(dict_data)
+
+ elif request.method == "POST":
+ response = session.post(request.host + request.url, data=json.dumps(request.post_body), headers=request.header)
+ dict_data = json.loads(response.text, encoding="utf-8")
+ # print("call_sync === recv data : ", dict_data)
+ check_response(dict_data)
+ return request.json_parser(dict_data)
+
+def call_sync_perforence_test(request, is_checked=False):
+ if request.method == "GET":
+ inner_start_time = time.time()
+ # print("call_sync_perforence_test url : ", request.host + request.url)
+ response = session.get(request.host + request.url, headers=request.header)
+ #print("call_sync_perforence_test data :", response.text)
+ inner_end_time = time.time()
+ cost_manual = round(inner_end_time - inner_start_time, 6)
+ req_cost = response.elapsed.total_seconds()
+ if is_checked is True:
+ return response.text
+ dict_data = json.loads(response.text, encoding="utf-8")
+ # print("call_sync === recv data : ", dict_data)
+ check_response(dict_data)
+ return request.json_parser(dict_data), req_cost, cost_manual
+
+ elif request.method == "POST":
+ inner_start_time = time.time()
+ response = session.post(request.host + request.url, data=json.dumps(request.post_body), headers=request.header)
+ inner_end_time = time.time()
+ cost_manual = round(inner_end_time - inner_start_time, 6)
+ req_cost = response.elapsed.total_seconds()
+ dict_data = json.loads(response.text, encoding="utf-8")
+ # print("call_sync === recv data : ", dict_data)
+ check_response(dict_data)
+ return request.json_parser(dict_data), req_cost, cost_manual
diff --git a/huobi/connection/impl/restapi_request.py b/huobi/connection/impl/restapi_request.py
new file mode 100644
index 0000000..d6e029f
--- /dev/null
+++ b/huobi/connection/impl/restapi_request.py
@@ -0,0 +1,11 @@
+
+class RestApiRequest(object):
+
+ def __init__(self):
+ self.method = ""
+ self.url = ""
+ self.host = ""
+ self.post_body = ""
+ self.header = dict()
+ self.json_parser = None
+
diff --git a/huobi/connection/impl/websocket_manage.py b/huobi/connection/impl/websocket_manage.py
new file mode 100644
index 0000000..72bc10c
--- /dev/null
+++ b/huobi/connection/impl/websocket_manage.py
@@ -0,0 +1,281 @@
+import threading
+import websocket
+import gzip
+import ssl
+import logging
+import urllib.parse
+
+from huobi.constant import *
+from huobi.utils import *
+from huobi.exception.huobi_api_exception import HuobiApiException
+from huobi.connection.impl.private_def import ConnectionState
+
+# Key: original_connection, Value: connection
+websocket_connection_handler = dict()
+
+
+def on_message(original_connection, message):
+ websocket_connection = websocket_connection_handler[original_connection]
+ websocket_connection.on_message(message)
+ return
+
+
+def on_error(original_connection, error):
+ websocket_connection = websocket_connection_handler[original_connection]
+ websocket_connection.on_failure(error)
+
+
+def on_close(original_connection):
+ websocket_connection = websocket_connection_handler[original_connection]
+ websocket_connection.on_close()
+
+
+def on_open(original_connection):
+ websocket_connection = websocket_connection_handler[original_connection]
+ websocket_connection.on_open(original_connection)
+
+
+connection_id = 0
+
+
+def websocket_func(*args):
+ try:
+ websocket_manage = args[0]
+ websocket_manage.original_connection = websocket.WebSocketApp(websocket_manage.url,
+ on_message=on_message,
+ on_error=on_error,
+ on_close=on_close)
+ global websocket_connection_handler
+ websocket_connection_handler[websocket_manage.original_connection] = websocket_manage
+ websocket_manage.logger.info("[Sub][" + str(websocket_manage.id) + "] Connecting...")
+ websocket_manage.original_connection.on_open = on_open
+ websocket_manage.original_connection.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
+ websocket_manage.logger.info("[Sub][" + str(websocket_manage.id) + "] Connection event loop down")
+ if websocket_manage.state == ConnectionState.CONNECTED:
+ websocket_manage.state = ConnectionState.IDLE
+ except Exception as ex:
+ print(ex)
+
+class WebsocketManage:
+
+ def __init__(self, api_key, secret_key, uri, request):
+ self.__thread = None
+ self.__market_url = HUOBI_WEBSOCKET_URI_PRO + "/ws"
+ self.__trading_url = HUOBI_WEBSOCKET_URI_PRO + "/ws/" + request.api_version
+ self.__mbp_feed_url = HUOBI_WEBSOCKET_URI_PRO + "/feed"
+ self.__api_key = api_key
+ self.__secret_key = secret_key
+ self.request = request
+ self.reconnect_at = 0
+ self.original_connection = None
+ self.last_receive_time = 0
+ self.logger = logging.getLogger("huobi-client")
+ self.state = ConnectionState.IDLE
+ global connection_id
+ connection_id += 1
+ self.id = connection_id
+ host = urllib.parse.urlparse(uri).hostname
+ if host.find("api") == 0:
+ self.__market_url = "wss://" + host + "/ws"
+ self.__mbp_feed_url = "wss://" + host + "/feed"
+ self.__trading_url = "wss://" + host + "/ws/" + request.api_version
+ else:
+ self.__market_url = "wss://" + host + "/api/ws"
+ self.__mbp_feed_url = "wss://" + host + "/feed"
+ self.__trading_url = "wss://" + host + "/ws/" + request.api_version
+
+ if request.is_trading:
+ self.url = self.__trading_url
+ elif request.is_mbp_feed:
+ self.url = self.__mbp_feed_url
+ else:
+ self.url = self.__market_url
+
+ def close_and_wait_reconnect(self, delay_in_ms):
+ if self.original_connection is not None:
+ self.original_connection.close()
+ self.original_connection = None
+ self.state = ConnectionState.WAIT_RECONNECT
+ self.reconnect_at = + delay_in_ms
+ self.logger.warning("[Sub][%d] Lost connectiong for %d ms, will try reconnecting " % (self.id, self.reconnect_at))
+
+ def re_connect(self):
+ if get_current_timestamp() > self.reconnect_at:
+ self.logger.info("[Sub][%d] Reconnecting ... " % self.id)
+ self.connect()
+
+ def connect(self):
+ if self.state == ConnectionState.CONNECTED:
+ self.logger.info("[Sub][" + str(self.id) + "] Already connected")
+ else:
+ self.__thread = threading.Thread(target=websocket_func, args=[self])
+ self.__thread.start()
+
+ def send(self, data):
+ # print("Send Data : " + data)
+ self.original_connection.send(data)
+
+ def close(self):
+ self.original_connection.close()
+ del websocket_connection_handler[self.original_connection]
+ self.state = ConnectionState.CLOSED
+ self.logger.info("[Sub][" + str(self.id) + "] Closing normally")
+
+ def on_open(self, original_connection):
+ self.logger.info("[Sub][" + str(self.id) + "] Connected to server")
+ self.original_connection = original_connection
+ self.last_receive_time = get_current_timestamp()
+ self.state = ConnectionState.CONNECTED
+ if self.request.is_trading:
+ try:
+ if self.request.api_version == ApiVersion.VERSION_V1:
+ builder = UrlParamsBuilder()
+ create_signature(self.__api_key, self.__secret_key,
+ "GET", self.url, builder)
+ builder.put_url("op", "auth")
+ self.send(builder.build_url_to_json())
+ elif self.request.api_version == ApiVersion.VERSION_V2:
+ builder = UrlParamsBuilder()
+ create_signature_v2(self.__api_key, self.__secret_key,
+ "GET", self.url, builder)
+ self.send(builder.build_url_to_json())
+ else:
+ self.on_error("api version for create the signature fill failed")
+
+ except Exception as e:
+ self.on_error("Unexpected error when create the signature: " + str(e))
+ else:
+ if self.request.subscription_handler is not None:
+ self.request.subscription_handler(self)
+ return
+
+ def on_error(self, error_message):
+ if self.request.error_handler is not None:
+ exception = HuobiApiException(HuobiApiException.SUBSCRIPTION_ERROR, error_message)
+ self.request.error_handler(exception)
+ self.logger.error("[Sub][" + str(self.id) + "] " + str(error_message))
+
+ def on_failure(self, error):
+ self.on_error("Unexpected error: " + str(error))
+ self.close_on_error()
+
+ def on_message(self, message):
+ self.last_receive_time = get_current_timestamp()
+ if isinstance(message, (str)): # V2
+ # print("RX string : ", message)
+ dict_data = json.loads(message)
+ elif isinstance(message, (bytes)): # V1
+ # print("RX bytes: " + gzip.decompress(message).decode("utf-8"))
+ dict_data = json.loads(gzip.decompress(message).decode("utf-8"))
+ else:
+ print("RX unknow type : ", type(message))
+ return
+
+ status_outer = dict_data.get("status", "")
+ err_code_outer = dict_data.get("err-code", 0)
+ op_outer = dict_data.get("op", "")
+ action_outer = dict_data.get("action", "")
+ ch_outer = dict_data.get("ch", "")
+ rep_outer = dict_data.get("rep", "")
+ ping_market_outer = int(dict_data.get("ping", 0))
+ if status_outer and len(status_outer) and status_outer != "ok":
+ error_code = dict_data.get("err-code", "Unknown error")
+ error_msg = dict_data.get("err-msg", "Unknown error")
+ self.on_error(error_code + ": " + error_msg)
+ elif err_code_outer and int(err_code_outer) != 0:
+ error_code = dict_data.get("err-code", "Unknown error")
+ error_msg = dict_data.get("err-msg", "Unknown error")
+ self.on_error(error_code + ": " + error_msg)
+ elif op_outer and len(op_outer): # for V1
+ if op_outer == "notify":
+ self.__on_receive(dict_data)
+ elif op_outer == "ping":
+ #print("******** receive trade ping pong ********", dict_data)
+ ping_ts = dict_data.get("ts", 0)
+ self.__process_ping_on_trading_line(ping_ts)
+ elif op_outer == "auth":
+ if self.request.subscription_handler is not None:
+ self.request.subscription_handler(self)
+ elif op_outer == "req":
+ self.__on_receive(dict_data)
+ elif action_outer and len(action_outer): # for V2
+ if action_outer == "ping":
+ action_data = dict_data.get("data")
+ ping_ts = action_data.get("ts")
+ self.__process_ping_on_v2_trade(ping_ts)
+ elif action_outer == "sub":
+ action_code = dict_data.get("code", -1)
+ if action_code == 200:
+ logging.info("subscribe ACK received")
+ else:
+ logging.error("receive error data : " + message)
+ elif action_outer == "req": #
+ action_code = dict_data.get("code", -1)
+ if action_code == 200:
+ logging.info("signature ACK received")
+ if self.request.subscription_handler is not None:
+ self.request.subscription_handler(self)
+ else:
+ logging.error("receive error data : " + message)
+ elif action_outer == "push":
+ action_data = dict_data.get("data")
+ if action_data:
+ self.__on_receive(dict_data)
+ else:
+ logging.error("receive error push data : " + message)
+
+ elif ch_outer and len(ch_outer):
+ self.__on_receive(dict_data)
+ elif rep_outer and len(rep_outer):
+ self.__on_receive(dict_data)
+ elif ping_market_outer:
+ #print("******** receive market ping pong ********", dict_data)
+ ping_ts = ping_market_outer
+ self.__process_ping_on_market_line(ping_ts)
+ else:
+ #print("unknown data process, RX: ", gzip.decompress(message).decode("utf-8"))
+ pass
+
+ def __on_receive(self, dict_data):
+ res = None
+ try:
+ if self.request.json_parser is not None:
+ res = self.request.json_parser(dict_data)
+ except Exception as e:
+ self.on_error("Failed to parse server's response: " + str(e))
+
+ try:
+ if self.request.update_callback is not None:
+ self.request.update_callback(res)
+ except Exception as e:
+ self.on_error("Process error: " + str(e)
+ + " You should capture the exception in your error handler")
+
+ # websocket request will close the connection after receive
+ if self.request.auto_close:
+ self.close()
+
+ def __process_ping_on_trading_line(self, ping_ts):
+ #print("### __process_ping_on_trading_line ###")
+ #self.send("{\"op\":\"pong\",\"ts\":" + str(get_current_timestamp()) + "}")
+ PrintBasic.print_basic(ping_ts, "response time")
+ self.send("{\"op\":\"pong\",\"ts\":" + str(ping_ts) + "}")
+ return
+
+ def __process_ping_on_market_line(self, ping_ts):
+ #print("### __process_ping_on_market_line ###")
+ #self.send("{\"pong\":" + str(get_current_timestamp()) + "}")
+ PrintBasic.print_basic(ping_ts, "response time")
+ self.send("{\"pong\":" + str(ping_ts) + "}")
+ return
+
+ def __process_ping_on_v2_trade(self, ping_ts):
+ # PrintDate.timestamp_to_date(ping_ts)
+ self.send("{\"action\": \"pong\",\"data\": {\"ts\": " + str(ping_ts) +"}}")
+ return
+
+ def close_on_error(self):
+ if self.original_connection is not None:
+ self.original_connection.close()
+ self.state = ConnectionState.CLOSED_ON_ERROR
+ self.logger.error("[Sub][" + str(self.id) + "] Connection is closing due to error")
diff --git a/huobi/connection/impl/websocket_request.py b/huobi/connection/impl/websocket_request.py
new file mode 100644
index 0000000..9efd1de
--- /dev/null
+++ b/huobi/connection/impl/websocket_request.py
@@ -0,0 +1,14 @@
+from huobi.constant import ApiVersion
+
+
+class WebsocketRequest(object):
+
+ def __init__(self):
+ self.subscription_handler = None
+ self.auto_close = False # close connection after receive data, for subscribe set False, for request set True
+ self.is_trading = False
+ self.is_mbp_feed = False
+ self.error_handler = None
+ self.json_parser = None
+ self.update_callback = None
+ self.api_version = ApiVersion.VERSION_V1 # v1 as default
diff --git a/huobi/connection/impl/websocket_watchdog.py b/huobi/connection/impl/websocket_watchdog.py
new file mode 100644
index 0000000..f755def
--- /dev/null
+++ b/huobi/connection/impl/websocket_watchdog.py
@@ -0,0 +1,67 @@
+import threading
+import logging
+from apscheduler.schedulers.blocking import BlockingScheduler
+
+from huobi.connection.impl.private_def import *
+from huobi.utils.time_service import get_current_timestamp
+
+
+def watch_dog_job(*args):
+ watch_dog_obj = args[0]
+
+ for idx, websocket_manage in enumerate(watch_dog_obj.websocket_manage_list):
+ if websocket_manage.request.auto_close: # setting auto close no need reconnect
+ pass
+ elif websocket_manage.state == ConnectionState.CONNECTED:
+ if watch_dog_obj.is_auto_connect:
+ ts = get_current_timestamp() - websocket_manage.last_receive_time
+ if ts > watch_dog_obj.heart_beat_limit_ms:
+ watch_dog_obj.logger.warning("[Sub][" + str(websocket_manage.id) + "] No response from server")
+ websocket_manage.close_and_wait_reconnect(watch_dog_obj.wait_reconnect_millisecond())
+ elif websocket_manage.state == ConnectionState.WAIT_RECONNECT:
+ watch_dog_obj.logger.warning("[Sub] call re_connect")
+ websocket_manage.re_connect()
+ pass
+ elif websocket_manage.state == ConnectionState.CLOSED_ON_ERROR:
+ if watch_dog_obj.is_auto_connect:
+ websocket_manage.close_and_wait_reconnect(watch_dog_obj.reconnect_after_ms)
+ pass
+
+
+class WebSocketWatchDog(threading.Thread):
+ mutex = threading.Lock()
+ websocket_manage_list = list()
+
+ def __init__(self, is_auto_connect=True, heart_beat_limit_ms=CONNECT_HEART_BEAT_LIMIT_MS, reconnect_after_ms=RECONNECT_AFTER_TIME_MS):
+ threading.Thread.__init__(self)
+ self.is_auto_connect = is_auto_connect
+ self.heart_beat_limit_ms = heart_beat_limit_ms
+ self.reconnect_after_ms = reconnect_after_ms if reconnect_after_ms > heart_beat_limit_ms else heart_beat_limit_ms
+ self.logger = logging.getLogger("huobi-client")
+ self.scheduler = BlockingScheduler()
+ self.scheduler.add_job(watch_dog_job, "interval", max_instances=10, seconds=1, args=[self])
+ self.start()
+
+ def run(self):
+ self.scheduler.start()
+
+ def on_connection_created(self, websocket_manage):
+ self.mutex.acquire()
+ self.websocket_manage_list.append(websocket_manage)
+ self.mutex.release()
+
+ def on_connection_closed(self, websocket_manage):
+ self.mutex.acquire()
+ self.websocket_manage_list.remove(websocket_manage)
+ self.mutex.release()
+
+ # calculate next reconnect time
+ def wait_reconnect_millisecond(self):
+ wait_millisecond = int(self.reconnect_after_ms - self.heart_beat_limit_ms)
+ now_ms = get_current_timestamp()
+ wait_millisecond = wait_millisecond if wait_millisecond else 1000
+ # job loop after 1 second
+ return (wait_millisecond + now_ms)
+
+
+
diff --git a/huobi/connection/restapi_sync_client.py b/huobi/connection/restapi_sync_client.py
new file mode 100644
index 0000000..22e2207
--- /dev/null
+++ b/huobi/connection/restapi_sync_client.py
@@ -0,0 +1,154 @@
+import logging
+
+from huobi.connection.impl.restapi_invoker import call_sync, call_sync_perforence_test
+from huobi.connection.impl.restapi_request import RestApiRequest
+from huobi.constant import *
+from huobi.utils import *
+
+from huobi.exception.huobi_api_exception import HuobiApiException
+
+
+
+class RestApiSyncClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the request client instance.
+ :param kwargs: The option of request connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: The URL name like "https://api.huobi.pro".
+ performance_test: for performance test
+ init_log: to init logger
+ """
+ self.__api_key = kwargs.get("api_key", None)
+ self.__secret_key = kwargs.get("secret_key", None)
+ self.__server_url = kwargs.get("url", get_default_server_url(None))
+ self.__init_log = kwargs.get("init_log", None)
+ self.__performance_test = kwargs.get("performance_test", None)
+ if self.__init_log and self.__init_log:
+ logger = logging.getLogger("huobi-client")
+ logger.setLevel(level=logging.INFO)
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
+ logger.addHandler(handler)
+
+ def __create_request_by_get(self, url, builder):
+ request = RestApiRequest()
+ request.method = "GET"
+ request.host = self.__server_url
+ request.header.update({'Content-Type': 'application/json'})
+ request.url = url + builder.build_url()
+ return request
+
+ def __create_request_by_post_with_signature(self, url, builder):
+ request = RestApiRequest()
+ request.method = "POST"
+ request.host = self.__server_url
+ create_signature(self.__api_key, self.__secret_key, request.method, request.host + url, builder)
+ request.header.update({'Content-Type': 'application/json'})
+ if (len(builder.post_list)): # specify for case : /v1/order/batch-orders
+ request.post_body = builder.post_list
+ else:
+ request.post_body = builder.post_map
+ request.url = url + builder.build_url()
+ return request
+
+ def __create_request_by_get_with_signature(self, url, builder):
+ request = RestApiRequest()
+ request.method = "GET"
+ request.host = self.__server_url
+ create_signature(self.__api_key, self.__secret_key, request.method, request.host + url, builder)
+ request.header.update({"Content-Type": "application/x-www-form-urlencoded"})
+ request.url = url + builder.build_url()
+ return request
+
+ def create_request(self, method, url, params, parse):
+ builder = UrlParamsBuilder()
+ if params and len(params):
+ if method in [HttpMethod.GET, HttpMethod.GET_SIGN]:
+ for key, value in params.items():
+ builder.put_url(key, value)
+ elif method in [HttpMethod.POST, HttpMethod.POST_SIGN]:
+ for key, value in params.items():
+ builder.put_post(key, value)
+ else:
+ raise HuobiApiException(HuobiApiException.EXEC_ERROR,
+ "[error] undefined HTTP method")
+
+ if method == HttpMethod.GET:
+ request = self.__create_request_by_get(url, builder)
+ elif method == HttpMethod.GET_SIGN:
+ request = self.__create_request_by_get_with_signature(url, builder)
+ elif method == HttpMethod.POST_SIGN:
+ request = self.__create_request_by_post_with_signature(url, builder)
+ elif method == HttpMethod.POST:
+ request = self.__create_request_by_post_with_signature(url, builder)
+ else:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] " + method + " is invalid http method")
+
+ request.json_parser = parse
+
+ return request
+
+ """
+ for post batch operation, such as batch create orders[ /v1/order/batch-orders ]
+ """
+ def create_request_post_batch(self, method, url, params, parse):
+ builder = UrlParamsBuilder()
+ if params and len(params):
+ if method in [HttpMethod.POST, HttpMethod.POST_SIGN]:
+ if isinstance(params, list):
+ builder.post_list = params
+ else:
+ raise HuobiApiException(HuobiApiException.EXEC_ERROR,
+ "[error] undefined HTTP method")
+
+ request = self.__create_request_by_post_with_signature(url, builder)
+ request.json_parser = parse
+
+ return request
+
+ def request_process(self, method, url, params, parse):
+ if self.__performance_test is not None and self.__performance_test is True:
+ return self.request_process_performance(method, url, params, parse)
+ else:
+ return self.request_process_product(method, url, params, parse)
+
+ def request_process_product(self, method, url, params, parse):
+ request = self.create_request(method, url, params, parse)
+ if request:
+ return call_sync(request)
+
+ return None
+
+ def request_process_performance(self, method, url, params, parse):
+ request = self.create_request(method, url, params, parse)
+ if request:
+ return call_sync_perforence_test(request)
+
+ return None, 0, 0
+
+ """
+ for post batch operation, such as batch create orders[ /v1/order/batch-orders ]
+ """
+ def request_process_post_batch(self, method, url, params, parse):
+ if self.__performance_test is not None and self.__performance_test is True:
+ return self.request_process_post_batch_performance(method, url, params, parse)
+ else:
+ return self.request_process_post_batch_product(method, url, params, parse)
+
+ def request_process_post_batch_product(self, method, url, params, parse):
+ request = self.create_request_post_batch(method, url, params, parse)
+ if request:
+ return call_sync(request)
+
+ return None
+
+ def request_process_post_batch_performance(self, method, url, params, parse):
+ request = self.create_request_post_batch(method, url, params, parse)
+ if request:
+ return call_sync_perforence_test(request)
+
+ return None, 0, 0
+
diff --git a/huobi/connection/subscribe_client.py b/huobi/connection/subscribe_client.py
new file mode 100644
index 0000000..11a6d16
--- /dev/null
+++ b/huobi/connection/subscribe_client.py
@@ -0,0 +1,82 @@
+import logging
+
+from huobi.connection.impl.websocket_watchdog import WebSocketWatchDog
+from huobi.connection.impl.websocket_manage import WebsocketManage
+from huobi.connection.impl.websocket_request import WebsocketRequest
+from huobi.constant.system import WebSocketDefine, ApiVersion
+
+
+class SubscribeClient(object):
+ # static property
+ subscribe_watch_dog = WebSocketWatchDog()
+
+ def __init__(self, **kwargs):
+ """
+ Create the subscription client to subscribe the update from server.
+
+ :param kwargs: The option of subscription connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: Set the URI for subscription.
+ init_log: to init logger
+ """
+ self.__api_key = kwargs.get("api_key", None)
+ self.__secret_key = kwargs.get("secret_key", None)
+ self.__uri = kwargs.get("url", WebSocketDefine.Uri)
+ self.__init_log = kwargs.get("init_log", None)
+ if self.__init_log and self.__init_log:
+ logger = logging.getLogger("huobi-client")
+ logger.setLevel(level=logging.INFO)
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
+ logger.addHandler(handler)
+
+ self.__websocket_manage_list = list()
+
+ def __create_websocket_manage(self, request):
+ manager = WebsocketManage(self.__api_key, self.__secret_key, self.__uri, request)
+ self.__websocket_manage_list.append(manager)
+ manager.connect()
+ SubscribeClient.subscribe_watch_dog.on_connection_created(manager)
+
+ def create_request(self, subscription_handler, parse, callback, error_handler, is_trade, is_mbp_feed=False):
+ request = WebsocketRequest()
+ request.subscription_handler = subscription_handler
+ request.is_trading = is_trade
+ request.is_mbp_feed = is_mbp_feed
+ request.auto_close = False # subscribe need connection. websocket request need close request.
+ request.json_parser = parse
+ request.update_callback = callback
+ request.error_handler = error_handler
+ return request
+
+ def create_request_v1(self, subscription_handler, parse, callback, error_handler, is_trade=False):
+ request = self.create_request(subscription_handler=subscription_handler, parse=parse, callback=callback,
+ error_handler=error_handler, is_trade=is_trade)
+ request.api_version = ApiVersion.VERSION_V1
+ return request
+
+ def create_request_v2(self, subscription_handler, parse, callback, error_handler, is_trade=False):
+ request = self.create_request(subscription_handler=subscription_handler, parse=parse, callback=callback,
+ error_handler=error_handler, is_trade=is_trade)
+ request.api_version = ApiVersion.VERSION_V2
+ return request
+
+ def execute_subscribe_v1(self, subscription_handler, parse, callback, error_handler, is_trade=False):
+ request = self.create_request_v1(subscription_handler, parse, callback, error_handler, is_trade)
+ self.__create_websocket_manage(request)
+
+ def execute_subscribe_v2(self, subscription_handler, parse, callback, error_handler, is_trade=False):
+ request = self.create_request_v2(subscription_handler, parse, callback, error_handler, is_trade)
+ self.__create_websocket_manage(request)
+
+ def execute_subscribe_mbp(self, subscription_handler, parse, callback, error_handler, is_trade=False,
+ is_mbp_feed=True):
+ request = self.create_request(subscription_handler, parse, callback, error_handler, is_trade, is_mbp_feed)
+ self.__create_websocket_manage(request)
+
+ def unsubscribe_all(self):
+ for websocket_manage in self.__websocket_manage_list:
+ SubscribeClient.subscribe_watch_dog.on_connection_closed(websocket_manage)
+ websocket_manage.close()
+ self.__websocket_manage_list.clear()
diff --git a/huobi/connection/websocket_req_client.py b/huobi/connection/websocket_req_client.py
new file mode 100644
index 0000000..5e0c07a
--- /dev/null
+++ b/huobi/connection/websocket_req_client.py
@@ -0,0 +1,54 @@
+import logging
+
+from huobi.connection.impl.websocket_manage import WebsocketManage
+from huobi.connection.impl.websocket_request import WebsocketRequest
+from huobi.constant.system import WebSocketDefine
+
+
+class WebSocketReqClient(object):
+
+ def __init__(self, **kwargs):
+ """
+ Create the subscription client to subscribe the update from server.
+
+ :param kwargs: The option of subscription connection.
+ api_key: The public key applied from Huobi.
+ secret_key: The private key applied from Huobi.
+ url: Set the URI for subscription.
+ init_log: to init logger
+ """
+
+ self.__api_key = kwargs.get("api_key", None)
+ self.__secret_key = kwargs.get("secret_key", None)
+ self.__uri = kwargs.get("url", WebSocketDefine.Uri)
+ self.__init_log = kwargs.get("init_log", None)
+ if self.__init_log and self.__init_log:
+ logger = logging.getLogger("huobi-client")
+ logger.setLevel(level=logging.INFO)
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
+ logger.addHandler(handler)
+
+ def __create_websocket_manage(self, request):
+ manager = WebsocketManage(self.__api_key, self.__secret_key, self.__uri, request)
+ manager.connect()
+
+ def create_request(self, subscription_handler, parse, callback, error_handler, is_trade=False, is_mbp_feed=False):
+ request = WebsocketRequest()
+ request.subscription_handler = subscription_handler
+ request.is_trading = is_trade
+ request.is_mbp_feed = is_mbp_feed
+ request.auto_close = True # for websocket request, auto close the connection after request.
+ request.json_parser = parse
+ request.update_callback = callback
+ request.error_handler = error_handler
+ return request
+
+ def execute_subscribe_v1(self, subscription_handler, parse, callback, error_handler, is_trade=False):
+ request = self.create_request(subscription_handler, parse, callback, error_handler, is_trade)
+ self.__create_websocket_manage(request)
+
+ def execute_subscribe_mbp(self, subscription_handler, parse, callback, error_handler, is_trade=False,
+ is_mbp_feed=True):
+ request = self.create_request(subscription_handler, parse, callback, error_handler, is_trade, is_mbp_feed)
+ self.__create_websocket_manage(request)
diff --git a/huobi/constant/__init__.py b/huobi/constant/__init__.py
new file mode 100644
index 0000000..905e73b
--- /dev/null
+++ b/huobi/constant/__init__.py
@@ -0,0 +1,7 @@
+from huobi.constant.definition import *
+from huobi.constant.result import *
+from huobi.constant.system import *
+from huobi.constant.test import *
+
+
+
diff --git a/huobi/constant/definition.py b/huobi/constant/definition.py
new file mode 100644
index 0000000..3fdafaa
--- /dev/null
+++ b/huobi/constant/definition.py
@@ -0,0 +1,366 @@
+class CandlestickInterval:
+ MIN1 = "1min"
+ MIN5 = "5min"
+ MIN15 = "15min"
+ MIN30 = "30min"
+ MIN60 = "60min"
+ HOUR4 = "4hour"
+ DAY1 = "1day"
+ MON1 = "1mon"
+ WEEK1 = "1week"
+ YEAR1 = "1year"
+ INVALID = None
+
+
+class OrderSide:
+ BUY = "buy"
+ SELL = "sell"
+ INVALID = None
+
+
+class TradeDirection:
+ BUY = "buy"
+ SELL = "sell"
+ INVALID = None
+
+
+class OrderType:
+ SELL_LIMIT = "sell-limit"
+ BUY_LIMIT = "buy-limit"
+ BUY_MARKET = "buy-market"
+ SELL_MARKET = "sell-market"
+ BUY_IOC = "buy-ioc"
+ SELL_IOC = "sell-ioc"
+ BUY_LIMIT_MAKER = "buy-limit-maker"
+ SELL_LIMIT_MAKER = "sell-limit-maker"
+ BUY_STOP_LIMIT = "buy-stop-limit"
+ SELL_STOP_LIMIT = "sell-stop-limit"
+ BUY_LIMIT_FOK = "buy-limit-fok"
+ SELL_LIMIT_FOK = "sell-limit-fok"
+ BUY_STOP_LIMIT_FOK = "buy-stop-limit-fok"
+ SELL_STOP_LIMIT_FOK = "sell-stop-limit-fok"
+ INVALID = None
+
+
+class AlgoOrderType:
+ LIMIT = "limit"
+ MARKET = "market"
+
+
+class AlgoOrderStatus:
+ CANCELED = "canceled"
+ REJECTED = "rejected"
+ TRIGGERED = "triggered"
+
+
+class AccountType:
+ SPOT = "spot"
+ MARGIN = "margin"
+ OTC = "otc"
+ POINT = "point"
+ MINEPOLL = "minepool"
+ ETF = "etf"
+ AGENCY = "agency"
+ SUPER_MARGIN = "super-margin"
+ INVALID = None
+
+
+class AccountState:
+ WORKING = "working"
+ LOCK = "lock"
+ INVALID = None
+
+
+class AccountPointState:
+ WORKING = "working"
+ LOCK = "lock"
+ INVALID = None
+ FL_SYS = "fl-sys"
+ FL_MGT = "fl-mgt"
+ FL_END = "fl-end"
+ FL_NEGATIVE = "fl-negative"
+
+
+class AccountBalanceUpdateType:
+ TRADE = "trade"
+ FROZEN = "frozen"
+ LOAN = "loan"
+ INTEREST = "interest"
+ LOAN_AVAILABLE = "loan-available"
+ TRANSFER_OUT_AVAILABLE = "transfer-out-available"
+ INVALID = None
+
+
+class WithdrawState:
+ SUBMITTED = "submitted"
+ REEXAMINE = "reexamine"
+ CANCELED = "canceled"
+ PASS = "pass"
+ REJECT = "reject"
+ PRETRANSFER = "pre-transfer"
+ WALLETTRANSFER = "wallet-transfer"
+ WALEETREJECT = "wallet-reject"
+ CONFIRMED = "confirmed"
+ CONFIRMERROR = "confirm-error"
+ REPEALED = "repealed"
+ VERIFYING = "verifying"
+ FAILED = "failed"
+ INVALID = None
+
+
+class DepositWithdraw:
+ DEPOSIT = "deposit"
+ WITHDRAW = "withdraw"
+
+
+class DepositState:
+ CONFIRMING = "confirming"
+ SAFE = "safe"
+ CONFIRMED = "confirmed"
+ ORPHAN = "orphan"
+ INVALID = None
+
+
+class LoanOrderState:
+ CREATED = "created"
+ ACCRUAL = "accrual"
+ CLEARED = "cleared"
+ FAILED = "failed"
+ INVALID = None
+
+
+class OrderSource:
+ SYS = "sys"
+ WEB = "web"
+ API = "api"
+ APP = "app"
+ FL_SYS = "fl-sys"
+ FL_MGT = "fl-mgt"
+ SPOT_WEB = "spot-web"
+ SPOT_API = "spot-api"
+ SPOT_APP = "spot-app"
+ MARGIN_API = "margin-api"
+ MARGIN_WEB = "margin-web"
+ MARGIN_APP = "margin-app"
+ SUPER_MARGIN_API = "super-margin-api"
+ SUPER_MARGIN_WEB = "super-margin-web"
+ SUPER_MARGIN_APP = "super-margin-app"
+ SUPER_MARGIN_FL_SYS = "super-margin-fl-sys"
+ SUPER_MARGIN_FL_MGT = "super-margin-fl-mgt"
+ INVALID = None
+
+
+class OrderState:
+ CREATED = "created" # for stop loss order
+ PRE_SUBMITTED = "pre-submitted"
+ SUBMITTING = "submitting"
+ SUBMITTED = "submitted"
+ PARTIAL_FILLED = "partial-filled"
+ CANCELLING = "cancelling"
+ PARTIAL_CANCELED = "partial-canceled"
+ FILLED = "filled"
+ CANCELED = "canceled"
+ FAILED = "failed"
+ PLACE_TIMEOUT = "place_timeout"
+ INVALID = None
+
+
+class TimeInForceType:
+ IOC = "ioc"
+ FOK = "fok"
+ BOC = "boc"
+ GTC = "gtc"
+
+
+class TransferMasterType:
+ IN = "master-transfer-in"
+ OUT = "master-transfer-out"
+ POINT_IN = "master-point-transfer-in"
+ POINT_OUT = "master-point-transfer-out"
+ INVALID = None
+
+
+class EtfStatus:
+ NORMAL = "1"
+ REBALANCING_START = "2"
+ CREATION_AND_REDEMPTION_SUSPEND = "3"
+ CREATION_SUSPEND = "4"
+ REDEMPTION_SUSPEND = "5"
+ INVALID = None
+
+
+class EtfSwapType:
+ IN = "1"
+ OUT = "2"
+ INVALID = None
+
+
+class AccountChangeType:
+ NEWORDER = "order.place"
+ TRADE = "order.match"
+ REFUND = "order.refund"
+ CANCELORDER = "order.cancel"
+ FEE = "order.fee-refund"
+ TRANSFER = "margin.transfer"
+ LOAN = "margin.loan"
+ INTEREST = "margin.interest"
+ REPAY = "margin.repay"
+ OTHER = "other"
+ INVALID = None
+
+
+class BalanceMode:
+ AVAILABLE = "0"
+ TOTAL = "1"
+ INVALID = None
+
+
+class AccountBalanceMode:
+ BALANCE = "0"
+ TOTAL = "1"
+ INVALID = None
+
+
+class OperateMode:
+ PING = "ping"
+ PONG = "pong"
+ INVALID = None
+
+
+class QueryDirection:
+ PREV = "prev"
+ NEXT = "next"
+ INVALID = None
+
+
+class TransferFuturesPro:
+ TO_PRO = "futures-to-pro"
+ TO_FUTURES = "pro-to-futures"
+
+
+class MatchRole:
+ MAKER = "maker"
+ TAKER = "taker"
+
+
+class DepthStep:
+ STEP0 = "step0"
+ STEP1 = "step1"
+ STEP2 = "step2"
+ STEP3 = "step3"
+ STEP4 = "step4"
+ STEP5 = "step5"
+
+
+class DepthSize:
+ SIZE5 = 5
+ SIZE10 = 10
+ SIZE20 = 20
+
+
+class MbpLevel:
+ MBP5 = 5
+ MBP10 = 10
+ MBP20 = 20
+ MBP150 = 150
+
+
+class ChainDepositStatus:
+ ALLOWED = "allowed"
+ PROHIBITED = "prohibited"
+ INVALID = None
+
+
+class ChainWithdrawStatus:
+ ALLOWED = "allowed"
+ PROHIBITED = "prohibited"
+ INVALID = None
+
+
+class InstrumentStatus:
+ NORMAL = "normal"
+ DELISTED = "delisted"
+ INVALID = None
+
+
+class AccountChangeType:
+ ORDER_PLACE = "order-place"
+ ORDER_MATCH = "order-match"
+ ORDER_REFUND = "order-refund"
+ ORDER_CANCEL = "order-cancel"
+ ORDER_FEE_REFUND = "order-fee-refund"
+ MARGIN_TRANSFER = "margin-transfer"
+ MARGIN_LOAN = "margin-loan"
+ MARGIN_INTEREST = "margin-interest"
+ MARGIN_REPAY = "margin-repay"
+ OTHER = "other"
+ DEPOSIT = "deposit"
+ WITHDRAW = "withdraw"
+ INVALID = None
+
+
+class FeeDeductType:
+ DEDUCT_BY_HT = "ht"
+ DEDUCT_BY_POINT = "point"
+ INVALID = None
+
+
+class SubUidAction:
+ UNLOCK = "unlock"
+ LOCK = "lock"
+ INVALID = None
+
+
+class SubUidState:
+ NORMAL = "normal"
+ LOCK = "lock"
+ INVALID = None
+
+
+class OrderUpdateEventType:
+ CREATION = "creation"
+ TRADE = "trade"
+ CANCELLATION = "cancellation"
+ INVALID = None
+
+
+class AccountTransactType:
+ TRADE = "trade"
+ ETF = "etf"
+ TRANSACT_FEE = "transact-fee"
+ DEDUCTION = "deduction"
+ TRANSFER = "transfer"
+ CREDIT = "credit"
+ LIQUIDATION = "liquidation"
+ INTEREST = "interest"
+ DEPOSIT = "deposit"
+ WITHDRAW = "withdraw"
+ WITHDRAW_FEE = "withdraw-fee"
+ EXCHANGE = "exchange"
+ OTHER = "other-types"
+
+
+class SortDesc:
+ ASC = "asc"
+ DESC = "desc"
+
+
+class SubuserTradePrivilegeType:
+ MARGIN = "isolated-margin"
+ SUPER_MARGIN = "cross-margin"
+
+
+class SubUserTradeStatus:
+ ACTIVATED = "activated"
+ DEACTIVATED = "deactivated"
+
+
+class MarketStatus:
+ NORMAL = 1
+ HALTED = 2
+ CANCEL_ONLY = 3
+
+
+class HaltReason:
+ EMERGENCY_MAINTENANCE = 2
+ SCHEDULED_MAINTENANCE = 3
diff --git a/huobi/constant/result.py b/huobi/constant/result.py
new file mode 100644
index 0000000..0026777
--- /dev/null
+++ b/huobi/constant/result.py
@@ -0,0 +1,11 @@
+
+class OutputKey:
+ KeyData = "data"
+ KeyTick = "tick"
+ KeyChannelCh = "ch"
+ KeyChannelRep = "rep"
+
+
+
+
+
diff --git a/huobi/constant/system.py b/huobi/constant/system.py
new file mode 100644
index 0000000..f459404
--- /dev/null
+++ b/huobi/constant/system.py
@@ -0,0 +1,32 @@
+
+HUOBI_URL_PRO = "https://api.huobi.pro"
+HUOBI_URL_VN = "https://api.huobi.vn"
+HUOBI_URL_SO = "https://api.huobi.so"
+
+
+HUOBI_WEBSOCKET_URI_PRO = "wss://api.huobi.pro"
+HUOBI_WEBSOCKET_URI_VN = "wss://api.huobi.vn"
+HUOBI_WEBSOCKET_URI_SO = "wss://api.huobi.so"
+
+class WebSocketDefine:
+ Uri = HUOBI_WEBSOCKET_URI_PRO
+
+class RestApiDefine:
+ Url = HUOBI_URL_PRO
+
+class HttpMethod:
+ GET = "GET"
+ GET_SIGN = "GET_SIGN"
+ POST = "POST"
+ POST_SIGN = "POST_SIGN"
+
+
+class ApiVersion:
+ VERSION_V1 = "v1"
+ VERSION_V2 = "v2"
+
+def get_default_server_url(user_configed_url):
+ if user_configed_url and len(user_configed_url):
+ return user_configed_url
+ else:
+ return RestApiDefine.Url
diff --git a/huobi/constant/test.py b/huobi/constant/test.py
new file mode 100644
index 0000000..4dad710
--- /dev/null
+++ b/huobi/constant/test.py
@@ -0,0 +1,10 @@
+g_api_key="xxxxxx"
+g_secret_key="xxxxxx"
+
+
+
+
+g_sub_uid = 123456
+
+g_account_id = 123456
+
diff --git a/huobi/exception/__init__.py b/huobi/exception/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/exception/huobi_api_exception.py b/huobi/exception/huobi_api_exception.py
new file mode 100644
index 0000000..5f18e28
--- /dev/null
+++ b/huobi/exception/huobi_api_exception.py
@@ -0,0 +1,14 @@
+
+class HuobiApiException(Exception):
+
+ RUNTIME_ERROR = "RuntimeError"
+ INPUT_ERROR = "InputError"
+ KEY_MISSING = "KeyMissing"
+ SYS_ERROR = "SysError"
+ SUBSCRIPTION_ERROR = "SubscriptionError"
+ ENV_ERROR = "EnvironmentError"
+ EXEC_ERROR = "ExecuteError"
+
+ def __init__(self, error_code, error_message):
+ self.error_code = error_code
+ self.error_message = error_message
diff --git a/huobi/model/__init__.py b/huobi/model/__init__.py
new file mode 100644
index 0000000..fd40910
--- /dev/null
+++ b/huobi/model/__init__.py
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/huobi/model/account/__init__.py b/huobi/model/account/__init__.py
new file mode 100644
index 0000000..78d8613
--- /dev/null
+++ b/huobi/model/account/__init__.py
@@ -0,0 +1,14 @@
+from huobi.model.account.account import Account
+from huobi.model.account.account_balance import AccountBalance
+from huobi.model.account.account_balance_req import AccountBalanceReq
+from huobi.model.account.account_update import AccountUpdate
+from huobi.model.account.account_update_event import AccountUpdateEvent
+from huobi.model.account.balance import Balance
+from huobi.model.account.complete_subaccount import CompleteSubAccount
+from huobi.model.account.margin_balance_detail import MarginBalanceDetail
+from huobi.model.account.account_history import AccountHistory
+from huobi.model.account.sub_uid_management import SubUidManagement
+from huobi.model.account.account_ledger import AccountLedger
+from huobi.model.account.account_transfer_result import AccountTransferResult
+from huobi.model.account.account_point_result import AccountPointResult
+from huobi.model.account.account_point_transfer_result import AccountPointTransferResult
diff --git a/huobi/model/account/account.py b/huobi/model/account/account.py
new file mode 100644
index 0000000..d1e7535
--- /dev/null
+++ b/huobi/model/account/account.py
@@ -0,0 +1,27 @@
+from huobi.constant import *
+
+
+class Account:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ id: The unique account id.
+ account_type: The type of this account, possible value: spot, margin, otc, point.
+ account_state: The account state, possible value: working, lock.
+ balances: The balance list of the specified currency. The content is Balance class
+
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.type = AccountType.INVALID
+ self.state = AccountState.INVALID
+ self.subtype = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.type, format_data + "Account Type")
+ PrintBasic.print_basic(self.state, format_data + "Account State")
+ PrintBasic.print_basic(self.subtype, format_data + "Subtype")
diff --git a/huobi/model/account/account_asset_valuation.py b/huobi/model/account/account_asset_valuation.py
new file mode 100644
index 0000000..c0b78ee
--- /dev/null
+++ b/huobi/model/account/account_asset_valuation.py
@@ -0,0 +1,18 @@
+class AccountAssetValuationResult:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ balance: balance valuation bases on given valuation currency.
+ timestamp: unix timestamp from server.
+
+ """
+
+ def __init__(self):
+ self.balance = ""
+ self.timestamp = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.balance, format_data + "balance")
+ PrintBasic.print_basic(self.timestamp, format_data + "timestamp")
diff --git a/huobi/model/account/account_balance.py b/huobi/model/account/account_balance.py
new file mode 100644
index 0000000..66cbc48
--- /dev/null
+++ b/huobi/model/account/account_balance.py
@@ -0,0 +1,58 @@
+from huobi.constant import *
+from huobi.model.account.balance import Balance
+from huobi.utils import default_parse, default_parse_list_dict
+
+
+class AccountBalance:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ id: The unique account id.
+ account_type: The type of this account, possible value: spot, margin, otc, point.
+ account_state: The account state, possible value: working, lock.
+ list: The balance list of the specified currency. The content is Balance class
+
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.type = AccountType.INVALID
+ self.state = AccountState.INVALID
+ self.subtype = ""
+ self.list = list()
+
+ @staticmethod
+ def json_parse(data_dict):
+ if data_dict and len(data_dict):
+ balance_list = data_dict.get("list")
+ data_dict.pop("list")
+ account_balance_obj = default_parse(data_dict, AccountBalance, Balance)
+ account_balance_obj.subtype = data_dict.get("subtype", data_dict.get("symbol"))
+ account_balance_obj.list = default_parse_list_dict(balance_list, Balance, [])
+ return account_balance_obj
+
+ return None
+
+ @staticmethod
+ def json_parse_list(data_list):
+ account_balance_list = []
+ if data_list and len(data_list):
+ for item in data_list:
+ item_obj = AccountBalance.json_parse(item)
+ if item_obj:
+ account_balance_list.append(item_obj)
+ return account_balance_list
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "Account ID")
+ PrintBasic.print_basic(self.type, format_data + "Account Type")
+ PrintBasic.print_basic(self.state, format_data + "Account State")
+ PrintBasic.print_basic(self.subtype, format_data + "Subtype")
+
+ print()
+ if len(self.list):
+ for row in self.list:
+ row.print_object(format_data+"\t")
+ print()
diff --git a/huobi/model/account/account_balance_req.py b/huobi/model/account/account_balance_req.py
new file mode 100644
index 0000000..9539db2
--- /dev/null
+++ b/huobi/model/account/account_balance_req.py
@@ -0,0 +1,30 @@
+
+class AccountBalanceReq:
+ """
+ The account change information received by subscription of account.
+
+ :member
+ ts: The UNIX formatted timestamp generated by server in UTC.
+ cid: client request ID
+ topic: request Channel or Topic
+ data: The list of account and balance
+
+ """
+
+ def __init__(self):
+ self.ts = 0
+ self.cid = ""
+ self.topic = ""
+ self.data = list()
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ts, format_data + "Timestamp")
+ PrintBasic.print_basic(self.cid, format_data + "Client Order ID")
+ PrintBasic.print_basic(self.topic, format_data + "Topic")
+ print()
+ if len(self.data):
+ for account_balance in self.data:
+ account_balance.print_object()
+ print()
\ No newline at end of file
diff --git a/huobi/model/account/account_history.py b/huobi/model/account/account_history.py
new file mode 100644
index 0000000..3719483
--- /dev/null
+++ b/huobi/model/account/account_history.py
@@ -0,0 +1,36 @@
+class AccountHistory:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ account_id: Account Id.
+ currency: Currency name
+ transact_amt: Amount change (positive value if income, negative value if outcome)
+ transact-type: Amount change type
+ avail_balance: Available balance
+ acct_balance: Account balance
+ transact_time: Transaction time (database time)
+ record_id: Unique record ID in the database
+
+ """
+
+ def __init__(self):
+ self.account_id = 0
+ self.currency = ""
+ self.transact_amt = ""
+ self.transact_type = ""
+ self.avail_balance = ""
+ self.acct_balance = ""
+ self.transact_time = 0
+ self.record_id = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.account_id, format_data + "Account Id")
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.transact_amt, format_data + "Transact Amount")
+ PrintBasic.print_basic(self.transact_type, format_data + "Transact Type")
+ PrintBasic.print_basic(self.avail_balance, format_data + "Avail Balance")
+ PrintBasic.print_basic(self.acct_balance, format_data + "Account Balance")
+ PrintBasic.print_basic(self.transact_time, format_data + "Transact Time")
+ PrintBasic.print_basic(self.record_id, format_data + "Record Id")
diff --git a/huobi/model/account/account_ledger.py b/huobi/model/account/account_ledger.py
new file mode 100644
index 0000000..bf08d4a
--- /dev/null
+++ b/huobi/model/account/account_ledger.py
@@ -0,0 +1,38 @@
+class AccountLedger:
+ """
+ The account ledger information.
+
+ :member
+ accountId: Account ID.
+ currency: Cryptocurrency.
+ transactAmt: Transaction amount (income positive, expenditure negative).
+ transactType: Transaction type.
+ transferType: Transfer type (only valid for transactType=transfer).
+ transactId: Transaction ID.
+ transactTime: Transaction time.
+ transferer: Transferer’s account ID.
+ transferee: Transferee’s account ID.
+ """
+
+ def __init__(self):
+ self.accountId = 0
+ self.currency = ""
+ self.transactAmt = 0.0
+ self.transactType = ""
+ self.transferType = ""
+ self.transactId = 0
+ self.transactTime = 0
+ self.transferer = 0
+ self.transferee = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.accountId, format_data + "Account ID")
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.transactAmt, format_data + "Transaction Amount")
+ PrintBasic.print_basic(self.transactType, format_data + "Transaction Type")
+ PrintBasic.print_basic(self.transferType, format_data + "Transfer Type")
+ PrintBasic.print_basic(self.transactId, format_data + "Transaction ID")
+ PrintBasic.print_basic(self.transactTime, format_data + "Transaction Time")
+ PrintBasic.print_basic(self.transferer, format_data + "Transferer’s Account ID")
+ PrintBasic.print_basic(self.transferee, format_data + "Transferee’s Account ID")
\ No newline at end of file
diff --git a/huobi/model/account/account_point_group.py b/huobi/model/account/account_point_group.py
new file mode 100644
index 0000000..2a5223f
--- /dev/null
+++ b/huobi/model/account/account_point_group.py
@@ -0,0 +1,25 @@
+from huobi.constant import *
+
+
+class AccountPointGroup:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ id: The unique account id.
+ account_type: The type of this account, possible value: spot, margin, otc, point.
+ account_state: The account state, possible value: working, lock.
+ list: The balance list of the specified currency. The content is Balance class
+
+ """
+
+ def __init__(self):
+ self.groupId = ""
+ self.expiryDate = ""
+ self.remainAmt = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.groupId, format_data + "Group Id")
+ PrintBasic.print_basic(self.expiryDate, format_data + "Expiration date")
+ PrintBasic.print_basic(self.remainAmt, format_data + "Remain Amount")
diff --git a/huobi/model/account/account_point_result.py b/huobi/model/account/account_point_result.py
new file mode 100644
index 0000000..4a28b1a
--- /dev/null
+++ b/huobi/model/account/account_point_result.py
@@ -0,0 +1,46 @@
+from huobi.constant import *
+from huobi.model.account.account_point_group import AccountPointGroup
+from huobi.utils import default_parse, default_parse_list_dict
+
+
+class AccountPointResult:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ id: The unique account id.
+ account_type: The type of this account, possible value: spot, margin, otc, point.
+ account_state: The account state, possible value: working, lock.
+ list: The balance list of the specified currency. The content is Balance class
+
+ """
+
+ def __init__(self):
+ self.accountId = ""
+ self.accountStatus = AccountPointState.INVALID
+ self.groupIds = list()
+ self.acctBalance = ""
+
+ @staticmethod
+ def json_parse(data_dict):
+ if data_dict and len(data_dict):
+ group_ids = data_dict.get("groupIds")
+ data_dict.pop("groupIds")
+ account_point_obj = default_parse(data_dict, AccountPointResult, AccountPointGroup)
+ account_point_obj.subtype = data_dict.get("subtype", data_dict.get("symbol"))
+ account_point_obj.list = default_parse_list_dict(group_ids, AccountPointGroup, [])
+ return account_point_obj
+
+ return None
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.accountId, format_data + "Account ID")
+ PrintBasic.print_basic(self.accountStatus, format_data + "Account Status")
+ PrintBasic.print_basic(self.acctBalance, format_data + "Account Balance")
+
+ print()
+ if len(self.groupIds):
+ for row in self.groupIds:
+ row.print_object(format_data + "\t")
+ print()
diff --git a/huobi/model/account/account_point_transfer_result.py b/huobi/model/account/account_point_transfer_result.py
new file mode 100644
index 0000000..bc9c72f
--- /dev/null
+++ b/huobi/model/account/account_point_transfer_result.py
@@ -0,0 +1,20 @@
+class AccountPointTransferResult:
+ """
+ The account change information received by subscription of account.
+
+ :member
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ change_type: The event that asset change notification related.
+ account_change_list: The list of account change, the content is AccountChange class
+
+ """
+
+ def __init__(self):
+ self.transactId = ""
+ self.transactTime = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.transactId, format_data + "transactId")
+ PrintBasic.print_basic(self.transactTime, format_data + "transactTime")
+ self.data.print_object()
diff --git a/huobi/model/account/account_transfer_result.py b/huobi/model/account/account_transfer_result.py
new file mode 100644
index 0000000..9c5ae59
--- /dev/null
+++ b/huobi/model/account/account_transfer_result.py
@@ -0,0 +1,20 @@
+class AccountTransferResult:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ id: The unique account id.
+ account_type: The type of this account, possible value: spot, margin, otc, point.
+ account_state: The account state, possible value: working, lock.
+ balances: The balance list of the specified currency. The content is Balance class
+
+ """
+
+ def __init__(self):
+ self.transact_id = 0
+ self.transact_time = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.transact_id, format_data + "TransactionId")
+ PrintBasic.print_basic(self.transact_time, format_data + "TransactionTime")
diff --git a/huobi/model/account/account_update.py b/huobi/model/account/account_update.py
new file mode 100644
index 0000000..ca3ad1d
--- /dev/null
+++ b/huobi/model/account/account_update.py
@@ -0,0 +1,35 @@
+from huobi.constant import *
+
+
+class AccountUpdate:
+ """
+ The account change information received by subscription of account.
+
+ :member
+ currency: The currency of the change.
+ accountId: The account id.
+ balance: Account balance (only exists when account balance changed)
+ available: Available balance (only exists when available balance changed)
+ changeType: Change type see AccountChangeType, valid value: order-place,order-match,order-refund,order-cancel,order-fee-refund,margin-transfer,margin-loan,margin-interest,margin-repay,other,
+ accountType: Account type see AccountBalanceUpdateType, valid value: trade, frozen, loan, interest
+ changeTime: Change time, unix time in millisecond
+ """
+
+ def __init__(self):
+ self.currency = ""
+ self.accountId = 0
+ self.balance = ""
+ self.available = ""
+ self.changeType = AccountChangeType.INVALID
+ self.accountType = AccountBalanceUpdateType.INVALID
+ self.changeTime = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.accountId, format_data + "Account ID")
+ PrintBasic.print_basic(self.balance, format_data + "Balance")
+ PrintBasic.print_basic(self.available, format_data + "Available")
+ PrintBasic.print_basic(self.changeType, format_data + "Account Change Type")
+ PrintBasic.print_basic(self.accountType, format_data + "Account Balance Change Type")
+ PrintBasic.print_basic(self.changeTime, format_data + "Account Timestamp")
diff --git a/huobi/model/account/account_update_event.py b/huobi/model/account/account_update_event.py
new file mode 100644
index 0000000..4400983
--- /dev/null
+++ b/huobi/model/account/account_update_event.py
@@ -0,0 +1,23 @@
+from huobi.constant import *
+from huobi.model.account import AccountUpdate
+
+
+class AccountUpdateEvent:
+ """
+ The account change information received by subscription of account.
+
+ :member
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ change_type: The event that asset change notification related.
+ account_change_list: The list of account change, the content is AccountChange class
+
+ """
+
+ def __init__(self):
+ self.ch = 0
+ self.data = AccountUpdate()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Topic")
+ self.data.print_object()
\ No newline at end of file
diff --git a/huobi/model/account/balance.py b/huobi/model/account/balance.py
new file mode 100644
index 0000000..01621e7
--- /dev/null
+++ b/huobi/model/account/balance.py
@@ -0,0 +1,25 @@
+from huobi.constant import *
+from huobi.utils import default_parse_list_dict
+
+
+class Balance:
+ """
+ The balance of specified account.
+
+ :member
+ currency: The currency of this balance.
+ balance_type: The balance type, trade or frozen.
+ balance: The balance in the main currency unit.
+
+ """
+
+ def __init__(self):
+ self.currency = ""
+ self.type = AccountBalanceUpdateType.INVALID
+ self.balance = 0.0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.type, format_data + "Balance Type")
+ PrintBasic.print_basic(self.balance, format_data + "Balance")
\ No newline at end of file
diff --git a/huobi/model/account/complete_subaccount.py b/huobi/model/account/complete_subaccount.py
new file mode 100644
index 0000000..a49a163
--- /dev/null
+++ b/huobi/model/account/complete_subaccount.py
@@ -0,0 +1,27 @@
+
+from huobi.constant import *
+
+
+class CompleteSubAccount:
+ """
+ Sub-account completed info
+
+ :member
+ id: The sub-id.
+ account_type: The sub account type.
+ balances: The balance list, the content is Balance class.
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.account_type = AccountType.INVALID
+ self.balances = list()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.account_type, format_data + "Account Type")
+ if len(self.balances):
+ for row in self.balances:
+ row.print_object()
+ print()
\ No newline at end of file
diff --git a/huobi/model/account/margin_balance_detail.py b/huobi/model/account/margin_balance_detail.py
new file mode 100644
index 0000000..6e7da7a
--- /dev/null
+++ b/huobi/model/account/margin_balance_detail.py
@@ -0,0 +1,24 @@
+
+from huobi.constant import *
+
+
+class MarginBalanceDetail:
+ def __init__(self):
+ self.id = 0
+ self.symbol = 0
+ self.state = AccountState.INVALID
+ self.type = AccountType.INVALID
+ self.risk_rate = 0.0
+ self.fl_price = 0.0
+ self.fl_type = ""
+ self.sub_account_balance_list = list()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.type, format_data + "Account Type")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.state, format_data + "Account State")
+ PrintBasic.print_basic(self.fl_price, format_data + "Burst Price")
+ PrintBasic.print_basic(self.fl_type, format_data + "Burst Type")
+ PrintBasic.print_basic(self.risk_rate, format_data + "Risk Rate")
\ No newline at end of file
diff --git a/huobi/model/account/sub_uid_management.py b/huobi/model/account/sub_uid_management.py
new file mode 100644
index 0000000..9d3266e
--- /dev/null
+++ b/huobi/model/account/sub_uid_management.py
@@ -0,0 +1,20 @@
+from huobi.constant import SubUidState
+
+
+class SubUidManagement:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ subUid: sub user ID.
+ userState: sub user account state, states see SubUidState.
+ """
+
+ def __init__(self):
+ self.subUid = 0
+ self.userState = SubUidState.INVALID
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.subUid, format_data + "subUid")
+ PrintBasic.print_basic(self.userState, format_data + "userState")
diff --git a/huobi/model/algo/__init__.py b/huobi/model/algo/__init__.py
new file mode 100644
index 0000000..a7279a2
--- /dev/null
+++ b/huobi/model/algo/__init__.py
@@ -0,0 +1,3 @@
+from huobi.model.algo.cancel_order_result import CancelOrderResult
+from huobi.model.algo.order_list_item import OrderListItem
+from huobi.model.algo.order_history_item import OrderHistoryItem
diff --git a/huobi/model/algo/cancel_order_result.py b/huobi/model/algo/cancel_order_result.py
new file mode 100644
index 0000000..1ab3faa
--- /dev/null
+++ b/huobi/model/algo/cancel_order_result.py
@@ -0,0 +1,17 @@
+class CancelOrderResult:
+ """
+ The result of batch cancel operation.
+
+ :member
+ accepted: The clientOrderIds accepted.
+ rejected: The clientOrderIds rejected .
+
+ """
+
+ def __init__(self):
+ self.accepted = []
+ self.rejected = []
+
+ def print_object(self, format_data=""):
+ print("Success Order Counts", len(self.accepted), " accepted Order Ids : ", self.accepted)
+ print("Fail Order Counts", len(self.rejected), " Rejected Order Ids : ", self.rejected)
\ No newline at end of file
diff --git a/huobi/model/algo/order_history_item.py b/huobi/model/algo/order_history_item.py
new file mode 100644
index 0000000..f9b9020
--- /dev/null
+++ b/huobi/model/algo/order_history_item.py
@@ -0,0 +1,51 @@
+class OrderHistoryItem:
+ """
+ The result of batch cancel operation.
+
+ :member
+ orderOrigTime
+ lastActTime
+ symbol
+ source
+ orderSide
+ orderType
+ timeInForce
+ clientOrderId
+ accountId
+ orderPrice
+ orderSize
+ stopPrice
+ orderStatus
+
+ """
+
+ def __init__(self):
+ self.orderOrigTime = ""
+ self.lastActTime = ""
+ self.symbol = ""
+ self.source = ""
+ self.orderSide = ""
+ self.orderType = ""
+ self.timeInForce = ""
+ self.clientOrderId = ""
+ self.accountId = ""
+ self.orderPrice = ""
+ self.orderSize = ""
+ self.stopPrice = ""
+ self.orderStatus = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.orderOrigTime, format_data + "")
+ PrintBasic.print_basic(self.lastActTime, format_data + "")
+ PrintBasic.print_basic(self.symbol, format_data + "")
+ PrintBasic.print_basic(self.source, format_data + "")
+ PrintBasic.print_basic(self.orderSide, format_data + "")
+ PrintBasic.print_basic(self.orderType, format_data + "")
+ PrintBasic.print_basic(self.timeInForce, format_data + "")
+ PrintBasic.print_basic(self.clientOrderId, format_data + "")
+ PrintBasic.print_basic(self.accountId, format_data + "")
+ PrintBasic.print_basic(self.orderPrice, format_data + "")
+ PrintBasic.print_basic(self.orderSize, format_data + "")
+ PrintBasic.print_basic(self.stopPrice, format_data + "")
+ PrintBasic.print_basic(self.orderStatus, format_data + "")
diff --git a/huobi/model/algo/order_list_item.py b/huobi/model/algo/order_list_item.py
new file mode 100644
index 0000000..ee1e5b7
--- /dev/null
+++ b/huobi/model/algo/order_list_item.py
@@ -0,0 +1,73 @@
+class OrderListItem:
+ """
+ The result of batch cancel operation.
+
+ :member
+ event_type:
+ symbol:
+ order_id:
+ trade_price:
+ trade_volume:
+ order_side:
+ aggressor:
+ trade_id:
+ trade_time:
+ transact_fee:
+ fee_deduct:
+ fee_deduct_type:
+ fee_currency:
+ account_id:
+ source:
+ order_price:
+ order_size:
+ client_order_id:
+ order_create_time:
+ order_status:
+ """
+
+ def __init__(self):
+ self.eventType = ""
+ self.symbol = ""
+ self.orderId = ""
+ self.tradePrice = ""
+ self.tradeVolume = ""
+ self.orderSide = ""
+ self.aggressor = ""
+ self.tradeId = ""
+ self.tradeTime = ""
+ self.transactFee = ""
+ self.feeDeduct = ""
+ self.feeDeductType = ""
+ self.feeCurrency = ""
+ self.accountId = ""
+ self.source = ""
+ self.orderPrice = ""
+ self.orderSize = ""
+ self.clientOrderId = ""
+ self.orderCreateTime = ""
+ self.orderStatus = ""
+ self.trailingRate = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.eventType, format_data + "Event Type")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.orderId, format_data + "OrderId")
+ PrintBasic.print_basic(self.tradePrice, format_data + "Trade Price")
+ PrintBasic.print_basic(self.tradeVolume, format_data + "Trade Volume")
+ PrintBasic.print_basic(self.orderSide, format_data + "Order Side")
+ PrintBasic.print_basic(self.aggressor, format_data + "Aggressor")
+ PrintBasic.print_basic(self.tradeId, format_data + "TradeId")
+ PrintBasic.print_basic(self.tradeTime, format_data + "Trade Time")
+ PrintBasic.print_basic(self.transactFee, format_data + "Transact Fee")
+ PrintBasic.print_basic(self.feeDeduct, format_data + "Fee Deduct")
+ PrintBasic.print_basic(self.feeDeductType, format_data + "Fee Deduct Type")
+ PrintBasic.print_basic(self.feeCurrency, format_data + "Fee Currency")
+ PrintBasic.print_basic(self.accountId, format_data + "Account Id")
+ PrintBasic.print_basic(self.source, format_data + "Source")
+ PrintBasic.print_basic(self.orderPrice, format_data + "Order Price")
+ PrintBasic.print_basic(self.orderSize, format_data + "Order Size")
+ PrintBasic.print_basic(self.clientOrderId, format_data + "Client Order Id")
+ PrintBasic.print_basic(self.orderCreateTime, format_data + "Order Create Time")
+ PrintBasic.print_basic(self.orderStatus, format_data + "Order Status")
+ PrintBasic.print_basic(self.trailingRate, format_data + "Trailing Rate (Trailing Order Only)")
diff --git a/huobi/model/etf/__init__.py b/huobi/model/etf/__init__.py
new file mode 100644
index 0000000..2ee6750
--- /dev/null
+++ b/huobi/model/etf/__init__.py
@@ -0,0 +1,4 @@
+from huobi.model.etf.etf_swap_config import EtfSwapConfig
+from huobi.model.etf.etf_swap_list import EtfSwapList
+from huobi.model.etf.etf_swap_in_out import EtfSwapInOut
+from huobi.model.etf.unitprice import UnitPrice
\ No newline at end of file
diff --git a/huobi/model/etf/etf_swap_config.py b/huobi/model/etf/etf_swap_config.py
new file mode 100644
index 0000000..aa8ea8e
--- /dev/null
+++ b/huobi/model/etf/etf_swap_config.py
@@ -0,0 +1,48 @@
+from huobi.constant import *
+
+
+class EtfSwapConfig:
+ """
+ The basic information of ETF creation and redemption, as well as ETF constituents, including max
+ amount of creation, min amount of creation, max amount of redemption, min amount of redemption,
+ creation fee rate, redemption fee rate, eft create/redeem status.
+
+ :member
+ purchase_max_amount: The max creation amounts per request.
+ purchase_min_amount: The minimum creation amounts per request.
+ redemption_max_amount: The max redemption amounts per request.
+ redemption_min_amount: The minimum redemption amounts per request.
+ purchase_fee_rate: The creation fee rate.
+ redemption_fee_rate: The redemption fee rate.
+ status: The status of the ETF.
+ unit_price_list: ETF constitution in format of amount and currency.
+
+ """
+ def __init__(self):
+ self.etf_name = ""
+ self.etf_status = EtfStatus.INVALID
+ self.purchase_fee_rate = 0.0
+ self.purchase_max_amount = 0
+ self.purchase_min_amount = 0
+ self.redemption_fee_rate = 0.0
+ self.redemption_max_amount = 0
+ self.redemption_min_amount = 0
+ self.unit_price = list()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.etf_name, format_data + "ETF Name")
+ PrintBasic.print_basic(self.etf_status, format_data + "ETF Status")
+
+ PrintBasic.print_basic(self.purchase_fee_rate, format_data + "Purchase Fee Rate")
+ PrintBasic.print_basic(self.purchase_max_amount, format_data + "Purchase Max Amount")
+ PrintBasic.print_basic(self.purchase_min_amount, format_data + "Purchase Min Amount")
+
+ PrintBasic.print_basic(self.redemption_fee_rate, format_data + "Redemption Fee Rate")
+ PrintBasic.print_basic(self.redemption_max_amount, format_data + "Redemption Max Amount")
+ PrintBasic.print_basic(self.redemption_min_amount, format_data + "Redemption Min Amount")
+ print()
+ if len(self.unit_price):
+ for row in self.unit_price:
+ row.print_object(format_data)
+ print()
diff --git a/huobi/model/etf/etf_swap_in_out.py b/huobi/model/etf/etf_swap_in_out.py
new file mode 100644
index 0000000..0641c19
--- /dev/null
+++ b/huobi/model/etf/etf_swap_in_out.py
@@ -0,0 +1,19 @@
+
+
+class EtfSwapInOut:
+ """
+ :member
+ """
+
+ def __init__(self):
+ self.code = 0
+ self.data = None
+ self.message = ""
+ self.success = False
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.code, format_data + "Return Code")
+ PrintBasic.print_basic_bool(self.data, format_data + "Data")
+ PrintBasic.print_basic(self.message, format_data + "Message")
+ PrintBasic.print_basic_bool(self.success, format_data + "Success")
diff --git a/huobi/model/etf/etf_swap_list.py b/huobi/model/etf/etf_swap_list.py
new file mode 100644
index 0000000..be879ec
--- /dev/null
+++ b/huobi/model/etf/etf_swap_list.py
@@ -0,0 +1,88 @@
+from huobi.constant import *
+from huobi.model.etf.unitprice import UnitPrice
+from huobi.utils import default_parse_list_dict
+
+
+class EtfSwapList:
+ """
+ The past creation and redemption.
+
+ :member
+ id: the operation Id.
+ gmt_created: The UNIX formatted timestamp in UTC of the operation.
+ currency: The ETF name.
+ amount: Creation or redemption amount.
+ type: The swap type. Creation or redemption.
+ status: The operation result
+ rate: The fee rate.
+ fee: The actual fee amount
+
+ point_card_amount: Discount from point card.
+ used_currency_list: For creation this is the list and amount of underlying assets used for ETF creation.
+ For redemption this is the amount of ETF used for redemption. The content is UnitPrice class.
+ obtain_currency_list: For creation this is the amount for ETF created.
+ For redemption this is the list and amount of underlying assets obtained. The content is UnitPrice class
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.gmt_created = 0
+ self.currency = ""
+ self.amount = 0.0
+ self.type = EtfSwapType.INVALID
+ self.status = 0
+ self.rate = 0.0
+ self.fee = 0.0
+ self.point_card_amount = 0.0
+ self.used_currency_list = list()
+ self.obtain_currency_list = list()
+
+ @staticmethod
+ def json_parse(dict_data):
+ if dict_data and len(dict_data):
+ detail = dict_data.get("detail", {})
+ dict_data.pop("detail")
+ etf_swap_obj = default_parse_list_dict(dict_data, EtfSwapList)
+ if detail and len(detail):
+ etf_swap_obj.rate = detail.get("rate", 0)
+ etf_swap_obj.fee = detail.get("fee", 0)
+ etf_swap_obj.point_card_amount = detail.get("point_card_amount", 0)
+ etf_swap_obj.used_currency_list = default_parse_list_dict(detail.get("used_currency_list"), UnitPrice, [])
+ etf_swap_obj.obtain_currency_list = default_parse_list_dict(detail.get("obtain_currency_list"), UnitPrice, [])
+ return etf_swap_obj
+
+ return None
+
+ @staticmethod
+ def json_parse_list(dict_data):
+ ret_list = list()
+ for item in dict_data:
+ item_obj = EtfSwapList.json_parse(item)
+ if item_obj is not None:
+ ret_list.append(item_obj)
+
+ return ret_list
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "Operater Id")
+ PrintBasic.print_basic(self.gmt_created, format_data + "GMT Create Time")
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.type, format_data + "Type")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.rate, format_data + "Rate")
+ PrintBasic.print_basic(self.fee, format_data + "Fee")
+ PrintBasic.print_basic(self.status, format_data + "Status")
+ PrintBasic.print_basic(self.point_card_amount, format_data + "Point Card Amount")
+
+ if len(self.used_currency_list):
+ PrintBasic.print_basic("used_currency_list as below:")
+ for row in self.used_currency_list:
+ row.print_object(format_data + "\t")
+
+ if len(self.obtain_currency_list):
+ PrintBasic.print_basic("obtain_currency_list as below:")
+ for row in self.obtain_currency_list:
+ row.print_object(format_data + "\t")
+
diff --git a/huobi/model/etf/unitprice.py b/huobi/model/etf/unitprice.py
new file mode 100644
index 0000000..b9c3340
--- /dev/null
+++ b/huobi/model/etf/unitprice.py
@@ -0,0 +1,10 @@
+class UnitPrice:
+
+ def __init__(self):
+ self.currency = ""
+ self.amount = 0.0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
\ No newline at end of file
diff --git a/huobi/model/generic/__init__.py b/huobi/model/generic/__init__.py
new file mode 100644
index 0000000..08d45b4
--- /dev/null
+++ b/huobi/model/generic/__init__.py
@@ -0,0 +1,5 @@
+from huobi.model.generic.symbol import Symbol
+from huobi.model.generic.exchange_info import ExchangeInfo
+from huobi.model.generic.chain import Chain
+from huobi.model.generic.reference_currency import ReferenceCurrency
+from huobi.model.generic.market_status import MarketStatus
diff --git a/huobi/model/generic/chain.py b/huobi/model/generic/chain.py
new file mode 100644
index 0000000..1ae896d
--- /dev/null
+++ b/huobi/model/generic/chain.py
@@ -0,0 +1,69 @@
+from huobi.constant import *
+
+class Chain:
+ """
+ The Huobi Chain.
+
+ :member
+ chain: Chain name
+ numOfConfirmations: Number of confirmations required for deposit success (trading & withdrawal allowed once reached)
+ numOfFastConfirmations: Number of confirmations required for quick success (trading allowed but withdrawal disallowed once reached)
+ minDepositAmt: Minimal deposit amount in each request
+ depositStatus: Deposit status allowed,prohibited
+ minWithdrawAmt: Minimal withdraw amount in each request.
+ maxWithdrawAmt : Maximum withdraw amount in each request
+ withdrawQuotaPerDay : Maximum withdraw amount in a day
+ withdrawQuotaPerYear : Maximum withdraw amount in a year
+ withdrawQuotaTotal : Maximum withdraw amount in total
+ withdrawPrecision : Withdraw amount precision
+ withdrawFeeType : Type of withdraw fee (only one type can be applied to each currency)
+
+ transactFeeWithdraw : Withdraw fee in each request (only applicable to withdrawFeeType = fixed)
+ minTransactFeeWithdraw : Minimal withdraw fee in each request (only applicable to withdrawFeeType = circulated)
+ maxTransactFeeWithdraw : Maximum withdraw fee in each request (only applicable to withdrawFeeType = circulated or ratio)
+ transactFeeRateWithdraw : Withdraw fee in each request (only applicable to withdrawFeeType = ratio)
+ withdrawStatus : Withdraw status
+ """
+
+ def __init__(self):
+ self.chain = ""
+ self.baseChain = ""
+ self.baseChainProtocol = ""
+ self.numOfConfirmations = 0
+ self.numOfFastConfirmations = 0
+ self.depositStatus = ChainDepositStatus.INVALID
+ self.minDepositAmt = 0
+ self.withdrawStatus = ChainWithdrawStatus.INVALID
+ self.minWithdrawAmt = 0
+ self.withdrawPrecision = 0
+ self.maxWithdrawAmt = 0.0
+ self.withdrawQuotaPerDay = 0.0
+ self.withdrawQuotaPerYear = 0.0
+ self.withdrawQuotaTotal = 0.0
+ self.withdrawFeeType = ""
+ self.transactFeeWithdraw = 0.0
+ self.minTransactFeeWithdraw = 0.0
+ self.maxTransactFeeWithdraw = 0.0
+ self.transactFeeRateWithdraw = 0.0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.chain, format_data + "Chain")
+ PrintBasic.print_basic(self.baseChain, format_data + "Base Chain")
+ PrintBasic.print_basic(self.baseChainProtocol, format_data + "Base Chain Protocol")
+ PrintBasic.print_basic(self.numOfConfirmations, format_data + "numOfConfirmations")
+ PrintBasic.print_basic(self.numOfFastConfirmations, format_data + "numOfFastConfirmations")
+ PrintBasic.print_basic(self.depositStatus, format_data + "depositStatus")
+ PrintBasic.print_basic(self.minDepositAmt, format_data + "minDepositAmount")
+ PrintBasic.print_basic(self.withdrawStatus, format_data + "withdrawStatus")
+ PrintBasic.print_basic(self.minWithdrawAmt, format_data + "minWithdrawAmount")
+ PrintBasic.print_basic(self.withdrawPrecision, format_data + "withdrawPrecision")
+ PrintBasic.print_basic(self.maxWithdrawAmt, format_data + "maxWithdrawAmount")
+ PrintBasic.print_basic(self.withdrawQuotaPerDay, format_data + "withdrawQuotaPerDay")
+ PrintBasic.print_basic(self.withdrawQuotaPerYear, format_data + "withdrawQuotaPerYear")
+ PrintBasic.print_basic(self.withdrawQuotaTotal, format_data + "withdrawQuotaTotal")
+ PrintBasic.print_basic(self.withdrawFeeType, format_data + "withdrawFeeType")
+ PrintBasic.print_basic(self.transactFeeWithdraw, format_data + "transactFeeWithdraw")
+ PrintBasic.print_basic(self.minTransactFeeWithdraw, format_data + "minTransactFeeWithdraw")
+ PrintBasic.print_basic(self.maxTransactFeeWithdraw, format_data + "maxTransactFeeWithdraw")
+ PrintBasic.print_basic(self.transactFeeRateWithdraw, format_data + "transactFeeRateWithdraw")
diff --git a/huobi/model/generic/exchange_info.py b/huobi/model/generic/exchange_info.py
new file mode 100644
index 0000000..d0e6871
--- /dev/null
+++ b/huobi/model/generic/exchange_info.py
@@ -0,0 +1,12 @@
+class ExchangeInfo:
+ """
+ The Huobi supported the symbols and currencies.
+
+ :member
+ symbol_list: The symbol list. The content is Symbol class.
+ currencies: The currency list. The content is string value.
+ """
+
+ def __init__(self):
+ self.symbol_list = list()
+ self.currencies = list()
diff --git a/huobi/model/generic/market_status.py b/huobi/model/generic/market_status.py
new file mode 100644
index 0000000..4a5ac5c
--- /dev/null
+++ b/huobi/model/generic/market_status.py
@@ -0,0 +1,20 @@
+from huobi.constant import *
+
+
+class MarketStatus:
+ """
+ The Huobi market status info.
+
+ :member
+ marketStatus: .
+ haltStartTime: .
+ haltEndTime: .
+ haltReason:
+ affectedSymbols:
+ """
+
+ def __init__(self):
+ self.marketStatus = MarketStatus.NORMAL
+ self.haltStartTime = -1
+ self.haltEndTime = -1
+ self.affectedSymbols = ""
diff --git a/huobi/model/generic/reference_currency.py b/huobi/model/generic/reference_currency.py
new file mode 100644
index 0000000..9e52751
--- /dev/null
+++ b/huobi/model/generic/reference_currency.py
@@ -0,0 +1,26 @@
+from huobi.constant import *
+
+class ReferenceCurrency:
+ """
+ The Huobi supported static reference information for each currency.
+
+ :member
+ currency: currency
+ instStatus: Instrument status
+ chains: chain list
+ """
+
+ def __init__(self):
+ self.currency = ""
+ self.instStatus = InstrumentStatus.INVALID
+ self.chains = []
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.instStatus, format_data + "Instrument Status")
+ if self.chains and len(self.chains):
+ for chain_obj in self.chains:
+ chain_obj.print_object("\t")
+ print()
diff --git a/huobi/model/generic/symbol.py b/huobi/model/generic/symbol.py
new file mode 100644
index 0000000..e498c2a
--- /dev/null
+++ b/huobi/model/generic/symbol.py
@@ -0,0 +1,66 @@
+class Symbol:
+ """
+ The Huobi supported symbols.
+
+ :member
+ base_currency: The base currency in a trading symbol.
+ quote_currency: The quote currency in a trading symbol.
+ price_precision: The quote currency precision when quote price (decimal places).
+ amount_precision: The base currency precision when quote amount (decimal places).
+ symbol_partition: The trading section, possible values: [main,innovation,bifurcation].
+ symbol: The symbol, like "btcusdt".
+ state : trade status, maybe one in [online,offline,suspend]
+ value_precision : value precision
+ min_order_amt : minimum volume limit only used in limit-order and sell-market order
+ max_order_amt : Maximum volume
+ min_order_value : Minimum order amount
+ leverage_ratio : Leverage ratio for symbol
+ limit_order_min_order_amt: Minimum order amount of limit order in base currency (NEW)
+ limit_order_max_order_amt: Max order amount of limit order in base currency (NEW)
+ sell_market_min_order_amt: Minimum order amount of sell-market order in base currency (NEW)
+ sell_market_max_order_amt: Max order amount of sell-market order in base currency (NEW)
+ buy_market_max_order_amt: Max order value of buy-market order in quote currency (NEW)
+ max_order_value: Max order value of limit order and buy-market order in usdt (NEW)
+
+ """
+
+ def __init__(self):
+ self.base_currency = ""
+ self.quote_currency = ""
+ self.price_precision = 0
+ self.amount_precision = 0
+ self.symbol_partition = ""
+ self.symbol = ""
+ self.state = ""
+ self.value_precision = 0
+ self.min_order_amt = ""
+ self.max_order_amt = ""
+ self.min_order_value = ""
+ self.leverage_ratio = 0
+ self.limit_order_min_order_amt = 0
+ self.limit_order_max_order_amt = 0
+ self.sell_market_min_order_amt = 0
+ self.sell_market_max_order_amt = 0
+ self.buy_market_max_order_value = 0
+ self.max_order_value = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.base_currency, format_data + "Base Currency")
+ PrintBasic.print_basic(self.quote_currency, format_data + "Quote Currency")
+ PrintBasic.print_basic(self.price_precision, format_data + "Price Precision")
+ PrintBasic.print_basic(self.amount_precision, format_data + "Amount Precision")
+ PrintBasic.print_basic(self.symbol_partition, format_data + "Symbol Partition")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.state, format_data + "State")
+ PrintBasic.print_basic(self.value_precision, format_data + "Value Precision")
+ PrintBasic.print_basic(self.min_order_amt, format_data + "Min Order Amount")
+ PrintBasic.print_basic(self.max_order_amt, format_data + "Max Order Amount")
+ PrintBasic.print_basic(self.min_order_value, format_data + "Min Order Value")
+ PrintBasic.print_basic(self.leverage_ratio, format_data + "Leverage Ratio")
+ PrintBasic.print_basic(self.limit_order_min_order_amt, format_data + "Minimum order amount (Limit Order)")
+ PrintBasic.print_basic(self.limit_order_max_order_amt, format_data + "Max order amount (Limit Order)")
+ PrintBasic.print_basic(self.sell_market_min_order_amt, format_data + "Min order amount (Sell Market Order)")
+ PrintBasic.print_basic(self.sell_market_max_order_amt, format_data + "Max order amount (Sell Market Order)")
+ PrintBasic.print_basic(self.buy_market_max_order_value, format_data + "Max order value (Buy Market Order)")
+ PrintBasic.print_basic(self.max_order_value, format_data + "Max order value (In USDT)")
diff --git a/huobi/model/margin/__init__.py b/huobi/model/margin/__init__.py
new file mode 100644
index 0000000..9e38399
--- /dev/null
+++ b/huobi/model/margin/__init__.py
@@ -0,0 +1,8 @@
+from huobi.model.margin.loan_order import LoanOrder
+from huobi.model.margin.margin_account_balance import MarginAccountBalance
+from huobi.model.margin.loan_ino import LoanInfo
+from huobi.model.margin.margin_loan_ino import MarginLoanInfo
+from huobi.model.margin.cross_margin_loan_ino import CrossMarginLoanInfo
+from huobi.model.margin.cross_margin_account_balance import CrossMarginAccountBalance
+
+
diff --git a/huobi/model/margin/cross_margin_account_balance.py b/huobi/model/margin/cross_margin_account_balance.py
new file mode 100644
index 0000000..d62a89d
--- /dev/null
+++ b/huobi/model/margin/cross_margin_account_balance.py
@@ -0,0 +1,49 @@
+from huobi.constant import *
+from huobi.model.account import Balance
+from huobi.utils import *
+
+
+class CrossMarginAccountBalance:
+
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ id: The unique account id.
+ type: The type of this account, possible value: spot, margin, otc, point.
+ state: The account state, possible value: working, lock.
+ list: The balance list of the specified currency. The content is Balance class
+ """
+
+
+ def __init__(self):
+ self.id = 0
+ self.type = AccountType.INVALID
+ self.state = AccountState.INVALID
+ self.risk_rate = 0
+ self.acct_balance_sum = 0.0
+ self.debt_balance_sum = 0.0
+ self.list = list()
+
+ @staticmethod
+ def json_parse(data_json):
+ balance_list_json = data_json.get("list", [])
+ data_json.pop("list")
+
+ account_balance = default_parse_list_dict(data_json, CrossMarginAccountBalance)
+ account_balance.list = default_parse_list_dict(balance_list_json, Balance, [])
+
+ return account_balance
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "Account ID")
+ PrintBasic.print_basic(self.type, format_data + "Account Type")
+ PrintBasic.print_basic(self.state, format_data + "Account State")
+ PrintBasic.print_basic(self.risk_rate, format_data + "Risk Rate")
+ PrintBasic.print_basic(self.acct_balance_sum, format_data + "Total Balance")
+ PrintBasic.print_basic(self.debt_balance_sum, format_data + "Debt Balance")
+ if self.list and len(self.list):
+ for balance in self.list:
+ balance.print_object("\t")
+ print()
diff --git a/huobi/model/margin/cross_margin_loan_ino.py b/huobi/model/margin/cross_margin_loan_ino.py
new file mode 100644
index 0000000..e9b6986
--- /dev/null
+++ b/huobi/model/margin/cross_margin_loan_ino.py
@@ -0,0 +1,9 @@
+
+from huobi.model.margin.loan_ino import LoanInfo
+
+class CrossMarginLoanInfo(LoanInfo):
+ def __init__(self):
+ LoanInfo.__init__(self)
+
+ def print_object(self, format_data=""):
+ LoanInfo.print_object(self)
diff --git a/huobi/model/margin/general_repay_loan_record.py b/huobi/model/margin/general_repay_loan_record.py
new file mode 100644
index 0000000..d0e0760
--- /dev/null
+++ b/huobi/model/margin/general_repay_loan_record.py
@@ -0,0 +1,59 @@
+from huobi.constant import *
+
+
+class GeneralRepayLoanRecord:
+ """
+ The general repay loan record information.
+
+ :member
+ repayId: repayment transaction ID.
+ repayTime: repayment transaction time (unix time in millisecond).
+ accountId: repayment account ID.
+ currency: repayment currency, like "usdt".
+ repaidAmount: repaid amount.
+ transactIds: ID list of original loan transactions (arranged by order of repayment time).
+ nextId: search the start ID in the next page (return only when there is data in the next page).
+ """
+
+ def __init__(self):
+ self.repayId = None
+ self.repayTime = None
+ self.accountId = None
+ self.currency = None
+ self.repaidAmount = None
+ self.transactIds = Transact()
+ self.nextId = None
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.repayId, format_data + "repayId")
+ PrintBasic.print_basic(self.repayTime, format_data + "repayTime")
+ PrintBasic.print_basic(self.accountId, format_data + "accountId")
+ PrintBasic.print_basic(self.currency, format_data + "currency")
+ PrintBasic.print_basic(self.repaidAmount, format_data + "repaidAmount")
+ PrintBasic.print_basic(self.transactIds, format_data + "transactIds")
+ PrintBasic.print_basic(self.nextId, format_data + "nextId")
+
+ print()
+
+
+class Transact:
+
+ """
+ The general repay loan record information.
+
+ :member
+ transactId: original loan transaction ID.
+ repaidPrincipal: principal repaid.
+ repaidInterest: interest repaid.
+ paidHt: HT paid.
+ paidPoint: point paid.
+ """
+
+ def __init__(self):
+ self.transactId = None
+ self.repaidPrincipal = None
+ self.repaidInterest = None
+ self.paidHt = None
+ self.paidPoint = None
+
diff --git a/huobi/model/margin/general_repay_loan_result.py b/huobi/model/margin/general_repay_loan_result.py
new file mode 100644
index 0000000..1b65954
--- /dev/null
+++ b/huobi/model/margin/general_repay_loan_result.py
@@ -0,0 +1,31 @@
+from huobi.constant import *
+
+
+class GeneralRepayLoanResult:
+ """
+ The margin order information.
+
+ :member
+ id: Inner id.
+ type: The account type.
+ state: The account state.
+ symbol: The symbol, like "btcusdt".
+ fl_price: The trigger price.
+ fl_type: The trigger type.
+ risk_rate: The risk rate.
+ list:Balance Object list
+ """
+
+ def __init__(self):
+ self.repayId = 0
+ self.repayTime = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.repayId, format_data + "repayId")
+ PrintBasic.print_basic(self.repayTime, format_data + "repayTime")
+
+ print()
+
+
+
diff --git a/huobi/model/margin/loan_ino.py b/huobi/model/margin/loan_ino.py
new file mode 100644
index 0000000..27bdba9
--- /dev/null
+++ b/huobi/model/margin/loan_ino.py
@@ -0,0 +1,30 @@
+
+class LoanInfo:
+ """
+ The margin rate define.
+
+ :member
+ currency: The currency name.
+ interest_rate: all interest rate
+ min_loan_amt: min loan amount.
+ max_loan_amt: max loan amount.
+ loanable_amt: loanable amount.
+ actual_rate: rate after deduction.
+ """
+
+ def __init__(self):
+ self.currency = ""
+ self.interest_rate = ""
+ self.min_loan_amt = ""
+ self.max_loan_amt = ""
+ self.loanable_amt = ""
+ self.actual_rate = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.interest_rate, format_data + "Interest Rate")
+ PrintBasic.print_basic(self.min_loan_amt, format_data + "Min Loan Amount")
+ PrintBasic.print_basic(self.max_loan_amt, format_data + "Max Loan Amount")
+ PrintBasic.print_basic(self.loanable_amt, format_data + "Loanable Amount")
+ PrintBasic.print_basic(self.actual_rate, format_data + "Actual Rate")
diff --git a/huobi/model/margin/loan_order.py b/huobi/model/margin/loan_order.py
new file mode 100644
index 0000000..8d92fe2
--- /dev/null
+++ b/huobi/model/margin/loan_order.py
@@ -0,0 +1,62 @@
+from huobi.constant import *
+
+
+class LoanOrder:
+ """
+ The margin order information.
+
+ :member
+ id: The order id.
+ user_id: The user id.
+ account_type: The account type which created the loan order.
+ currency: The currency name.
+ loan_amount: The amount of the origin loan.
+ loan_balance: The amount of the loan left.
+ interest_rate: The loan interest rate.
+ interest_amount: The accumulated loan interest.
+ interest_balance: The amount of loan interest left.
+ state: The loan stats, possible values: created, accrual, cleared, invalid.
+ created_at: The UNIX formatted timestamp in UTC when the order was created.
+ accrued_at: The UNIX formatted timestamp in UTC when the last accrue happened.
+ """
+
+ def __init__(self):
+ self.currency = ""
+ self.deduct_rate = 0
+ self.paid_point = 0.0
+ self.deduct_currency = ""
+ self.user_id = 0
+ self.created_at = 0
+ self.account_id = 0
+ self.paid_coin = 0.0
+ self.loan_amount = 0.0
+ self.interest_amount = 0.0
+ self.deduct_amount = 0.0
+ self.loan_balance = 0.0
+ self.interest_balance = 0.0
+ self.updated_at = 0
+ self.accrued_at = 0
+ self.interest_rate = 0.0
+ self.id = 0
+ self.state = LoanOrderState.INVALID
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.deduct_rate, format_data + "Deduct Rate")
+ PrintBasic.print_basic(self.paid_point, format_data + "Paid Point")
+ PrintBasic.print_basic(self.deduct_currency, format_data + "Deduct Currency")
+ PrintBasic.print_basic(self.user_id, format_data + "User Id")
+ PrintBasic.print_basic(self.created_at, format_data + "Create Time")
+ PrintBasic.print_basic(self.account_id, format_data + "Account Id")
+ PrintBasic.print_basic(self.paid_coin, format_data + "Paid Coin")
+ PrintBasic.print_basic(self.loan_amount, format_data + "Load Amount")
+ PrintBasic.print_basic(self.interest_amount, format_data + "Interest Amount")
+ PrintBasic.print_basic(self.deduct_amount, format_data + "Deduct Amount")
+ PrintBasic.print_basic(self.loan_balance, format_data + "Loan Balance")
+ PrintBasic.print_basic(self.interest_balance, format_data + "Interest Balance")
+ PrintBasic.print_basic(self.updated_at, format_data + "Update Time")
+ PrintBasic.print_basic(self.accrued_at, format_data + "Accrued Time")
+ PrintBasic.print_basic(self.interest_rate, format_data + "Interest Rate")
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.state, format_data + "Loan Order State")
diff --git a/huobi/model/margin/margin_account_balance.py b/huobi/model/margin/margin_account_balance.py
new file mode 100644
index 0000000..f5aff02
--- /dev/null
+++ b/huobi/model/margin/margin_account_balance.py
@@ -0,0 +1,41 @@
+from huobi.constant import *
+
+
+class MarginAccountBalance:
+ """
+ The margin order information.
+
+ :member
+ id: Inner id.
+ type: The account type.
+ state: The account state.
+ symbol: The symbol, like "btcusdt".
+ fl_price: The trigger price.
+ fl_type: The trigger type.
+ risk_rate: The risk rate.
+ list:Balance Object list
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.type = AccountType.INVALID
+ self.state = AccountState.INVALID
+ self.symbol = ""
+ self.fl_price = 0.0
+ self.fl_type = 0.0
+ self.risk_rate = 0.0
+ self.list = []
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.type, format_data + "Account Type")
+ PrintBasic.print_basic(self.state, format_data + "Account State")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.fl_price, format_data + "Trigger Price")
+ PrintBasic.print_basic(self.fl_type, format_data + "Trigger Type")
+ PrintBasic.print_basic(self.risk_rate, format_data + "Risk Rate")
+ if self.list and len(self.list):
+ for balance_obj in self.list:
+ balance_obj.print_object("\t")
+ print()
\ No newline at end of file
diff --git a/huobi/model/margin/margin_loan_ino.py b/huobi/model/margin/margin_loan_ino.py
new file mode 100644
index 0000000..e7f3e48
--- /dev/null
+++ b/huobi/model/margin/margin_loan_ino.py
@@ -0,0 +1,40 @@
+from huobi.model.margin.loan_ino import LoanInfo
+from huobi.utils import default_parse_list_dict
+
+
+class MarginLoanInfo:
+ """
+ The margin loan info.
+
+ :member
+ symbol: symbol like "btcusdt"
+ currencies: loan info for currency in symbol
+ """
+
+ def __init__(self):
+ self.symbol = ""
+ self.currencies = list()
+
+ @staticmethod
+ def json_parse(json_data):
+ retList = []
+ for idx, item in enumerate(json_data):
+ margin_loan_obj = MarginLoanInfo()
+ margin_loan_obj.symbol = item.get("symbol", "")
+
+ currencies_json = item.get("currencies")
+ result_list = default_parse_list_dict(currencies_json, LoanInfo, [])
+
+ margin_loan_obj.currencies = result_list
+
+ retList.append(margin_loan_obj)
+
+ return retList
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ if self.currencies and len(self.currencies):
+ for currency_item in self.currencies:
+ currency_item.print_object("\t")
+ print()
diff --git a/huobi/model/market/__init__.py b/huobi/model/market/__init__.py
new file mode 100644
index 0000000..ffb0366
--- /dev/null
+++ b/huobi/model/market/__init__.py
@@ -0,0 +1,26 @@
+from huobi.model.market.candlestick import Candlestick
+from huobi.model.market.candlestick_event import CandlestickEvent
+from huobi.model.market.candlestick_req import CandlestickReq
+from huobi.model.market.last_trade_bestquote import LastTradeAndBestQuote
+from huobi.model.market.pricedepth import PriceDepth
+from huobi.model.market.pricedepth_event import PriceDepthEvent
+from huobi.model.market.pricedepth_req import PriceDepthReq
+from huobi.model.market.pricedepth_bbo import PriceDepthBbo
+from huobi.model.market.pricedepth_bbo_event import PriceDepthBboEvent
+from huobi.model.market.market_detail_merged import MarketDetailMerged
+from huobi.model.market.market_detail import MarketDetail
+from huobi.model.market.market_detail_event import MarketDetailEvent
+from huobi.model.market.market_detail_req import MarketDetailReq
+from huobi.model.market.trade import Trade
+from huobi.model.market.trade_detail import TradeDetail
+from huobi.model.market.trade_detail_event import TradeDetailEvent
+from huobi.model.market.trade_detail_req import TradeDetailReq
+from huobi.model.market.market_ticker import MarketTicker
+from huobi.model.market.depth_entry import DepthEntry
+from huobi.model.market.mbp import Mbp
+from huobi.model.market.mbp_increase_event import MbpIncreaseEvent
+from huobi.model.market.mbp_full_event import MbpFullEvent
+from huobi.model.market.mbp_req import MbpReq
+
+
+
diff --git a/huobi/model/market/candlestick.py b/huobi/model/market/candlestick.py
new file mode 100644
index 0000000..2bfdc61
--- /dev/null
+++ b/huobi/model/market/candlestick.py
@@ -0,0 +1,41 @@
+
+class Candlestick:
+ """
+ The candlestick/kline data.
+
+ :member
+ id : keep the original timestamp
+ timestamp: The UNIX formatted timestamp in UTC.
+ high: The high price.
+ low: The low price.
+ open: The opening price.
+ close: The closing price.
+ amount: The aggregated trading volume in USDT.
+ count: The number of completed trades. it returns 0 when get ETF candlestick
+ vol: The trading volume in base currency.
+
+ """
+
+ def __init__(self):
+ self.id = 0
+ #self.timestamp = 0
+ self.high = 0.0
+ self.low = 0.0
+ self.open = 0.0
+ self.close = 0.0
+ self.amount = 0.0
+ self.count = 0
+ self.vol = 0.0 #self.volume = 0.0
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "Id")
+ #PrintBasic.print_basic(self.timestamp, format_data + "Unix Time")
+ PrintBasic.print_basic(self.high, format_data + "High")
+ PrintBasic.print_basic(self.low, format_data + "Low")
+ PrintBasic.print_basic(self.open, format_data + "Open")
+ PrintBasic.print_basic(self.close, format_data + "Close")
+ PrintBasic.print_basic(self.count, format_data + "Count")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.vol, format_data + "Volume")
\ No newline at end of file
diff --git a/huobi/model/market/candlestick_event.py b/huobi/model/market/candlestick_event.py
new file mode 100644
index 0000000..47784bb
--- /dev/null
+++ b/huobi/model/market/candlestick_event.py
@@ -0,0 +1,26 @@
+from huobi.constant import *
+from huobi.model.market import *
+
+
+class CandlestickEvent:
+ """
+ The candlestick/kline data received by subscription of candlestick/kline.
+
+ :member
+ ch: the topic you subscribed
+ ts: the UNIX formatted timestamp generated by server in UTC.
+ tick: the data of candlestick/kline.
+ """
+
+ def __init__(self):
+ self.ch = ""
+ self.ts = 0
+ self.tick = Candlestick()
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ts, format_data + "Unix Time")
+ PrintBasic.print_basic(self.ch, format_data + "Channel")
+ if self.tick:
+ self.tick.print_object()
diff --git a/huobi/model/market/candlestick_req.py b/huobi/model/market/candlestick_req.py
new file mode 100644
index 0000000..db4d625
--- /dev/null
+++ b/huobi/model/market/candlestick_req.py
@@ -0,0 +1,27 @@
+from huobi.constant import *
+
+class CandlestickReq:
+ """
+ The candlestick/kline data received by subscription of candlestick/kline.
+
+ :member
+ rep: the Channel or topic you subscribed.
+ id: the UNIX formatted timestamp generated by server in UTC.
+ data: the data of candlestick/kline.
+
+ """
+
+ def __init__(self):
+ self.rep = ""
+ self.id = 0
+ self.data = list()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.rep, format_data + "Channel")
+ PrintBasic.print_basic(self.id, format_data + "Unix Time")
+ print()
+ if len(self.data):
+ for row in self.data:
+ row.print_object()
+ print()
\ No newline at end of file
diff --git a/huobi/model/market/depth_entry.py b/huobi/model/market/depth_entry.py
new file mode 100644
index 0000000..e043789
--- /dev/null
+++ b/huobi/model/market/depth_entry.py
@@ -0,0 +1,25 @@
+
+class DepthEntry:
+ """
+ An depth entry consisting of price and amount.
+
+ :member
+ price: The price of the depth.
+ amount: The amount of the depth.
+ """
+
+ def __init__(self):
+ self.price = 0.0
+ self.amount = 0.0
+
+ @staticmethod
+ def json_parse(data_array):
+ entry = DepthEntry()
+ entry.price = data_array[0]
+ entry.amount = data_array[1]
+ return entry
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.price, format_data + "Price")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
\ No newline at end of file
diff --git a/huobi/model/market/last_trade_bestquote.py b/huobi/model/market/last_trade_bestquote.py
new file mode 100644
index 0000000..88977fe
--- /dev/null
+++ b/huobi/model/market/last_trade_bestquote.py
@@ -0,0 +1,22 @@
+class LastTradeAndBestQuote:
+ """
+ The last trade and best bid/ask.
+
+ :member
+ last_trade_price: The last trade price.
+ last_trade_amount: The last trade amount.
+ ask_price: The best ask price.
+ ask_amount: The best ask amount.
+ bid_price: The best bid price.
+ bid_amount: The best bid amount.
+
+ """
+
+ def __init__(self):
+ self.last_trade_price = 0.0
+ self.last_trade_amount = 0.0
+ self.ask_price = 0.0
+ self.ask_amount = 0.0
+ self.bid_price = 0.0
+ self.bid_amount = 0.0
+
diff --git a/huobi/model/market/market_detail.py b/huobi/model/market/market_detail.py
new file mode 100644
index 0000000..094b3de
--- /dev/null
+++ b/huobi/model/market/market_detail.py
@@ -0,0 +1,38 @@
+class MarketDetail:
+ """
+ The summary of trading in the market for the last 24 hours
+
+ :member
+ id: response ID
+ open: The opening price of last 24 hours.
+ close: The last price of last 24 hours.
+ amount: The aggregated trading volume in USDT.
+ high: The high price of last 24 hours.
+ low: The low price of last 24 hours.
+ count: The number of completed trades.
+ volume: The trading volume in base currency of last 24 hours.
+ version: inner data
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.open = 0.0
+ self.close = 0.0
+ self.amount = 0.0
+ self.high = 0.0
+ self.low = 0.0
+ self.count = 0
+ self.vol = 0.0
+ self.version = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.open, format_data + "Open")
+ PrintBasic.print_basic(self.close, format_data + "Close")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.high, format_data + "High")
+ PrintBasic.print_basic(self.low, format_data + "Low")
+ PrintBasic.print_basic(self.count, format_data + "Count")
+ PrintBasic.print_basic(self.vol, format_data + "Volume")
+ # PrintBasic.print_basic(self.version, format_data + "Version")
\ No newline at end of file
diff --git a/huobi/model/market/market_detail_event.py b/huobi/model/market/market_detail_event.py
new file mode 100644
index 0000000..3f70ca4
--- /dev/null
+++ b/huobi/model/market/market_detail_event.py
@@ -0,0 +1,22 @@
+from huobi.model.market.market_detail import MarketDetail
+
+class MarketDetailEvent:
+ """
+ The 24H trade statistics received by subscription of trade statistics.
+
+ :member
+ Channel: topic you subscribe, include symbol.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ trade_statistics: The trade statistics.
+ """
+
+ def __init__(self):
+ self.ch = ""
+ self.ts = 0
+ self.tick = MarketDetail()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Channel")
+ PrintBasic.print_basic(self.ts, format_data + "Timestamp")
+ self.tick.print_object()
\ No newline at end of file
diff --git a/huobi/model/market/market_detail_merged.py b/huobi/model/market/market_detail_merged.py
new file mode 100644
index 0000000..5fe8ab4
--- /dev/null
+++ b/huobi/model/market/market_detail_merged.py
@@ -0,0 +1,41 @@
+
+class MarketDetailMerged:
+ """
+ The best bid/ask consisting of price and amount.
+
+ :member
+ timestamp: The Unix formatted timestamp in UTC.
+ bid_price: The best bid price.
+ bid_amount: The best bid amount.
+ ask_price: The best ask price.
+ ask_amount: The best ask amount.
+
+ """
+
+ def __init__(self):
+ self.amount = 0
+ self.open = 0.0
+ self.close = 0.0
+ self.high = 0.0
+ self.id = 0
+ self.count = 0.0
+ self.low = 0.0
+ self.version = 0
+ self.ask = []
+ self.vol = 0.0
+ self.bid = []
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ # PrintBasic.print_basic(self.version, format_data + "Version")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.count, format_data + "Count")
+ PrintBasic.print_basic(self.vol, format_data + "Volume")
+
+ PrintBasic.print_basic(self.open, format_data + "Open")
+ PrintBasic.print_basic(self.close, format_data + "Close")
+ PrintBasic.print_basic(self.high, format_data + "High")
+ PrintBasic.print_basic(self.low, format_data + "Low")
+
+ print("Ask", self.ask)
+ print("Bid", self.bid)
\ No newline at end of file
diff --git a/huobi/model/market/market_detail_req.py b/huobi/model/market/market_detail_req.py
new file mode 100644
index 0000000..1b73809
--- /dev/null
+++ b/huobi/model/market/market_detail_req.py
@@ -0,0 +1,22 @@
+from huobi.model.market.market_detail import MarketDetail
+
+class MarketDetailReq:
+ """
+ The 24H trade statistics received by request of trade statistics only once.
+
+ :member
+ rep: The topic you subscribed.
+ ts: The UNIX formatted timestamp generated by server in UTC.
+ trade_statistics: The trade statistics.
+ """
+
+ def __init__(self):
+ self.rep = 0
+ self.ts = 0
+ self.data = MarketDetail()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ts, format_data + "Timestamp")
+ PrintBasic.print_basic(self.rep, format_data + "Channel")
+ self.data.print_object()
\ No newline at end of file
diff --git a/huobi/model/market/market_ticker.py b/huobi/model/market/market_ticker.py
new file mode 100644
index 0000000..d9662da
--- /dev/null
+++ b/huobi/model/market/market_ticker.py
@@ -0,0 +1,47 @@
+class MarketTicker:
+ """
+ The ticker information.
+
+ :member
+ amount: The aggregated trading volume in last 24 hours (rotating 24h).
+ count: The number of completed trades of last 24 hours (rotating 24h).
+ open: The opening price of a nature day (Singapore time).
+ close: The last price of a nature day (Singapore time).
+ low: The low price of a nature day (Singapore time).
+ high: The high price of a nature day (Singapore time).
+ vol: The aggregated trading value in last 24 hours (rotating 24h).
+ symbol: The trading symbol of this object, e.g. btcusdt, bccbtc.
+ bid: Best bid price.
+ bidSize: Best bid size.
+ ask: Best ask price.
+ askSize: Best ask size.
+ """
+
+ def __init__(self):
+ self.amount = 0.0
+ self.count = 0
+ self.open = 0.0
+ self.close = 0.0
+ self.low = 0.0
+ self.high = 0.0
+ self.vol = 0.0
+ self.symbol = ""
+ self.bid = 0.0
+ self.bidSize = 0.0
+ self.ask = 0.0
+ self.askSize = 0.0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.count, format_data + "Count")
+ PrintBasic.print_basic(self.open, format_data + "Opening Price")
+ PrintBasic.print_basic(self.close, format_data + "Last Price")
+ PrintBasic.print_basic(self.low, format_data + "Low Price")
+ PrintBasic.print_basic(self.high, format_data + "High Price")
+ PrintBasic.print_basic(self.vol, format_data + "Vol")
+ PrintBasic.print_basic(self.symbol, format_data + "Trading Symbol")
+ PrintBasic.print_basic(self.bid, format_data + "Best Bid Price")
+ PrintBasic.print_basic(self.bidSize, format_data + "Best Bid Size")
+ PrintBasic.print_basic(self.ask, format_data + "Best Ask Price")
+ PrintBasic.print_basic(self.askSize, format_data + "Best Ask Size")
\ No newline at end of file
diff --git a/huobi/model/market/mbp.py b/huobi/model/market/mbp.py
new file mode 100644
index 0000000..fb962cc
--- /dev/null
+++ b/huobi/model/market/mbp.py
@@ -0,0 +1,54 @@
+
+
+from huobi.model.market.depth_entry import DepthEntry
+
+class Mbp:
+ """
+ Increasement of price depth information.
+
+ :member
+ seqNum: current seqNum.
+ prevSeqNum: previous seqNum.
+ bids: The list of the bid depth. The content is DepthEntry class.
+ asks: The list of the ask depth. The content is DepthEntry class.
+
+ """
+ def __init__(self):
+ self.seqNum = 0
+ self.prevSeqNum = 0
+ self.bids = list()
+ self.asks = list()
+
+ @staticmethod
+ def json_parse(json_data):
+ mbp = Mbp()
+ bid_list = list()
+ mbp.seqNum = json_data.get("seqNum", 0)
+ mbp.prevSeqNum = json_data.get("prevSeqNum", 0) # prevSeqNum only for increased subscribe, request doesn't have this value
+ for item in json_data.get("bids", []):
+ depth_entry = DepthEntry()
+ depth_entry.price = item[0]
+ depth_entry.amount = item[1]
+ bid_list.append(depth_entry)
+ ask_list = list()
+ for item in json_data.get("asks", []):
+ depth_entry = DepthEntry()
+ depth_entry.price = item[0]
+ depth_entry.amount = item[1]
+ ask_list.append(depth_entry)
+ mbp.bids = bid_list
+ mbp.asks = ask_list
+
+ return mbp
+
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.seqNum, format_data + "seqNum")
+ PrintBasic.print_basic(self.prevSeqNum, format_data + "prevSeqNum")
+ for entry in self.bids:
+ PrintBasic.print_basic(str(entry.price) + ", amount: " + str(entry.amount), format_data + "Bids price: ")
+
+ for entry in self.asks:
+ PrintBasic.print_basic(str(entry.price) + ", amount: " + str(entry.amount), format_data + "Asks price: ")
diff --git a/huobi/model/market/mbp_full_event.py b/huobi/model/market/mbp_full_event.py
new file mode 100644
index 0000000..e70112e
--- /dev/null
+++ b/huobi/model/market/mbp_full_event.py
@@ -0,0 +1,33 @@
+from huobi.model.market import Mbp
+
+
+class MbpFullEvent:
+ """
+ full price depth.
+
+ :member
+ ch: Topic of subscribed.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ data: The price depth.
+
+ """
+
+ def __init__(self):
+ self.ch = ""
+ self.ts = 0
+ self.data = Mbp()
+
+ @staticmethod
+ def json_parse(json_data):
+ mbp_event = MbpFullEvent()
+ mbp_event.ts = json_data.get("ts")
+ mbp_event.ch = json_data.get("ch")
+ mbp = Mbp.json_parse(json_data.get("tick", {}))
+ mbp_event.data = mbp
+ return mbp_event
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Topic")
+ PrintBasic.print_basic(self.ts, format_data + "Timestamp")
+ self.data.print_object(format_data + "\t")
\ No newline at end of file
diff --git a/huobi/model/market/mbp_increase_event.py b/huobi/model/market/mbp_increase_event.py
new file mode 100644
index 0000000..c60da0b
--- /dev/null
+++ b/huobi/model/market/mbp_increase_event.py
@@ -0,0 +1,34 @@
+from huobi.model.market import Mbp
+
+
+class MbpIncreaseEvent:
+ """
+ increasement of price depth.
+
+ :member
+ ch: Topic of subscribed.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ data: The price depth.
+
+ """
+
+ def __init__(self):
+ self.ch = ""
+ self.ts = 0
+ self.data = Mbp()
+
+ @staticmethod
+ def json_parse(json_data):
+ mbp_event = MbpIncreaseEvent()
+ mbp_event.ts = json_data.get("ts")
+ mbp_event.ch = json_data.get("ch")
+ mbp = Mbp.json_parse(json_data.get("tick", {}))
+ mbp_event.data = mbp
+ return mbp_event
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Topic")
+ PrintBasic.print_basic(self.ts, format_data + "Timestamp")
+
+ self.data.print_object(format_data + "\t")
diff --git a/huobi/model/market/mbp_req.py b/huobi/model/market/mbp_req.py
new file mode 100644
index 0000000..31f4fd9
--- /dev/null
+++ b/huobi/model/market/mbp_req.py
@@ -0,0 +1,36 @@
+from huobi.model.market import Mbp, PriceDepth
+
+
+class MbpReq:
+ """
+ The market price depth.
+
+ :member
+ rep: request Topic
+ id: The UNIX formatted timestamp generated by server in UTC.
+ data: The price depth.
+ """
+
+ def __init__(self):
+ self.rep = ""
+ self.id = ""
+
+ self.data = Mbp()
+
+
+ @staticmethod
+ def json_parse(data_json):
+ mbp_event = MbpReq()
+ mbp_event.id = data_json.get("id")
+ mbp_event.rep = data_json.get("rep")
+ data = data_json.get("data", {})
+ mbp = Mbp.json_parse(data)
+ mbp_event.data = mbp
+ return mbp_event
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.rep, format_data + "Topic")
+ PrintBasic.print_basic(self.id, format_data + "Timestamp")
+
+ self.data.print_object(format_data + "\t")
diff --git a/huobi/model/market/pricedepth.py b/huobi/model/market/pricedepth.py
new file mode 100644
index 0000000..814a120
--- /dev/null
+++ b/huobi/model/market/pricedepth.py
@@ -0,0 +1,59 @@
+from huobi.model.market.depth_entry import DepthEntry
+
+
+class PriceDepth:
+ """
+ The price depth information.
+
+ :member
+ ts: The UNIX formatted timestamp in UTC.
+ version:
+ bids: The list of the bid depth. The content is DepthEntry class.
+ asks: The list of the ask depth. The content is DepthEntry class.
+
+ """
+ def __init__(self):
+ self.ts = 0
+ self.version = 0
+ self.bids = list()
+ self.asks = list()
+
+ @staticmethod
+ def json_parse(dict_data):
+ price_depth_obj = PriceDepth()
+ price_depth_obj.ts = dict_data.get("ts")
+ price_depth_obj.version = dict_data.get("version")
+ bid_list = list()
+ bids_array = dict_data.get("bids", [])
+ for item in bids_array:
+ depth_entry = DepthEntry.json_parse(item)
+ bid_list.append(depth_entry)
+ ask_list = list()
+ asks_array = dict_data.get("asks", [])
+ for item in asks_array:
+ depth_entry = DepthEntry.json_parse(item)
+ ask_list.append(depth_entry)
+ price_depth_obj.bids = bid_list
+ price_depth_obj.asks = ask_list
+
+ return price_depth_obj
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ts, format_data + "UTC Time")
+ PrintBasic.print_basic(self.version, format_data + "Version")
+ if len(self.bids):
+ i = 0
+ print(format_data, "---- Top " + str(len(self.bids)) + " bids ----")
+ for entry in self.bids:
+ i = i + 1
+ print(format_data, str(i) + ": price: " + str(entry.price) + ", amount: " + str(entry.amount))
+ #print()
+
+ if len(self.asks):
+ i = 0
+ print(format_data, "---- Top " + str(len(self.asks)) + " asks ----")
+ for entry in self.asks:
+ i = i + 1
+ print(format_data, str(i) + ": price: " + str(entry.price) + ", amount: " + str(entry.amount))
+ #print()
\ No newline at end of file
diff --git a/huobi/model/market/pricedepth_bbo.py b/huobi/model/market/pricedepth_bbo.py
new file mode 100644
index 0000000..001a1b7
--- /dev/null
+++ b/huobi/model/market/pricedepth_bbo.py
@@ -0,0 +1,34 @@
+
+class PriceDepthBbo:
+ """
+ The price depth information.
+
+ :member
+ timestamp: The UNIX formatted timestamp in UTC.
+ bid: the first bid near trade value.
+ bidSize: the bid size.
+ ask: The first ask near trade value.
+ askSize: the ask size.
+ quoteTime : quote time
+ symbol : trade symbol
+
+
+ """
+ def __init__(self):
+ self.seqId = 0
+ self.ask = 0.0
+ self.askSize = 0.0
+ self.bid = 0.0
+ self.bidSize = 0.0
+ self.quoteTime = 0
+ self.symbol = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.seqId, format_data + "Seq ID")
+ PrintBasic.print_basic(self.ask, format_data + "Ask")
+ PrintBasic.print_basic(self.askSize, format_data + "Ask Size")
+ PrintBasic.print_basic(self.bid, format_data + "Bid")
+ PrintBasic.print_basic(self.bidSize, format_data + "Bid Size")
+ PrintBasic.print_basic(self.quoteTime, format_data + "Quote Time")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
\ No newline at end of file
diff --git a/huobi/model/market/pricedepth_bbo_event.py b/huobi/model/market/pricedepth_bbo_event.py
new file mode 100644
index 0000000..f65d560
--- /dev/null
+++ b/huobi/model/market/pricedepth_bbo_event.py
@@ -0,0 +1,25 @@
+from huobi.model.market import PriceDepthBbo
+
+
+class PriceDepthBboEvent:
+ """
+ The price depth received by subscription of price depth.
+
+ :member
+ symbol: The symbol you subscribed.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ data: The price depth.
+
+ """
+
+ def __init__(self):
+ self.ts = 0
+ self.ch = ""
+ self.tick = PriceDepthBbo()
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ts, format_data + "Time")
+ PrintBasic.print_basic(self.ch, format_data + "Channel")
+ self.tick.print_object(format_data)
\ No newline at end of file
diff --git a/huobi/model/market/pricedepth_event.py b/huobi/model/market/pricedepth_event.py
new file mode 100644
index 0000000..aa960be
--- /dev/null
+++ b/huobi/model/market/pricedepth_event.py
@@ -0,0 +1,23 @@
+
+from huobi.model.market.pricedepth import PriceDepth
+class PriceDepthEvent:
+ """
+ The price depth information.
+
+ :member
+ ts: The UNIX formatted timestamp in UTC.
+ version:
+ bids: The list of the bid depth. The content is DepthEntry class.
+ asks: The list of the ask depth. The content is DepthEntry class.
+
+ """
+ def __init__(self):
+ self.ch = ""
+ self.tick = PriceDepth()
+
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Channel")
+ self.tick.print_object("\t")
\ No newline at end of file
diff --git a/huobi/model/market/pricedepth_req.py b/huobi/model/market/pricedepth_req.py
new file mode 100644
index 0000000..f04f993
--- /dev/null
+++ b/huobi/model/market/pricedepth_req.py
@@ -0,0 +1,22 @@
+
+from huobi.model.market.pricedepth import PriceDepth
+
+class PriceDepthReq:
+ """
+ The price depth information.
+
+ :member
+ ts: The UNIX formatted timestamp in UTC.
+ version:
+ bids: The list of the bid depth. The content is DepthEntry class.
+ asks: The list of the ask depth. The content is DepthEntry class.
+
+ """
+ def __init__(self):
+ self.rep = ""
+ self.data = PriceDepth()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.rep, format_data + "Channel")
+ self.data.print_object("\t")
\ No newline at end of file
diff --git a/huobi/model/market/trade.py b/huobi/model/market/trade.py
new file mode 100644
index 0000000..e621ed0
--- /dev/null
+++ b/huobi/model/market/trade.py
@@ -0,0 +1,29 @@
+
+class Trade:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ price: The trading price in quote currency.
+ amount: The trading volume in base currency.
+ trade_id: The unique trade id of this trade.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ direction: The direction of the taker trade: 'buy' or 'sell'.
+ """
+
+ def __init__(self):
+ self.price = 0.0
+ self.amount = 0.0
+ self.trade_id = 0
+ self.ts = 0
+ self.direction = ""
+
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.trade_id, format_data + "Trade Id")
+ PrintBasic.print_basic(self.ts, format_data + "Trade Time")
+ PrintBasic.print_basic(self.price, format_data + "Price")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.direction, format_data + "Direction")
\ No newline at end of file
diff --git a/huobi/model/market/trade_detail.py b/huobi/model/market/trade_detail.py
new file mode 100644
index 0000000..629db66
--- /dev/null
+++ b/huobi/model/market/trade_detail.py
@@ -0,0 +1,29 @@
+
+class TradeDetail:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ price: The trading price in quote currency.
+ amount: The trading volume in base currency.
+ tradeId: The unique trade id of this trade.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ direction: The direction of the taker trade: 'buy' or 'sell'.
+ """
+
+ def __init__(self):
+ self.price = 0.0
+ self.amount = 0.0
+ self.tradeId = 0
+ self.ts = 0
+ self.direction = ""
+
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.tradeId, format_data + "Trade Id")
+ PrintBasic.print_basic(self.ts, format_data + "Trade Time")
+ PrintBasic.print_basic(self.price, format_data + "Price")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.direction, format_data + "Direction")
\ No newline at end of file
diff --git a/huobi/model/market/trade_detail_event.py b/huobi/model/market/trade_detail_event.py
new file mode 100644
index 0000000..ef16073
--- /dev/null
+++ b/huobi/model/market/trade_detail_event.py
@@ -0,0 +1,28 @@
+
+
+class TradeDetailEvent:
+ """
+ The trade received by subscription of trade.
+
+ :member
+ symbol: The symbol you subscribed.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ trade_list: The trade list. The content is Trade class.
+ """
+
+ def __init__(self):
+ self.ch = ""
+ self.id = 0
+ self.ts = 0
+ self.data = list()
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Channel")
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.ts, format_data + "Unix Time")
+ if len(self.data):
+ for trade_detail in self.data:
+ trade_detail.print_object()
+ print()
\ No newline at end of file
diff --git a/huobi/model/market/trade_detail_req.py b/huobi/model/market/trade_detail_req.py
new file mode 100644
index 0000000..8aa1e29
--- /dev/null
+++ b/huobi/model/market/trade_detail_req.py
@@ -0,0 +1,23 @@
+
+
+class TradeDetailReq:
+ """
+ The trade received by subscription of trade.
+
+ :member
+ rep: The Channel you subscribed.
+ trade_list: The trade list. The content is Trade class.
+ """
+
+ def __init__(self):
+ self.rep = ""
+ self.data = list()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.rep, format_data + "Channel")
+ print()
+ if len(self.data):
+ for trade_detail in self.data:
+ trade_detail.print_object()
+ print()
\ No newline at end of file
diff --git a/huobi/model/subuser/__init__.py b/huobi/model/subuser/__init__.py
new file mode 100644
index 0000000..41bd908
--- /dev/null
+++ b/huobi/model/subuser/__init__.py
@@ -0,0 +1,5 @@
+from huobi.model.subuser.subuser_creation import SubuserCreation
+from huobi.model.subuser.subuser_transferability import SubuserTransferability
+from huobi.model.subuser.subuser_apikey_generation import SubuserApikeyGeneration
+from huobi.model.subuser.user_apikey_info import UserApikeyInfo
+from huobi.model.subuser.subuser_apikey_modification import SubuserApikeyModification
diff --git a/huobi/model/subuser/subuser_apikey_generation.py b/huobi/model/subuser/subuser_apikey_generation.py
new file mode 100644
index 0000000..aad98a3
--- /dev/null
+++ b/huobi/model/subuser/subuser_apikey_generation.py
@@ -0,0 +1,26 @@
+class SubuserApikeyGeneration:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ accessKey:
+ secretKey:
+ note:
+ permission: "trade,readOnly",
+ ipAddresses":
+ """
+
+ def __init__(self):
+ self.accessKey = ""
+ self.secretKey = ""
+ self.note = ""
+ self.permission = ""
+ self.ipAddresses = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.accessKey, format_data + "accessKey")
+ PrintBasic.print_basic(self.secretKey, format_data + "secretKey")
+ PrintBasic.print_basic(self.note, format_data + "note")
+ PrintBasic.print_basic(self.permission, format_data + "permission")
+ PrintBasic.print_basic(self.ipAddresses, format_data + "ipAddresses")
diff --git a/huobi/model/subuser/subuser_apikey_modification.py b/huobi/model/subuser/subuser_apikey_modification.py
new file mode 100644
index 0000000..6265e7d
--- /dev/null
+++ b/huobi/model/subuser/subuser_apikey_modification.py
@@ -0,0 +1,20 @@
+class SubuserApikeyModification:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ note:
+ permission: "trade,readOnly",
+ ipAddresses":
+ """
+
+ def __init__(self):
+ self.note = ""
+ self.permission = ""
+ self.ipAddresses = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.note, format_data + "note")
+ PrintBasic.print_basic(self.permission, format_data + "permission")
+ PrintBasic.print_basic(self.ipAddresses, format_data + "ipAddresses")
diff --git a/huobi/model/subuser/subuser_creation.py b/huobi/model/subuser/subuser_creation.py
new file mode 100644
index 0000000..b8f6b88
--- /dev/null
+++ b/huobi/model/subuser/subuser_creation.py
@@ -0,0 +1,27 @@
+
+class SubuserCreation:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ subUid: sub user ID.
+ userState: sub user account state, states see SubUidState.
+ """
+
+ def __init__(self):
+ self.user_name = ""
+ self.note = ""
+ self.uid = 0
+ self.err_code = 0
+ self.err_message = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.user_name, format_data + "userName")
+ PrintBasic.print_basic(self.note, format_data + "note")
+ PrintBasic.print_basic(self.uid, format_data + "uid")
+ PrintBasic.print_basic(self.err_code, format_data + "errCode")
+ PrintBasic.print_basic(self.err_message, format_data + "errMessage")
+
+
+
diff --git a/huobi/model/subuser/subuser_transferability.py b/huobi/model/subuser/subuser_transferability.py
new file mode 100644
index 0000000..a327390
--- /dev/null
+++ b/huobi/model/subuser/subuser_transferability.py
@@ -0,0 +1,20 @@
+
+class SubuserTransferability:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ subUid: sub user ID.
+ userState: sub user account state, states see SubUidState.
+ """
+
+ def __init__(self):
+ self.transferrable = ""
+ self.accountType = ""
+ self.subUid = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.subUid, format_data + "subUid")
+ PrintBasic.print_basic(self.accountType, format_data + "accountType")
+ PrintBasic.print_basic(self.transferrable, format_data + "transferrable")
diff --git a/huobi/model/subuser/trade_market.py b/huobi/model/subuser/trade_market.py
new file mode 100644
index 0000000..74d22b7
--- /dev/null
+++ b/huobi/model/subuser/trade_market.py
@@ -0,0 +1,21 @@
+
+class TradeMarket:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ subUid: sub user ID.
+ accountType:
+ activation: sub user account state for given accountType.
+ """
+
+ def __init__(self):
+ self.sub_uid = ""
+ self.account_type = ""
+ self.activation = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.sub_uid, format_data + "subUid")
+ PrintBasic.print_basic(self.account_type, format_data + "accountType")
+ PrintBasic.print_basic(self.activation, format_data + "activation")
diff --git a/huobi/model/subuser/user_apikey_info.py b/huobi/model/subuser/user_apikey_info.py
new file mode 100644
index 0000000..a55c126
--- /dev/null
+++ b/huobi/model/subuser/user_apikey_info.py
@@ -0,0 +1,37 @@
+class UserApikeyInfo:
+ """
+ The trade information with price and amount etc.
+
+ :member
+ accessKey: .
+ createTime:
+ ipAddresses: .
+ note:
+ permission:
+ status:
+ updateTime:
+ validDays:
+
+ """
+
+ def __init__(self):
+ self.accessKey = ""
+ self.createTime = 0
+ self.ipAddresses = ""
+ self.note = ""
+ self.permission = ""
+ self.status = ""
+ self.updateTime = 0
+ self.validDays = -1
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+
+ PrintBasic.print_basic(self.accessKey, format_data + "accessKey")
+ PrintBasic.print_basic(self.createTime, format_data + "createTime")
+ PrintBasic.print_basic(self.ipAddresses, format_data + "ipAddresses")
+ PrintBasic.print_basic(self.note, format_data + "note")
+ PrintBasic.print_basic(self.permission, format_data + "permission")
+ PrintBasic.print_basic(self.status, format_data + "status")
+ PrintBasic.print_basic(self.updateTime, format_data + "updateTime")
+ PrintBasic.print_basic(self.validDays, format_data + "validDays")
diff --git a/huobi/model/trade/__init__.py b/huobi/model/trade/__init__.py
new file mode 100644
index 0000000..3a83d41
--- /dev/null
+++ b/huobi/model/trade/__init__.py
@@ -0,0 +1,17 @@
+from huobi.model.trade.batch_cancel_result import BatchCancelResult
+from huobi.model.trade.batch_cancel_count import BatchCancelCount
+from huobi.model.trade.feerate import FeeRate
+from huobi.model.trade.matchresult import MatchResult
+from huobi.model.trade.order import Order
+from huobi.model.trade.order_detail_req import OrderDetailReq
+from huobi.model.trade.order_list_item import OrderListItem
+from huobi.model.trade.order_list_req import OrderListReq
+from huobi.model.trade.order_update_event import OrderUpdateEvent
+from huobi.model.trade.order_update import OrderUpdate
+from huobi.model.trade.batch_create_order import BatchCreateOrder
+from huobi.model.trade.transact_feerate import TransactFeeRate
+from huobi.model.trade.trade_clearing import TradeClearing
+from huobi.model.trade.trade_clearing_event import TradeClearingEvent
+
+
+
diff --git a/huobi/model/trade/batch_cancel_count.py b/huobi/model/trade/batch_cancel_count.py
new file mode 100644
index 0000000..bbec6d0
--- /dev/null
+++ b/huobi/model/trade/batch_cancel_count.py
@@ -0,0 +1,23 @@
+
+
+
+class BatchCancelCount:
+ """
+ The result of batch cancel operation.
+
+ :member
+ success_count: The number of cancel request sent successfully.
+ failed_count: The number of cancel request failed.
+ next_id:next open order id
+ """
+
+ def __init__(self):
+ self.success_count = 0
+ self.failed_count = 0
+ self.next_id = -1
+
+ def print_object(self, format_data=""):
+ from huobi.utils import PrintBasic
+ PrintBasic.print_basic(self.success_count, format_data + "Success Count")
+ PrintBasic.print_basic(self.failed_count, format_data + "Failed Count")
+ PrintBasic.print_basic(self.next_id, format_data + "Next Open Order ID")
\ No newline at end of file
diff --git a/huobi/model/trade/batch_cancel_result.py b/huobi/model/trade/batch_cancel_result.py
new file mode 100644
index 0000000..aae02b5
--- /dev/null
+++ b/huobi/model/trade/batch_cancel_result.py
@@ -0,0 +1,17 @@
+class BatchCancelResult:
+ """
+ The result of batch cancel operation.
+
+ :member
+ success_count: The number of cancel request sent successfully.
+ failed_count: The number of cancel request failed.
+
+ """
+
+ def __init__(self):
+ self.success = []
+ self.failed = []
+
+ def print_object(self, format_data=""):
+ print("Success Order Counts", len(self.success), " Success Order Ids : ", self.success)
+ print("Fail Order Counts", len(self.failed), " Fail Order Ids : ", self.failed)
\ No newline at end of file
diff --git a/huobi/model/trade/batch_create_order.py b/huobi/model/trade/batch_create_order.py
new file mode 100644
index 0000000..dc36274
--- /dev/null
+++ b/huobi/model/trade/batch_create_order.py
@@ -0,0 +1,23 @@
+class BatchCreateOrder:
+ """
+ batch create order result
+
+ :member
+ order_id: The transfer id.
+ client_order_id: The crypto currency to deposit.
+ err_code: The on-chain transaction hash.
+ err_msg: The number of crypto asset transferred in its minimum unit.
+
+ """
+ def __init__(self):
+ self.order_id = 0
+ self.client_order_id = ""
+ self.err_code = ""
+ self.err_msg = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils import PrintBasic
+ PrintBasic.print_basic(self.order_id, format_data + "Order Id")
+ PrintBasic.print_basic(self.client_order_id, format_data + "Client Order Id")
+ PrintBasic.print_basic(self.err_code, format_data + "Error Code")
+ PrintBasic.print_basic(self.err_msg, format_data + "Error Message")
\ No newline at end of file
diff --git a/huobi/model/trade/feerate.py b/huobi/model/trade/feerate.py
new file mode 100644
index 0000000..62d0db9
--- /dev/null
+++ b/huobi/model/trade/feerate.py
@@ -0,0 +1,22 @@
+
+class FeeRate:
+ """
+ The account information for spot account, margin account etc.
+
+ :member
+ symbol: The symbol, like "btcusdt".
+ maker_fee: maker fee rate
+ taker_fee: taker fee rate
+
+ """
+
+ def __init__(self):
+ self.symbol = ""
+ self.maker_fee = ""
+ self.taker_fee = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.maker_fee, format_data + "Maker Fee")
+ PrintBasic.print_basic(self.taker_fee, format_data + "Taker Fee")
\ No newline at end of file
diff --git a/huobi/model/trade/matchresult.py b/huobi/model/trade/matchresult.py
new file mode 100644
index 0000000..26f42a6
--- /dev/null
+++ b/huobi/model/trade/matchresult.py
@@ -0,0 +1,58 @@
+from huobi.constant import *
+
+
+class MatchResult:
+ """
+ The match result information.
+
+ :member
+ created_timestamp: The UNIX formatted timestamp in UTC when the match and fill is done.
+ filled_amount: The amount which has been filled.
+ filled_fees: The transaction fee paid so far.
+ id: The internal id.
+ match_id: The match id of this match.
+ order_id: The order id of this order.
+ price: The limit price of limit order.
+ source: The source where the order was triggered, possible values: sys, web, api, app.
+ symbol: The symbol, like "btcusdt".
+ type: The order type, possible values are: buy-market, sell-market, buy-limit, sell-limit,
+ buy-ioc, sell-ioc, buy-limit-maker, sell-limit-maker, buy-limit-fok, sell-limit-fok, buy-stop-limit-fok, sell-stop-limit-fok.
+ filled_points: deduct points
+ fee_deduct_currency: deduct type, it means deduct from HT/ HT points / or other currency
+ fee_currency:
+ """
+
+ def __init__(self):
+ self.created_at = 0
+ self.filled_amount = 0.0
+ self.filled_fees = 0.0
+ self.id = 0
+ self.match_id = 0
+ self.order_id = 0
+ self.price = 0.0
+ self.source = OrderSource.INVALID
+ self.symbol = ""
+ self.type = OrderType.INVALID
+ self.role = ""
+ self.filled_points = ""
+ self.fee_deduct_currency = ""
+ self.fee_currency = ""
+ self.fee_deduct_state = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.created_at, format_data + "Create Time")
+ PrintBasic.print_basic(self.filled_amount, format_data + "Fill Amount")
+ PrintBasic.print_basic(self.filled_fees, format_data + "Fill Fee")
+ PrintBasic.print_basic(self.filled_points, format_data + "Fill Points")
+ PrintBasic.print_basic(self.match_id, format_data + "Match ID")
+ PrintBasic.print_basic(self.order_id, format_data + "Order ID")
+ PrintBasic.print_basic(self.price, format_data + "Price")
+ PrintBasic.print_basic(self.source, format_data + "Source")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.type, format_data + "Order Type")
+ PrintBasic.print_basic(self.role, format_data + "Role")
+ PrintBasic.print_basic(self.fee_deduct_currency, format_data + "Fee Deduct Currency")
+ PrintBasic.print_basic(self.fee_currency, format_data + "Fee Currency")
+ PrintBasic.print_basic(self.fee_deduct_state, format_data + "Fee Deduct State")
diff --git a/huobi/model/trade/order.py b/huobi/model/trade/order.py
new file mode 100644
index 0000000..00df78b
--- /dev/null
+++ b/huobi/model/trade/order.py
@@ -0,0 +1,86 @@
+from huobi.constant import *
+from huobi.utils.json_parser import fill_obj
+
+
+class Order:
+ """
+ The detail order information.
+
+ :member
+ amount: The amount of base currency in this order.
+ price: The limit price of limit order.
+ created_timestamp: The UNIX formatted timestamp in UTC when the order was created.
+ canceled_timestamp: The UNIX formatted timestamp in UTC when the order was canceled, if not canceled then has value of 0.
+ finished_timestamp: The UNIX formatted timestamp in UTC when the order was changed to a final state. This is not the time the order is matched.
+ order_id: The order id.
+ symbol: The symbol, like "btcusdt".
+ order_type: The order type, possible values are: buy-market, sell-market, buy-limit, sell-limit, buy-ioc, sell-ioc, buy-limit-maker, sell-limit-maker, buy-limit-fok, sell-limit-fok, buy-stop-limit-fok, sell-stop-limit-fok.
+ filled_amount: The amount which has been filled.
+ filled_cash_amount: The filled total in quote currency.
+ filled_fees: The transaction fee paid so far.
+ source: The source where the order was triggered, possible values: sys, web, api, app.
+ state: The order state: submitted, partial-filled, cancelling, filled, canceled.
+ stop_price : stop price used for buy-stop-limit,sell-stop-limit
+ operator : only [gte] and [lte] to trigger buy-stop-limit,sell-stop-limit
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.symbol = ""
+ self.account_id = 0
+ self.amount = 0.0
+ self.price = 0.0
+ self.created_at = 0
+ self.canceled_at = 0
+ self.finished_at = 0
+ self.type = OrderType.INVALID
+ self.filled_amount = 0.0
+ self.filled_cash_amount = 0.0
+ self.filled_fees = 0.0
+ self.source = OrderSource.INVALID
+ self.state = OrderState.INVALID
+ self.client_order_id = ""
+ self.stop_price = ""
+ self.next_time = 0
+ self.operator=""
+
+ @staticmethod
+ def json_parse(json_data):
+ order = fill_obj(json_data, Order)
+ order.filled_amount = json_data.get("filled-amount", json_data.get("field-amount", 0))
+ order.filled_cash_amount = json_data.get("filled-cash-amount", json_data.get("field-cash-amount", 0))
+ order.filled_fees = json_data.get("filled-fees", json_data.get("field-fees", 0))
+ return order
+
+ @staticmethod
+ def json_parse_list(json_data):
+ if json_data and len(json_data):
+ order_list = list()
+ for idx, row in enumerate(json_data):
+ order_item = Order.json_parse(row)
+ order_list.append(order_item)
+ return order_list
+
+ return list()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "Order Id")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.price, format_data + "Price")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.created_at, format_data + "Create Time")
+ PrintBasic.print_basic(self.canceled_at, format_data + "Cancel Time")
+ PrintBasic.print_basic(self.finished_at, format_data + "Finish Time")
+ PrintBasic.print_basic(self.type, format_data + "Order Type")
+ PrintBasic.print_basic(self.filled_amount, format_data + "Filled Amount")
+ PrintBasic.print_basic(self.filled_cash_amount, format_data + "Filled Cash Amount")
+ PrintBasic.print_basic(self.filled_fees, format_data + "Filled Fees")
+ #PrintBasic.print_basic(self.account_type, format_data + "Account Type")
+ PrintBasic.print_basic(self.source, format_data + "Order Source")
+ PrintBasic.print_basic(self.state, format_data + "Order State")
+ PrintBasic.print_basic(self.client_order_id, format_data + "Client Order Id")
+ PrintBasic.print_basic(self.stop_price, format_data + "Stop Price")
+ PrintBasic.print_basic(self.operator, format_data + "Operator")
+ PrintBasic.print_basic(self.next_time, format_data + "Next Time")
+
diff --git a/huobi/model/trade/order_detail_req.py b/huobi/model/trade/order_detail_req.py
new file mode 100644
index 0000000..4b4ac09
--- /dev/null
+++ b/huobi/model/trade/order_detail_req.py
@@ -0,0 +1,29 @@
+from huobi.model.trade.order_list_item import OrderListItem
+
+
+class OrderDetailReq:
+ """
+ The order update received by subscription of order update.
+
+ :member
+ symbol: The symbol you subscribed.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ topic : request topic
+ client_req_id : client request id
+ data: The order detail.
+
+ """
+
+ def __init__(self):
+ self.ts = 0
+ self.topic = ""
+ self.cid = ""
+ self.data = OrderListItem()
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ts, format_data + "Timestamp")
+ PrintBasic.print_basic(self.cid, format_data + "Client Req ID")
+ PrintBasic.print_basic(self.topic, format_data + "Topic")
+ self.data.print_object("\t")
\ No newline at end of file
diff --git a/huobi/model/trade/order_list_item.py b/huobi/model/trade/order_list_item.py
new file mode 100644
index 0000000..c790d0a
--- /dev/null
+++ b/huobi/model/trade/order_list_item.py
@@ -0,0 +1,55 @@
+
+from huobi.constant import *
+
+class OrderListItem:
+ """
+ The order update received by request of order list.
+
+ :member
+ symbol: The symbol you subscribed.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ topic: request topic
+ client_req_id: client request ID
+ order_list : order list
+
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.symbol = ""
+ self.account_id = 0
+ self.amount = 0.0
+ self.price = 0.0
+ self.created_at = 0
+ self.type = OrderType.INVALID
+ self.finished_at = 0
+ self.source = OrderSource.INVALID
+ self.state = OrderState.INVALID
+ self.canceled_at = 0
+ self.filled_amount = 0.0
+ self.filled_cash_amount = 0.0
+ self.filled_fees = 0.0
+ self.stop_price = 0.0
+ self.operator = ""
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.account_id, format_data + "Account Id")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.price, format_data + "Price")
+ PrintBasic.print_basic(self.created_at, format_data + "Create Time")
+ PrintBasic.print_basic(self.type, format_data + "Order Type")
+ PrintBasic.print_basic(self.finished_at, format_data + "Finish Time")
+ PrintBasic.print_basic(self.source, format_data + "Order Source")
+ PrintBasic.print_basic(self.state, format_data + "Order State")
+ PrintBasic.print_basic(self.canceled_at, format_data + "Cancel Time")
+ PrintBasic.print_basic(self.filled_amount, format_data + "Filled Amount")
+ PrintBasic.print_basic(self.filled_cash_amount, format_data + "Filled Cash Amount")
+ PrintBasic.print_basic(self.filled_fees, format_data + "Filled Fees")
+ PrintBasic.print_basic(self.stop_price, format_data + "Stop Price")
+ PrintBasic.print_basic(self.operator, format_data + "Operator")
+
+
diff --git a/huobi/model/trade/order_list_req.py b/huobi/model/trade/order_list_req.py
new file mode 100644
index 0000000..dddf0b0
--- /dev/null
+++ b/huobi/model/trade/order_list_req.py
@@ -0,0 +1,29 @@
+
+class OrderListReq:
+ """
+ The order update received by request of order list.
+
+ :member
+ symbol: The symbol you subscribed.
+ timestamp: The UNIX formatted timestamp generated by server in UTC.
+ topic: request topic
+ data : OrderListItem
+
+ """
+
+ def __init__(self):
+ self.ts = 0
+ self.topic = ""
+ self.data = list()
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ts, format_data + "Timestamp")
+ PrintBasic.print_basic(self.topic, format_data + "Channel")
+ print("Order List as below : count " + str(len(self.data)))
+ if len(self.data):
+ for orderlistitem_obj in self.data:
+ orderlistitem_obj.print_object("\t ")
+ print()
+
diff --git a/huobi/model/trade/order_update.py b/huobi/model/trade/order_update.py
new file mode 100644
index 0000000..fdbf747
--- /dev/null
+++ b/huobi/model/trade/order_update.py
@@ -0,0 +1,56 @@
+from huobi.constant import *
+
+
+class OrderUpdate:
+ """
+ The detail order information.
+
+ :member
+ orderId: The order id.
+ tradePrice: trade price
+ tradeVolume: trade volume
+ tradeId: Id record for trade
+ tradeTime: trade timestamp (ms)
+ aggressor: true (taker), false (maker)
+ remainAmt: Remaining amount (for buy-market order it's remaining value)
+ orderStatus: Order status, valid value: partial-filled, filled
+ clientOrderId: Client order ID (if any)
+ eventType: Event type, valid value: trade
+ symbol: The symbol, like "btcusdt".
+ type: The order type, possible values are: buy-market, sell-market, buy-limit, sell-limit, buy-ioc, sell-ioc, buy-limit-maker, sell-limit-maker, buy-limit-fok, sell-limit-fok.
+ """
+
+ def __init__(self):
+ self.orderId = 0
+ self.tradePrice = ""
+ self.tradeVolume = ""
+ self.tradeId = 0
+ self.tradeTime = 0
+ self.aggressor = False
+ self.remainAmt = ""
+ self.orderStatus = OrderState.INVALID
+ self.clientOrderId = ""
+ self.eventType = ""
+ self.symbol = ""
+ self.type = OrderType.INVALID
+ self.accountId = 0
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.orderId, format_data + "Order Id")
+ PrintBasic.print_basic(self.tradePrice, format_data + "Trade Price")
+ PrintBasic.print_basic(self.tradeVolume, format_data + "Trade Volume")
+ PrintBasic.print_basic(self.tradeId, format_data + "Trade Id")
+ PrintBasic.print_basic(self.tradeTime, format_data + "Trade Timestamp")
+ PrintBasic.print_basic(self.aggressor, format_data + "is Taker")
+ PrintBasic.print_basic(self.orderStatus, format_data + "Order State")
+ PrintBasic.print_basic(self.clientOrderId, format_data + "Client Order Id")
+ PrintBasic.print_basic(self.eventType, format_data + "Event Type")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.type, format_data + "Order Type")
+ PrintBasic.print_basic(self.accountId, format_data + "Account Id")
+
+
+
+
diff --git a/huobi/model/trade/order_update_event.py b/huobi/model/trade/order_update_event.py
new file mode 100644
index 0000000..8399ec3
--- /dev/null
+++ b/huobi/model/trade/order_update_event.py
@@ -0,0 +1,23 @@
+from huobi.model.trade.order_update import OrderUpdate
+
+
+class OrderUpdateEvent:
+ """
+ The order update received by subscription of order update.
+
+ :member
+ ch: The symbol you subscribed.
+ data: The order detail.
+
+ """
+
+ def __init__(self):
+ self.ch = ""
+ self.data = OrderUpdate()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Topic")
+
+ orderupdate = self.data
+ orderupdate.print_object()
\ No newline at end of file
diff --git a/huobi/model/trade/trade_clearing.py b/huobi/model/trade/trade_clearing.py
new file mode 100644
index 0000000..d1f1726
--- /dev/null
+++ b/huobi/model/trade/trade_clearing.py
@@ -0,0 +1,60 @@
+from huobi.constant import *
+from huobi.utils.json_parser import fill_obj, default_parse_list_dict
+
+
+class TradeClearing:
+ """
+ The detail order information.
+
+ :member
+ order_id: The order id.
+ symbol: The symbol, like "btcusdt".
+ tradePrice: trade price.
+ tradeVolume: trade Volume.
+ orderSide: order Side, more to see OrderSide
+ orderType: order type, more to see OrderType
+ aggressor: is aggressor, only true or false
+ tradeId: trade ID.
+ tradeTime: trade Time.
+ transactFee: transact Fee.
+ feeDeduct: Deduct Fee.
+ feeDeductType: fee Deduct Type, current only support ht and point
+ """
+
+ def __init__(self):
+ self.symbol = ""
+ self.orderId = 0
+ self.tradePrice = ""
+ self.tradeVolume = ""
+ self.orderSide = OrderSide.INVALID
+ self.orderType = OrderType.INVALID
+ self.aggressor = False
+ self.tradeId = 0
+ self.tradeTime = 0
+ self.transactFee = ""
+ self.feeDeduct = ""
+ self.feeDeductType = FeeDeductType.INVALID
+
+ @staticmethod
+ def json_parse(json_data):
+ if json_data.get("orderId", None):
+ return default_parse_list_dict(json_data, TradeClearing)
+ else:
+ return TradeClearing()
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.orderId, format_data + "Order Id")
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.tradePrice, format_data + "Trade Price")
+ PrintBasic.print_basic(self.tradeVolume, format_data + "Trade Volume")
+ PrintBasic.print_basic(self.orderSide, format_data + "Order Side")
+ PrintBasic.print_basic(self.orderType, format_data + "Order Type")
+ PrintBasic.print_basic(self.aggressor, format_data + "is Taker")
+ PrintBasic.print_basic(self.tradeId, format_data + "Trade Id")
+ PrintBasic.print_basic(self.tradeTime, format_data + "Trade Time")
+ PrintBasic.print_basic(self.transactFee, format_data + "Transact Fee")
+ PrintBasic.print_basic(self.feeDeduct, format_data + "Fee Deduct")
+ PrintBasic.print_basic(self.feeDeductType, format_data + "Fee Deduct Type")
+
+
diff --git a/huobi/model/trade/trade_clearing_event.py b/huobi/model/trade/trade_clearing_event.py
new file mode 100644
index 0000000..adb64b6
--- /dev/null
+++ b/huobi/model/trade/trade_clearing_event.py
@@ -0,0 +1,32 @@
+from huobi.model.trade import TradeClearing
+
+
+class TradeClearingEvent:
+ """
+ subscribe trading clearing information
+
+ :member
+ action: current is "sub" for subscribe
+ ch: subscribe topic.
+ data: data detail in TradeClearing.
+ """
+
+ def __init__(self):
+ self.action = ""
+ self.ch = ""
+ self.seq = 0
+ self.data = TradeClearing()
+
+ @staticmethod
+ def json_parse(data_json):
+ event_obj = TradeClearingEvent()
+ event_obj.action = data_json.get("action")
+ event_obj.ch = data_json.get("ch")
+ event_obj.seq = data_json.get("seq", 0)
+ event_obj.data = TradeClearing.json_parse(data_json.get("data", {}))
+ return event_obj
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.ch, format_data + "Channel")
+ self.data.print_object()
diff --git a/huobi/model/trade/transact_feerate.py b/huobi/model/trade/transact_feerate.py
new file mode 100644
index 0000000..67c0be5
--- /dev/null
+++ b/huobi/model/trade/transact_feerate.py
@@ -0,0 +1,27 @@
+
+class TransactFeeRate:
+ """
+ The transact fee rate.
+
+ :member
+ symbol: symbol like "btcusdt"
+ makerFeeRate: maker fee rate
+ takerFeeRate: taker fee rate
+ actualMakerRate: actual maker fee rate
+ actualTakerRate: actual taker fee rate
+ """
+
+ def __init__(self):
+ self.symbol = ""
+ self.makerFeeRate = ""
+ self.takerFeeRate = ""
+ self.actualMakerRate = ""
+ self.actualTakerRate = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.symbol, format_data + "Symbol")
+ PrintBasic.print_basic(self.makerFeeRate, format_data + "makerFeeRate")
+ PrintBasic.print_basic(self.takerFeeRate, format_data + "takerFeeRate")
+ PrintBasic.print_basic(self.actualMakerRate, format_data + "actualMakerRate")
+ PrintBasic.print_basic(self.actualTakerRate, format_data + "actualTakerRate")
\ No newline at end of file
diff --git a/huobi/model/wallet/__init__.py b/huobi/model/wallet/__init__.py
new file mode 100644
index 0000000..f351a02
--- /dev/null
+++ b/huobi/model/wallet/__init__.py
@@ -0,0 +1,7 @@
+from huobi.model.wallet.deposit import Deposit
+from huobi.model.wallet.withdraw import Withdraw
+from huobi.model.wallet.chain_deposit_address import ChainDepositAddress
+from huobi.model.wallet.chain_withdraw_address import ChainWithdrawAddress
+from huobi.model.wallet.withdraw_quota import WithdrawQuota
+from huobi.model.wallet.deposit_history import DepositHistory
+from huobi.model.wallet.deposit_history_item import DepositHistoryItem
diff --git a/huobi/model/wallet/chain_deposit_address.py b/huobi/model/wallet/chain_deposit_address.py
new file mode 100644
index 0000000..b2f6ce0
--- /dev/null
+++ b/huobi/model/wallet/chain_deposit_address.py
@@ -0,0 +1,25 @@
+
+class ChainDepositAddress:
+ """
+ The deposit address.
+
+ :member
+ currency: The crypto currency to deposit.
+ address: Deposit address
+ addressTag: Deposit address tag.
+ chain: Block chain name.
+ """
+ def __init__(self):
+ self.currency = ""
+ self.address = ""
+ self.addressTag = ""
+ self.chain = ""
+
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.address, format_data + "Address")
+ PrintBasic.print_basic(self.addressTag, format_data + "addressTag")
+ PrintBasic.print_basic(self.chain, format_data + "Chain")
diff --git a/huobi/model/wallet/chain_withdraw_address.py b/huobi/model/wallet/chain_withdraw_address.py
new file mode 100644
index 0000000..ff16357
--- /dev/null
+++ b/huobi/model/wallet/chain_withdraw_address.py
@@ -0,0 +1,25 @@
+class ChainWithdrawAddress:
+ """
+ The deposit address.
+
+ :member
+ currency: The crypto currency to deposit.
+ address: Deposit address
+ addressTag: Deposit address tag.
+ chain: Block chain name.
+ """
+
+ def __init__(self):
+ self.currency = ""
+ self.address = ""
+ self.addressTag = ""
+ self.chain = ""
+ self.note = ""
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.address, format_data + "Address")
+ PrintBasic.print_basic(self.addressTag, format_data + "addressTag")
+ PrintBasic.print_basic(self.chain, format_data + "Chain")
+ PrintBasic.print_basic(self.note, format_data + "Note")
diff --git a/huobi/model/wallet/deposit.py b/huobi/model/wallet/deposit.py
new file mode 100644
index 0000000..fc6471a
--- /dev/null
+++ b/huobi/model/wallet/deposit.py
@@ -0,0 +1,48 @@
+from huobi.constant import *
+
+
+class Deposit:
+ """
+ The latest status for deposits
+
+ :member
+ id: The transfer id.
+ currency: The crypto currency to deposit.
+ tx_hash: The on-chain transaction hash.
+ amount: The number of crypto asset transferred in its minimum unit.
+ address: The deposit source address.
+ address_tag: The user defined address tag.
+ fee: The amount of fee taken by Huobi in this crypto's minimum unit.
+ created_at: The UNIX formatted timestamp in UTC for the transfer creation.
+ updated_at: The UNIX formatted timestamp in UTC for the transfer's latest update.
+ state: The deposit state of this transfer.
+ """
+
+ def __init__(self):
+ self.id = 0
+ self.type = DepositWithdraw.DEPOSIT
+ self.currency = ""
+ self.tx_hash = ""
+ self.amount = 0.0
+ self.chain = ""
+ self.address = ""
+ self.address_tag = ""
+ self.fee = 0.0
+ self.created_at = 0
+ self.updated_at = 0
+ self.state = DepositState.INVALID
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.type, format_data + "Operate Type")
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.chain, format_data + "Chain")
+ PrintBasic.print_basic(self.tx_hash, format_data + "Trade Hash")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.address, format_data + "Address")
+ PrintBasic.print_basic(self.address_tag, format_data + "Address Tag")
+ PrintBasic.print_basic(self.fee, format_data + "Fee")
+ PrintBasic.print_basic(self.state, format_data + "Deposit State")
+ PrintBasic.print_basic(self.created_at, format_data + "Create Time")
+ PrintBasic.print_basic(self.updated_at, format_data + "Update Time")
diff --git a/huobi/model/wallet/deposit_history.py b/huobi/model/wallet/deposit_history.py
new file mode 100644
index 0000000..7ef4ac8
--- /dev/null
+++ b/huobi/model/wallet/deposit_history.py
@@ -0,0 +1,21 @@
+from huobi.model.wallet.deposit_history_item import DepositHistoryItem
+class DepositHistory:
+ """
+ The deposit history
+
+ :member
+ nextId: next id.
+ data: history list.
+ """
+
+ def __init__(self):
+ self.data = list()
+ self.nextId = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.nextId, format_data + "NextId")
+ if self.data and len(self.data):
+ for item in self.data:
+ item.print_object()
+ PrintBasic.print_basic("", format_data + "")
diff --git a/huobi/model/wallet/deposit_history_item.py b/huobi/model/wallet/deposit_history_item.py
new file mode 100644
index 0000000..9215c9d
--- /dev/null
+++ b/huobi/model/wallet/deposit_history_item.py
@@ -0,0 +1,42 @@
+from huobi.constant import *
+
+
+class DepositHistoryItem:
+ """
+ The deposit history
+
+ :member
+ id: The transfer id.
+ currency: The crypto currency to deposit.
+ txHash: The on-chain transaction hash.
+ amount: The number of crypto asset transferred in its minimum unit.
+ address: The deposit source address.
+ addressTag: The user defined address tag.
+ deposit_state: The deposit state of this transfer.
+ created_timestamp: The UNIX formatted timestamp in UTC for the transfer creation.
+ updated_timestamp: The UNIX formatted timestamp in UTC for the transfer's latest update.
+ """
+ def __init__(self):
+ self.id = 0
+ self.currency = ""
+ self.txHash = ""
+ self.chain = ""
+ self.amount = 0.0
+ self.address = ""
+ self.addressTag = ""
+ self.deposit_state = WithdrawState.INVALID
+ self.created_timestamp = 0
+ self.updated_timestamp = 0
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.chain, format_data + "Chain")
+ PrintBasic.print_basic(self.txHash, format_data + "Trade Hash")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.address, format_data + "Address")
+ PrintBasic.print_basic(self.addressTag, format_data + "Address Tag")
+ PrintBasic.print_basic(self.deposit_state, format_data + "Deposit State")
+ PrintBasic.print_basic(self.created_timestamp, format_data + "Create Time")
+ PrintBasic.print_basic(self.updated_timestamp, format_data + "Update Time")
diff --git a/huobi/model/wallet/withdraw.py b/huobi/model/wallet/withdraw.py
new file mode 100644
index 0000000..24a69d9
--- /dev/null
+++ b/huobi/model/wallet/withdraw.py
@@ -0,0 +1,48 @@
+from huobi.constant import *
+
+
+class Withdraw:
+ """
+ The latest status for withdraws.
+
+ :member
+ id: The transfer id.
+ currency: The crypto currency to deposit.
+ tx_hash: The on-chain transaction hash.
+ amount: The number of crypto asset transferred in its minimum unit.
+ address: The deposit source address.
+ address_tag: The user defined address tag.
+ fee: The amount of fee taken by Huobi in this crypto's minimum unit.
+ created_at: The UNIX formatted timestamp in UTC for the transfer creation.
+ updated_at: The UNIX formatted timestamp in UTC for the transfer's latest update.
+ state: The withdraw state of this transfer.
+ """
+ def __init__(self):
+ self.id = 0
+ self.type = DepositWithdraw.WITHDRAW
+ self.currency = ""
+ self.chain = ""
+ self.tx_hash = ""
+ self.amount = 0.0
+ self.address = ""
+ self.address_tag = ""
+ self.fee = 0.0
+ self.created_at = 0
+ self.updated_at = 0
+ self.state = WithdrawState.INVALID
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.id, format_data + "ID")
+ PrintBasic.print_basic(self.currency, format_data + "Currency")
+ PrintBasic.print_basic(self.type, format_data + "Operator Type")
+ PrintBasic.print_basic(self.chain, format_data + "Chain")
+ PrintBasic.print_basic(self.tx_hash, format_data + "Trade Hash")
+ PrintBasic.print_basic(self.amount, format_data + "Amount")
+ PrintBasic.print_basic(self.address, format_data + "Address")
+ PrintBasic.print_basic(self.address_tag, format_data + "Address Tag")
+ PrintBasic.print_basic(self.fee, format_data + "Fee")
+ PrintBasic.print_basic(self.state, format_data + "Withdraw State")
+ PrintBasic.print_basic(self.created_at, format_data + "Create Time")
+ PrintBasic.print_basic(self.updated_at, format_data + "Update Time")
\ No newline at end of file
diff --git a/huobi/model/wallet/withdraw_quota.py b/huobi/model/wallet/withdraw_quota.py
new file mode 100644
index 0000000..376cc47
--- /dev/null
+++ b/huobi/model/wallet/withdraw_quota.py
@@ -0,0 +1,37 @@
+
+class WithdrawQuota:
+ """
+ Withdraw Quota info.
+
+ :member
+ chain: Block chain name.
+ maxWithdrawAmt: Maximum withdraw amount in each request.
+ withdrawQuotaPerDay: Maximum withdraw amount in a day
+ remainWithdrawQuotaPerDay: Remaining withdraw quota in the day
+ withdrawQuotaPerYear: Maximum withdraw amount in a year
+ remainWithdrawQuotaPerYear: Remaining withdraw quota in the year
+ withdrawQuotaTotal: Maximum withdraw amount in total
+ remainWithdrawQuotaTotal: Remaining withdraw quota in total
+ """
+ def __init__(self):
+ self.chain = ""
+ self.maxWithdrawAmt = ""
+ self.withdrawQuotaPerDay = ""
+ self.remainWithdrawQuotaPerDay = ""
+ self.withdrawQuotaPerYear = ""
+ self.remainWithdrawQuotaPerYear = ""
+ self.withdrawQuotaTotal = ""
+ self.remainWithdrawQuotaTotal = ""
+
+
+
+ def print_object(self, format_data=""):
+ from huobi.utils.print_mix_object import PrintBasic
+ PrintBasic.print_basic(self.chain, format_data + "Chain")
+ PrintBasic.print_basic(self.maxWithdrawAmt, format_data + "maxWithdrawAmt")
+ PrintBasic.print_basic(self.withdrawQuotaPerDay, format_data + "withdrawQuotaPerDay")
+ PrintBasic.print_basic(self.remainWithdrawQuotaPerDay, format_data + "remainWithdrawQuotaPerDay")
+ PrintBasic.print_basic(self.withdrawQuotaPerYear, format_data + "withdrawQuotaPerYear")
+ PrintBasic.print_basic(self.remainWithdrawQuotaPerYear, format_data + "remainWithdrawQuotaPerYear")
+ PrintBasic.print_basic(self.withdrawQuotaTotal, format_data + "withdrawQuotaTotal")
+ PrintBasic.print_basic(self.remainWithdrawQuotaTotal, format_data + "remainWithdrawQuotaTotal")
diff --git a/huobi/service/__init__.py b/huobi/service/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/account/__init__.py b/huobi/service/account/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/account/error_code.md b/huobi/service/account/error_code.md
new file mode 100644
index 0000000..99e4095
--- /dev/null
+++ b/huobi/service/account/error_code.md
@@ -0,0 +1,13 @@
+常见错误码
+以下是账户相关接口返回的错误码、错误消息以及说明。
+
+错误码 错误消息 说明
+500 system error 调用内部服务异常
+1002 forbidden 禁止操作,如用户入参中accountId与UID不一致
+2002 "invalid field value in currency" currency不符合正则规则^[a-z0-9]{2,10}$
+2002 "invalid field value in transactTypes" 变动类型transactTypes不是“transfer”
+2002 "invalid field value in sort" 分页请求参数不是合法的"asc或desc"
+2002 "value in fromId is not found in record” 未找到fromId
+2002 "invalid field value in accountId" 查询参数中accountId为空
+2002 "value in startTime exceeded valid range" 入参查询时间大于当前时间,或者距离当前时间超过180天
+2002 "value in endTime exceeded valid range") 查询结束时间小于开始时间,或者查询时间跨度大于10天
\ No newline at end of file
diff --git a/huobi/service/account/get_account_asset_valuation.py b/huobi/service/account/get_account_asset_valuation.py
new file mode 100644
index 0000000..6162c27
--- /dev/null
+++ b/huobi/service/account/get_account_asset_valuation.py
@@ -0,0 +1,38 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account.account_asset_valuation import AccountAssetValuationResult
+from huobi.utils import *
+'''
+获取账户资产估值
+API Key 权限:读取
+
+限频值(NEW):100次/2s
+
+按照BTC或法币计价单位,获取指定账户的总资产估值。
+请求参数:
+accountType 账户类型 spot:现货账户, margin:逐仓杠杆账户,otc:OTC 账户,super-margin:全仓杠杆账户
+valuationCurrency 资产估值法币,即资产按哪个法币为单位进行估值。 可选法币有:BTC、CNY、USD、JPY、KRW、GBP、TRY、EUR、RUB、VND、HKD、TWD、MYR、SGD、AED、SAR (大小写敏感)
+subUid 子用户的 UID,若不填,则返回API key所属用户的账户资产估值
+{
+ "code": 200,
+ "data": {
+ "balance": "34.75", 按照某一个法币为单位的总资产估值
+ "timestamp": 1594901254363 数据返回时间,为unix time in millisecond
+ },
+ "ok": true
+}
+'''
+
+class GetAccountAssetValuationService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/account/asset-valuation"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse(data, AccountAssetValuationResult, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/account/get_account_balance_by_subuid.py b/huobi/service/account/get_account_balance_by_subuid.py
new file mode 100644
index 0000000..2953207
--- /dev/null
+++ b/huobi/service/account/get_account_balance_by_subuid.py
@@ -0,0 +1,22 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+
+
+class GetAccountBalanceBySubUidService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ sub_uid = self.params["sub-uid"]
+
+ def get_channel():
+ path = "/v1/account/accounts/{}"
+ return path.format(sub_uid)
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return AccountBalance.json_parse_list(data_list)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, get_channel(), self.params, parse)
diff --git a/huobi/service/account/get_account_history.py b/huobi/service/account/get_account_history.py
new file mode 100644
index 0000000..213c39e
--- /dev/null
+++ b/huobi/service/account/get_account_history.py
@@ -0,0 +1,30 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#0d3c2e7382
+账户流水
+API Key 权限:读取
+限频值(NEW):5次/2s
+
+该节点基于用户账户ID返回账户流水。
+
+'''
+
+class GetAccountHistoryService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/account/history"
+
+ def parse(dict_data):
+ response = dict()
+ data_list = dict_data.get("data", [])
+ response['data'] = default_parse_list_dict(data_list, AccountHistory, [])
+ response['next_id'] = dict_data.get("next-id", 0)
+ return response
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/account/get_account_ledger.py b/huobi/service/account/get_account_ledger.py
new file mode 100644
index 0000000..2802131
--- /dev/null
+++ b/huobi/service/account/get_account_ledger.py
@@ -0,0 +1,36 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#84f1b5486d
+财务流水
+API Key 权限:读取
+
+该节点基于用户账户ID返回财务流水。
+一期上线暂时仅支持划转流水的查询(“transactType” = “transfer”)。
+通过“startTime”/“endTime”框定的查询窗口最大为10天,意即,通过单次查询可检索的范围最大为10天。
+该查询窗口可在最近180天范围内平移,意即,通过多次平移窗口查询,最多可检索到过往180天的记录。
+
+'''
+
+
+class GetAccountLedgerService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/account/ledger"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, AccountLedger, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/account/get_account_point.py b/huobi/service/account/get_account_point.py
new file mode 100644
index 0000000..b2d03ad
--- /dev/null
+++ b/huobi/service/account/get_account_point.py
@@ -0,0 +1,19 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+
+
+class GetAccountPointService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/point/account"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse(data, AccountPointResult, {})
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/account/get_accounts.py b/huobi/service/account/get_accounts.py
new file mode 100644
index 0000000..51572a2
--- /dev/null
+++ b/huobi/service/account/get_accounts.py
@@ -0,0 +1,50 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#555911a809
+账户信息
+API Key 权限:读取
+限频值(NEW):100次/2s
+响应数据
+
+
+{
+ "data": [
+ {
+ "id": 100001, account-id
+ "type": "spot", 账户类型 spot:现货账户, margin:逐仓杠杆账户, otc:OTC 账户, point:点卡账户, super-margin:全仓杠杆账户, investment: C2C杠杆借出账户, borrow: C2C杠杆借入账户,矿池账户: minepool, ETF账户: etf, 抵押借贷账户: crypto-loans
+ "subtype": "", 子账户类型(仅对逐仓杠杆账户有效) 逐仓杠杆交易标的,例如btcusdt
+ "state": "working" 账户状态 working:正常, lock:账户被锁定
+ }
+ {
+ "id": 100002,
+ "type": "margin",
+ "subtype": "btcusdt",
+ "state": "working"
+ },
+ {
+ "id": 100003,
+ "type": "otc",
+ "subtype": "",
+ "state": "working"
+ }
+ ]
+}
+逐仓/全仓/C2C杠杆账户(margin/super-margin/borrow)会在第一次划转资产时创建,如果未划转过资产则不会有杠杆账户。
+'''
+
+class GetAccountsService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/account/accounts"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, Account, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/account/get_aggregate_subuser_balance.py b/huobi/service/account/get_aggregate_subuser_balance.py
new file mode 100644
index 0000000..c8610e2
--- /dev/null
+++ b/huobi/service/account/get_aggregate_subuser_balance.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+
+
+
+class GetAggregateSubUserBalanceService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/subuser/aggregate-balance"
+
+ def parse(dict_data):
+ data = dict_data.get("data", [])
+ return default_parse_list_dict(data, Balance)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/account/get_balance.py b/huobi/service/account/get_balance.py
new file mode 100644
index 0000000..f4f2678
--- /dev/null
+++ b/huobi/service/account/get_balance.py
@@ -0,0 +1,66 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#bd9157656f
+账户余额
+API Key 权限:读取
+限频值(NEW):100次/2s
+查询指定账户的余额,支持以下账户:
+
+spot:现货账户, margin:逐仓杠杆账户,otc:OTC 账户,point:点卡账户,super-margin:全仓杠杆账户, investment: C2C杠杆借出账户, borrow: C2C杠杆借入账户
+响应数据
+{
+ "data": {
+ "id": 100009, 账户 ID
+ "type": "spot", 账户类型 pot:现货账户, margin:逐仓杠杆账户, otc:OTC 账户, point:点卡账户, super-margin:全仓杠杆账户, investment: C2C杠杆借出账户, borrow: C2C杠杆借入账户,矿池账户: minepool, ETF账户: etf, 抵押借贷账户: crypto-loans
+ "state": "working", 账户状态 working:正常 lock:账户被锁定
+ "list": [
+ {
+ "currency": "usdt", 币种
+ "type": "trade", 类型 trade: 交易余额,frozen: 冻结余额, loan: 待还借贷本金, interest: 待还借贷利息, lock: 锁仓, bank: 储蓄
+ "balance": "5007.4362872650" 余额
+ },
+ {
+ "currency": "usdt",
+ "type": "frozen",
+ "balance": "348.1199920000"
+ }
+ ]
+ }
+}
+'''
+
+class GetBalanceService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ account_id = self.params["account-id"]
+
+ def get_channel():
+ path = "/v1/account/accounts/{}/balance"
+ return path.format(account_id)
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ balance_list = data.get("list", [])
+ return default_parse_list_dict(balance_list, Balance, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, get_channel(), self.params, parse)
+
+ def get_request(self, **kwargs):
+ account_id = self.params["account-id"]
+
+ def get_channel():
+ path = "/v1/account/accounts/{}/balance"
+ return path.format(account_id)
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ balance_list = data.get("list", [])
+ return default_parse_list_dict(balance_list, Balance, [])
+
+ return RestApiSyncClient(**kwargs).create_request(HttpMethod.GET_SIGN, get_channel(), self.params, parse)
diff --git a/huobi/service/account/post_account_transfer.py b/huobi/service/account/post_account_transfer.py
new file mode 100644
index 0000000..b5d31f7
--- /dev/null
+++ b/huobi/service/account/post_account_transfer.py
@@ -0,0 +1,41 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#c5034eb6d0
+资产划转
+API Key 权限:交易
+
+该节点为母用户和子用户进行资产划转的通用接口。
+
+母用户和子用户均支持的功能包括:
+1、币币账户与逐仓杠杠账户之间的划转;
+2、逐仓杠杠不同账户间相同币种的直接划转,如逐仓杠杠BTC/USDT账户和ETH/USDT账户,相同币种USDT可直接划转;
+
+仅母用户支持的功能包括:
+1、母用户币币账户与子用户币币账户间的划转;
+2、不同子用户币币账户间划转;
+
+仅子用户支持的功能包括:
+1、子用户币币账户向母用户下的其他子用户币币账户划转,此权限默认关闭,需母用户授权。授权接口为 POST /v2/sub-user/transferability;
+2、子用户币币账户向母用户币币账户划转;
+
+其他划转功能将逐步上线,敬请期待。
+
+
+'''
+
+class PostAccountTransferService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/account/transfer"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse(data, AccountTransferResult, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/account/post_futures_and_pro_transfer.py b/huobi/service/account/post_futures_and_pro_transfer.py
new file mode 100644
index 0000000..802af81
--- /dev/null
+++ b/huobi/service/account/post_futures_and_pro_transfer.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostTransferBetweenFuturesAndProService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/futures/transfer"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/account/post_point_transfer.py b/huobi/service/account/post_point_transfer.py
new file mode 100644
index 0000000..b771c3c
--- /dev/null
+++ b/huobi/service/account/post_point_transfer.py
@@ -0,0 +1,19 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import *
+from huobi.utils import *
+
+
+class PostPointTransferService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/point/transfer"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse(data, AccountPointTransferResult, {})
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/account/post_sub_uid_management.py b/huobi/service/account/post_sub_uid_management.py
new file mode 100644
index 0000000..226f300
--- /dev/null
+++ b/huobi/service/account/post_sub_uid_management.py
@@ -0,0 +1,24 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.account import *
+
+
+class PostSubUidManagementService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/management"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", {}), SubUidManagement)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/account/post_subaccount_transfer.py b/huobi/service/account/post_subaccount_transfer.py
new file mode 100644
index 0000000..832b844
--- /dev/null
+++ b/huobi/service/account/post_subaccount_transfer.py
@@ -0,0 +1,24 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostSubaccountTransferService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/subuser/transfer"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/account/req_account_balance.py b/huobi/service/account/req_account_balance.py
new file mode 100644
index 0000000..7226b36
--- /dev/null
+++ b/huobi/service/account/req_account_balance.py
@@ -0,0 +1,34 @@
+
+from huobi.utils import *
+
+from huobi.connection.websocket_req_client import *
+from huobi.model.account import *
+
+
+
+class ReqAccountBalanceService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ client_req_id = self.params["client_req_id"]
+ def subscription(connection):
+ connection.send(request_account_list_channel(client_req_id))
+
+ def parse(dict_data):
+ req_obj = AccountBalanceReq()
+ req_obj.ts = dict_data.get("ts", 0)
+ req_obj.topic = dict_data.get("topic", 0)
+ req_obj.cid = dict_data.get("cid", 0)
+ data_list = dict_data.get("data", [])
+ req_obj.data = AccountBalance.json_parse_list(data_list)
+ return req_obj
+
+ WebSocketReqClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler,
+ is_trade=True)
+
+
+
diff --git a/huobi/service/account/sub_account_update_v2.py b/huobi/service/account/sub_account_update_v2.py
new file mode 100644
index 0000000..a1e1b3f
--- /dev/null
+++ b/huobi/service/account/sub_account_update_v2.py
@@ -0,0 +1,30 @@
+from huobi.utils import *
+
+from huobi.connection.subscribe_client import SubscribeClient
+from huobi.model.account import *
+
+
+class SubAccountUpdateV2Service:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ mode = self.params["mode"]
+
+ def subscription(connection):
+ connection.send(accounts_update_channel(mode))
+
+ def parse(dict_data):
+ account_change_event = AccountUpdateEvent()
+ account_change_event.ch = dict_data.get("ch")
+ data = dict_data.get("data", {})
+ if data and len(data):
+ account_change_event.data = default_parse_list_dict(data, AccountUpdate)
+
+ return account_change_event
+
+ SubscribeClient(**kwargs).execute_subscribe_v2(subscription,
+ parse,
+ callback,
+ error_handler,
+ is_trade=True)
diff --git a/huobi/service/algo/__init__.py b/huobi/service/algo/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/algo/get_open_orders.py b/huobi/service/algo/get_open_orders.py
new file mode 100644
index 0000000..c390037
--- /dev/null
+++ b/huobi/service/algo/get_open_orders.py
@@ -0,0 +1,41 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import *
+from huobi.model.algo import *
+
+
+class GetOpenOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/algo-orders/opening"
+
+ # {
+ # "code": 200,
+ # "data": [
+ # {
+ # "accountId": 3684354,
+ # "clientOrderId": "test004",
+ # "lastActTime": 1600141535221,
+ # "orderOrigTime": 1600141535137,
+ # "orderPrice": "0.08",
+ # "orderSide": "buy",
+ # "orderSize": "65",
+ # "orderStatus": "created",
+ # "orderType": "limit",
+ # "source": "api",
+ # "stopPrice": "0.085",
+ # "symbol": "adausdt",
+ # "trailingRate": 0.001,
+ # "timeInForce": "gtc"
+ # }
+ # ]
+ # }
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse_list_dict(data, OrderListItem)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/algo/get_order_by_cid.py b/huobi/service/algo/get_order_by_cid.py
new file mode 100644
index 0000000..c33c285
--- /dev/null
+++ b/huobi/service/algo/get_order_by_cid.py
@@ -0,0 +1,19 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import *
+from huobi.model.algo import *
+
+
+class GetOrderByClientOrderIdService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/algo-orders/specific"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse(data, OrderHistoryItem)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/algo/get_order_history.py b/huobi/service/algo/get_order_history.py
new file mode 100644
index 0000000..05aee58
--- /dev/null
+++ b/huobi/service/algo/get_order_history.py
@@ -0,0 +1,19 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import *
+from huobi.model.algo import *
+
+
+class GetOrderHistoryService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/algo-orders/history"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse_list_dict(data, OrderHistoryItem)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/algo/post_cancel_orders.py b/huobi/service/algo/post_cancel_orders.py
new file mode 100644
index 0000000..96caaff
--- /dev/null
+++ b/huobi/service/algo/post_cancel_orders.py
@@ -0,0 +1,20 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_fill_directly
+from huobi.model.algo import *
+
+
+class PostCancelOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/algo-orders/cancellation"
+
+ # {'code': 200, 'data': {'accepted': [], 'rejected': ['test001', 'test002']}}
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse_fill_directly(data, CancelOrderResult)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/algo/post_create_order.py b/huobi/service/algo/post_create_order.py
new file mode 100644
index 0000000..9f3ab86
--- /dev/null
+++ b/huobi/service/algo/post_create_order.py
@@ -0,0 +1,18 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+
+
+class PostCreateOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/algo-orders"
+
+ # {'code': 200, 'data': {'clientOrderId': 'test001'}}
+ def parse(dict_data):
+ data = dict_data.get('data')
+ return data.get('clientOrderId')
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/etf/__init__.py b/huobi/service/etf/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/etf/get_etf_swap_config.py b/huobi/service/etf/get_etf_swap_config.py
new file mode 100644
index 0000000..9712b9d
--- /dev/null
+++ b/huobi/service/etf/get_etf_swap_config.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.etf import *
+from huobi.utils import *
+
+
+
+class GetEtfSwapConfigService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/etf/swap/config"
+
+ def parse(dict_data):
+ data_info = dict_data.get("data", {})
+ return default_parse(data_info, EtfSwapConfig, UnitPrice)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/etf/get_etf_swap_list.py b/huobi/service/etf/get_etf_swap_list.py
new file mode 100644
index 0000000..26d01dd
--- /dev/null
+++ b/huobi/service/etf/get_etf_swap_list.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.etf import *
+
+class GetEtfSwapListService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/etf/swap/list"
+
+ def parse(dict_data):
+ return EtfSwapList.json_parse_list(dict_data.get("data", []))
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
+
diff --git a/huobi/service/etf/post_etf_swap_in.py b/huobi/service/etf/post_etf_swap_in.py
new file mode 100644
index 0000000..f7b28eb
--- /dev/null
+++ b/huobi/service/etf/post_etf_swap_in.py
@@ -0,0 +1,28 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.etf import *
+
+
+
+class PostEftSwapInService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/etf/swap/in"
+
+ def parse(dict_data):
+ return default_parse_fill_directly(dict_data, EtfSwapInOut)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
+
+
+
diff --git a/huobi/service/etf/post_etf_swap_out.py b/huobi/service/etf/post_etf_swap_out.py
new file mode 100644
index 0000000..9eca017
--- /dev/null
+++ b/huobi/service/etf/post_etf_swap_out.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.etf import *
+
+
+class PostEtfSwapOutService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/etf/swap/out"
+
+ def parse(dict_data):
+ return default_parse_fill_directly(dict_data, EtfSwapInOut)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
+
diff --git a/huobi/service/generic/__init__.py b/huobi/service/generic/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/generic/get_exchange_currencies.py b/huobi/service/generic/get_exchange_currencies.py
new file mode 100644
index 0000000..d403c34
--- /dev/null
+++ b/huobi/service/generic/get_exchange_currencies.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+
+
+class GetExchangeCurrenciesService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/common/currencys"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return data_list if len(data_list) else []
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/generic/get_exchange_symbols.py b/huobi/service/generic/get_exchange_symbols.py
new file mode 100644
index 0000000..b92a56e
--- /dev/null
+++ b/huobi/service/generic/get_exchange_symbols.py
@@ -0,0 +1,19 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.generic import *
+from huobi.utils import *
+
+
+class GetExchangeSymbolsService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/common/symbols"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, Symbol, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
diff --git a/huobi/service/generic/get_exchange_timestamp.py b/huobi/service/generic/get_exchange_timestamp.py
new file mode 100644
index 0000000..f95d669
--- /dev/null
+++ b/huobi/service/generic/get_exchange_timestamp.py
@@ -0,0 +1,24 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class GetExchangeTimestampService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/common/timestamp"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/generic/get_market_status.py b/huobi/service/generic/get_market_status.py
new file mode 100644
index 0000000..788e55c
--- /dev/null
+++ b/huobi/service/generic/get_market_status.py
@@ -0,0 +1,18 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.generic import *
+from huobi.utils import *
+
+
+class GetMarketStatusService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/market-status"
+
+ def parse(dict_data):
+ return default_parse(dict_data.get("data", {}), MarketStatus)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
diff --git a/huobi/service/generic/get_reference_currencies.py b/huobi/service/generic/get_reference_currencies.py
new file mode 100644
index 0000000..cda6039
--- /dev/null
+++ b/huobi/service/generic/get_reference_currencies.py
@@ -0,0 +1,31 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.generic import *
+from huobi.utils import *
+
+
+
+class GetReferenceCurrenciesService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/reference/currencies"
+
+ def parse(dict_data):
+ ret_list = []
+ data_list = dict_data.get("data", [])
+ if data_list and len(data_list):
+ for reference_currency in data_list:
+ reference_currency_obj = default_parse(reference_currency, ReferenceCurrency, Chain)
+ ret_list.append(reference_currency_obj)
+ return ret_list
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/generic/get_system_status.py b/huobi/service/generic/get_system_status.py
new file mode 100644
index 0000000..ebeffa5
--- /dev/null
+++ b/huobi/service/generic/get_system_status.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+
+
+class GetSystemStatusService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/api/v2/summary.json"
+ kwargs["url"] = "https://status.huobigroup.com"
+
+ def parse(dict_data):
+ return dict_data
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/linear-swap/__init__.py b/huobi/service/linear-swap/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/linear-swap/post_swap_openorders.py b/huobi/service/linear-swap/post_swap_openorders.py
new file mode 100644
index 0000000..b8249fe
--- /dev/null
+++ b/huobi/service/linear-swap/post_swap_openorders.py
@@ -0,0 +1,69 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+
+'''
+https://huobiapi.github.io/docs/usdt_swap/v1/cn/#136259e73a
+该接口仅支持逐仓模式。
+请求参数
+参数名称 是否必须 类型 描述 取值范围
+contract_code true string 合约代码 "BTC-USDT" ...
+page_index false int 页码,不填默认第1页
+page_size false int 页长,不填默认20,不得多于50
+
+返回参数
+参数名称 是否必须 类型 描述 取值范围
+status true string 请求处理结果
+
+
+symbol true string 品种代码
+contract_code true string 合约代码 "BTC-USDT" ...
+volume true decimal 委托数量
+price true decimal 委托价格
+order_price_type true string 订单报价类型 "limit":限价,"opponent":对手价,"post_only":只做maker单,post only下单只受用户持仓数量限制,"lightning":闪电平仓,"optimal_5":最优5档,"optimal_10":最优10档,"optimal_20":最优20档,"fok":FOK订单,"ioc":IOC订单, "opponent_ioc": 对手价-IOC下单,"lightning_ioc": 闪电平仓-IOC下单,"optimal_5_ioc": 最优5档-IOC下单,"optimal_10_ioc": 最优10档-IOC下单,"optimal_20_ioc":最优20档-IOC下单,"opponent_fok": 对手价-FOK下单,"lightning_fok":闪电平仓-FOK下单,"optimal_5_fok":最优5档-FOK下单,"optimal_10_fok":最优10档-FOK下单,"optimal_20_fok":最优20档-FOK下单
+order_type true int 订单类型 1:报单 、 2:撤单 、 3:强平、4:交割
+direction true string "buy":买 "sell":卖
+offset true string "open":开 "close":平
+lever_rate true int 杠杆倍数
+order_id true long 订单ID
+order_id_str true string 订单ID,字符串类型
+client_order_id true long 客户订单ID
+created_at true long 订单创建时间
+trade_volume true decimal 成交数量
+trade_turnover true decimal 成交总金额
+fee true decimal 手续费
+fee_asset true string 手续费币种 "BTC","ETH"...
+trade_avg_price true decimal 成交均价
+margin_frozen true decimal 冻结保证金
+margin_asset true string 保证金币种(计价币种)
+profit true decimal 收益
+status true int 订单状态 (3未成交 4部分成交 5部分成交已撤单 6全部成交 7已撤单)
+order_source true string 订单来源 (system:系统、web:用户网页、api:用户API、m:用户M站、risk:风控系统、settlement:交割结算、ios:ios客户端、android:安卓客户端、windows:windows客户端、mac:mac客户端、trigger:计划委托触发、tpsl:止盈止损触发)
+liquidation_type true string 强平类型
+canceled_at true long 撤单时间
+margin_mode true string 保证金模式 isolated:逐仓模式
+margin_account true string 保证金账户 比如“BTC-USDT”
+is_tpsl true int 是否设置止盈止损 1:是;0:否
+
+total_page true int 总页数
+current_page true int 当前页
+total_size true int 总条数
+
+ts true long 时间戳
+'''
+
+
+class PostSwapOpenOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/linear-swap-api/v1/swap_openorders"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/margin/__init__.py b/huobi/service/margin/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/margin/get_cross_margin_account_balance.py b/huobi/service/margin/get_cross_margin_account_balance.py
new file mode 100644
index 0000000..b534211
--- /dev/null
+++ b/huobi/service/margin/get_cross_margin_account_balance.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.margin import *
+from huobi.utils import *
+
+
+
+class GetCrossMarginAccountBalanceService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/cross-margin/accounts/balance"
+
+ def parse(dict_data):
+ return CrossMarginAccountBalance.json_parse(dict_data.get("data", {}))
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/get_cross_margin_loan_info.py b/huobi/service/margin/get_cross_margin_loan_info.py
new file mode 100644
index 0000000..30040d2
--- /dev/null
+++ b/huobi/service/margin/get_cross_margin_loan_info.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.margin import *
+from huobi.utils import *
+
+
+
+class GetCrossMarginLoanInfoService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/cross-margin/loan-info"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", []), CrossMarginLoanInfo, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/get_cross_margin_loan_orders.py b/huobi/service/margin/get_cross_margin_loan_orders.py
new file mode 100644
index 0000000..36c8878
--- /dev/null
+++ b/huobi/service/margin/get_cross_margin_loan_orders.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.margin import *
+from huobi.utils import *
+
+
+
+class GetCrossMarginLoanOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/cross-margin/loan-orders"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, LoanOrder, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/get_general_repayment_loan_records.py b/huobi/service/margin/get_general_repayment_loan_records.py
new file mode 100644
index 0000000..59df420
--- /dev/null
+++ b/huobi/service/margin/get_general_repayment_loan_records.py
@@ -0,0 +1,22 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.margin.general_repay_loan_record import GeneralRepayLoanRecord
+from huobi.model.margin.general_repay_loan_result import GeneralRepayLoanResult
+from huobi.utils.json_parser import default_parse_list_dict
+
+
+class GetGeneralRepaymentLoanRecordsService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+
+ def get_channel():
+ path = "/v2/account/repayment"
+ return path
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", {}), GeneralRepayLoanRecord)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, get_channel(), self.params, parse)
\ No newline at end of file
diff --git a/huobi/service/margin/get_margin_account_balance.py b/huobi/service/margin/get_margin_account_balance.py
new file mode 100644
index 0000000..14531a6
--- /dev/null
+++ b/huobi/service/margin/get_margin_account_balance.py
@@ -0,0 +1,32 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.account import Balance
+from huobi.model.margin import *
+from huobi.utils import *
+
+
+
+class GetMarginAccountBalanceService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/margin/accounts/balance"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ account_balance_list = []
+ if data_list and len(data_list):
+ for row in data_list:
+ account_balance = default_parse(row, MarginAccountBalance, Balance)
+ account_balance_list.append(account_balance)
+ return account_balance_list
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/get_margin_loan_info.py b/huobi/service/margin/get_margin_loan_info.py
new file mode 100644
index 0000000..82b9a04
--- /dev/null
+++ b/huobi/service/margin/get_margin_loan_info.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.margin import *
+from huobi.utils import *
+
+
+
+class GetMarginLoanInfoService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/margin/loan-info"
+
+ def parse(dict_data):
+ return MarginLoanInfo.json_parse(dict_data.get("data", []))
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/get_margin_loan_orders.py b/huobi/service/margin/get_margin_loan_orders.py
new file mode 100644
index 0000000..509d210
--- /dev/null
+++ b/huobi/service/margin/get_margin_loan_orders.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.margin import *
+from huobi.utils import *
+
+
+
+class GetMarginLoanOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/margin/loan-orders"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, LoanOrder, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_create_margin_order.py b/huobi/service/margin/post_create_margin_order.py
new file mode 100644
index 0000000..1ffc452
--- /dev/null
+++ b/huobi/service/margin/post_create_margin_order.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostCreateMarginOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/margin/orders"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_cross_margin_create_loan_orders.py b/huobi/service/margin/post_cross_margin_create_loan_orders.py
new file mode 100644
index 0000000..f99fa3e
--- /dev/null
+++ b/huobi/service/margin/post_cross_margin_create_loan_orders.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostCrossMarginCreateLoanOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/cross-margin/orders"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_cross_margin_loan_order_repay.py b/huobi/service/margin/post_cross_margin_loan_order_repay.py
new file mode 100644
index 0000000..0fc53eb
--- /dev/null
+++ b/huobi/service/margin/post_cross_margin_loan_order_repay.py
@@ -0,0 +1,22 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+
+
+class PostCrossMarginLoanOrderRepayService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/cross-margin/orders/{order_id}/repay".format(order_id=self.params.get("order-id"))
+
+ def parse(dict_data):
+ return dict_data.get("status", None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_cross_margin_transfer_in.py b/huobi/service/margin/post_cross_margin_transfer_in.py
new file mode 100644
index 0000000..dada255
--- /dev/null
+++ b/huobi/service/margin/post_cross_margin_transfer_in.py
@@ -0,0 +1,24 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostCrossMarginTransferInService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/cross-margin/transfer-in"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_cross_margin_transfer_out.py b/huobi/service/margin/post_cross_margin_transfer_out.py
new file mode 100644
index 0000000..91f7019
--- /dev/null
+++ b/huobi/service/margin/post_cross_margin_transfer_out.py
@@ -0,0 +1,24 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostCrossMarginTransferOutService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/cross-margin/transfer-out"
+
+ def parse(dict_data):
+ transfer_id = default_parse_data_as_long(dict_data, None)
+ return transfer_id
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_general_repay_loan.py b/huobi/service/margin/post_general_repay_loan.py
new file mode 100644
index 0000000..95d26dc
--- /dev/null
+++ b/huobi/service/margin/post_general_repay_loan.py
@@ -0,0 +1,21 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.margin.general_repay_loan_result import GeneralRepayLoanResult
+from huobi.utils.json_parser import default_parse_list_dict
+
+
+class PostGeneralRepayLoanService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+
+ def get_channel():
+ path = "/v2/account/repayment"
+ return path
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", {}), GeneralRepayLoanResult)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, get_channel(), self.params, parse)
\ No newline at end of file
diff --git a/huobi/service/margin/post_repay_margin_order.py b/huobi/service/margin/post_repay_margin_order.py
new file mode 100644
index 0000000..d1f7a53
--- /dev/null
+++ b/huobi/service/margin/post_repay_margin_order.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostRepayMarginOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ loan_id = self.params["loan_id"]
+ def get_channel():
+ path = "/v1/margin/orders/{}/repay"
+ return path.format(loan_id)
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, get_channel(), self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_transfer_in_margin.py b/huobi/service/margin/post_transfer_in_margin.py
new file mode 100644
index 0000000..3415432
--- /dev/null
+++ b/huobi/service/margin/post_transfer_in_margin.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostTransferInMarginService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/dw/transfer-in/margin"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/margin/post_transfer_out_margin.py b/huobi/service/margin/post_transfer_out_margin.py
new file mode 100644
index 0000000..57e4876
--- /dev/null
+++ b/huobi/service/margin/post_transfer_out_margin.py
@@ -0,0 +1,23 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostTransferOutMarginService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/dw/transfer-out/margin"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/__init__.py b/huobi/service/market/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/huobi/service/market/__init__.py
@@ -0,0 +1 @@
+
diff --git a/huobi/service/market/get_candlestick.py b/huobi/service/market/get_candlestick.py
new file mode 100644
index 0000000..6fa76ee
--- /dev/null
+++ b/huobi/service/market/get_candlestick.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.market import *
+from huobi.utils import *
+
+
+
+class GetCandleStickService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/market/history/kline"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", []), Candlestick)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/get_history_trade.py b/huobi/service/market/get_history_trade.py
new file mode 100644
index 0000000..8e9015d
--- /dev/null
+++ b/huobi/service/market/get_history_trade.py
@@ -0,0 +1,35 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.market import *
+from huobi.utils import *
+
+
+class GetHistoryTradeService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/market/history/trade"
+
+ def parse(dict_data):
+ trade_list_ret = [] # two level list, list item is list too
+ data_list_outer = dict_data.get("data", [])
+ if len(data_list_outer):
+ for row in data_list_outer:
+ data_list_inner = row.get("data", [])
+ if len(data_list_inner):
+ for trade_info in data_list_inner:
+ trade_obj = default_parse_list_dict(trade_info, Trade, None) # return a list
+ if trade_obj:
+ trade_list_ret.append(trade_obj)
+
+ return trade_list_ret
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/get_market_detail.py b/huobi/service/market/get_market_detail.py
new file mode 100644
index 0000000..8b72e20
--- /dev/null
+++ b/huobi/service/market/get_market_detail.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.market import *
+from huobi.utils import *
+
+
+
+class GetMarketDetailService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/market/detail"
+
+ def parse(dict_data):
+ tick = dict_data.get("tick", {})
+ return default_parse(tick, MarketDetail)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/get_market_detail_merged.py b/huobi/service/market/get_market_detail_merged.py
new file mode 100644
index 0000000..94977c0
--- /dev/null
+++ b/huobi/service/market/get_market_detail_merged.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.market import *
+from huobi.utils import *
+
+
+
+class GetMarketDetailMergedService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/market/detail/merged"
+
+ def parse(dict_data):
+ tick = dict_data.get("tick", {})
+ return default_parse_fill_directly(tick, MarketDetailMerged)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/get_market_tickers.py b/huobi/service/market/get_market_tickers.py
new file mode 100644
index 0000000..285c343
--- /dev/null
+++ b/huobi/service/market/get_market_tickers.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.market import *
+from huobi.utils import *
+
+
+
+class GetMarketTickersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/market/tickers"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", []), MarketTicker)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/get_market_trade.py b/huobi/service/market/get_market_trade.py
new file mode 100644
index 0000000..9d14e06
--- /dev/null
+++ b/huobi/service/market/get_market_trade.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.market import *
+from huobi.utils import *
+
+
+class GetMarketTradeService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/market/trade"
+
+ def parse(dict_data):
+ tick = dict_data.get("tick", {})
+ data = tick.get("data", [])
+ return default_parse_list_dict(data, Trade, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/get_pricedepth.py b/huobi/service/market/get_pricedepth.py
new file mode 100644
index 0000000..a245382
--- /dev/null
+++ b/huobi/service/market/get_pricedepth.py
@@ -0,0 +1,24 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.market import PriceDepth
+
+
+class GetPriceDepthService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/market/depth"
+
+ def parse(dict_data):
+ tick = dict_data.get("tick", {})
+ return PriceDepth.json_parse(tick)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/market/req_candlestick.py b/huobi/service/market/req_candlestick.py
new file mode 100644
index 0000000..695a039
--- /dev/null
+++ b/huobi/service/market/req_candlestick.py
@@ -0,0 +1,34 @@
+import time
+
+from huobi.utils import *
+
+from huobi.connection.websocket_req_client import *
+from huobi.model.market import *
+from huobi.utils.channels_request import *
+
+
+class ReqCandleStickService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+ interval = self.params["interval"]
+ from_ts_second = self.params.get("from_ts_second", None)
+ end_ts_second = self.params.get("end_ts_second", None)
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(request_kline_channel(symbol, interval, from_ts_second, end_ts_second))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return default_parse(dict_data, CandlestickReq, Candlestick)
+
+ WebSocketReqClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/req_market_detail.py b/huobi/service/market/req_market_detail.py
new file mode 100644
index 0000000..043b204
--- /dev/null
+++ b/huobi/service/market/req_market_detail.py
@@ -0,0 +1,31 @@
+import time
+
+from huobi.utils import *
+
+from huobi.connection.websocket_req_client import *
+from huobi.model.market import *
+from huobi.utils.channels_request import *
+
+
+class ReqMarketDetailService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(request_market_detail_channel(symbol))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return default_parse(dict_data, MarketDetailReq, MarketDetail)
+
+ WebSocketReqClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/req_mbp.py b/huobi/service/market/req_mbp.py
new file mode 100644
index 0000000..51aa791
--- /dev/null
+++ b/huobi/service/market/req_mbp.py
@@ -0,0 +1,27 @@
+import time
+
+from huobi.model.market import *
+from huobi.utils import *
+from huobi.connection.websocket_req_client import WebSocketReqClient
+
+
+class ReqMbpService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+ level = self.params["levels"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(request_mbp_channel(symbol, level))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return MbpReq.json_parse(dict_data)
+
+ WebSocketReqClient(**kwargs).execute_subscribe_mbp(subscription,
+ parse,
+ callback,
+ error_handler)
diff --git a/huobi/service/market/req_pricedepth.py b/huobi/service/market/req_pricedepth.py
new file mode 100644
index 0000000..1a5cd59
--- /dev/null
+++ b/huobi/service/market/req_pricedepth.py
@@ -0,0 +1,36 @@
+import time
+
+from huobi.connection.websocket_req_client import *
+from huobi.utils.channels_request import *
+from huobi.model.market import *
+
+
+class ReqPriceDepthService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+ step = self.params["step"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(request_price_depth_channel(symbol, step))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ price_depth_event = PriceDepthReq()
+ price_depth_event.id = dict_data.get("id")
+ price_depth_event.rep = dict_data.get("rep")
+ data = dict_data.get("data", {})
+ price_depth_obj = PriceDepth.json_parse(data)
+ price_depth_event.data = price_depth_obj
+ return price_depth_event
+
+ WebSocketReqClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/req_trade_detail.py b/huobi/service/market/req_trade_detail.py
new file mode 100644
index 0000000..44b28d4
--- /dev/null
+++ b/huobi/service/market/req_trade_detail.py
@@ -0,0 +1,29 @@
+import time
+
+from huobi.model.market import *
+from huobi.utils import *
+from huobi.connection.websocket_req_client import *
+
+
+class ReqTradeDetailService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(request_trade_detail_channel(symbol))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return default_parse(dict_data, TradeDetailReq, TradeDetail)
+
+ WebSocketReqClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/sub_candlestick.py b/huobi/service/market/sub_candlestick.py
new file mode 100644
index 0000000..794f87a
--- /dev/null
+++ b/huobi/service/market/sub_candlestick.py
@@ -0,0 +1,33 @@
+import time
+
+from huobi.utils import *
+
+from huobi.connection.subscribe_client import SubscribeClient
+from huobi.model.market import *
+
+
+
+class SubCandleStickService:
+ def __init__(self, params):
+
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+ interval = self.params["interval"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(kline_channel(symbol, interval))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return default_parse(dict_data, CandlestickEvent, Candlestick)
+
+ SubscribeClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/sub_market_detail.py b/huobi/service/market/sub_market_detail.py
new file mode 100644
index 0000000..13c4655
--- /dev/null
+++ b/huobi/service/market/sub_market_detail.py
@@ -0,0 +1,31 @@
+import time
+
+from huobi.utils import *
+
+from huobi.connection.subscribe_client import SubscribeClient
+from huobi.model.market import *
+
+
+
+class SubMarketDetailService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(market_detail_channel(symbol))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return default_parse(dict_data, MarketDetailEvent, MarketDetail)
+
+ SubscribeClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/sub_mbp_full.py b/huobi/service/market/sub_mbp_full.py
new file mode 100644
index 0000000..0e99660
--- /dev/null
+++ b/huobi/service/market/sub_mbp_full.py
@@ -0,0 +1,27 @@
+import time
+
+from huobi.model.market import *
+from huobi.utils import *
+from huobi.connection.subscribe_client import SubscribeClient
+
+
+class SubMbpFullService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+ level = self.params["levels"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(mbp_full_channel(symbol, level))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return MbpFullEvent.json_parse(dict_data)
+
+ SubscribeClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
diff --git a/huobi/service/market/sub_mbp_increase.py b/huobi/service/market/sub_mbp_increase.py
new file mode 100644
index 0000000..a185769
--- /dev/null
+++ b/huobi/service/market/sub_mbp_increase.py
@@ -0,0 +1,30 @@
+import time
+
+from huobi.model.market import *
+from huobi.utils import *
+from huobi.connection.subscribe_client import SubscribeClient
+
+
+class SubMbpIncreaseService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+ level = self.params["levels"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(mbp_increase_channel(symbol, level))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return MbpIncreaseEvent.json_parse(dict_data)
+
+ SubscribeClient(**kwargs).execute_subscribe_mbp(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/sub_pricedepth.py b/huobi/service/market/sub_pricedepth.py
new file mode 100644
index 0000000..6210365
--- /dev/null
+++ b/huobi/service/market/sub_pricedepth.py
@@ -0,0 +1,34 @@
+import time
+
+from huobi.model.market import *
+from huobi.utils import *
+from huobi.connection.subscribe_client import SubscribeClient
+
+
+class SubPriceDepthService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+ step = self.params["step"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(price_depth_channel(symbol, step))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ price_depth_event_obj = PriceDepthEvent()
+ price_depth_event_obj.ch = dict_data.get("ch", "")
+ tick = dict_data.get("tick", "")
+ price_depth_event_obj.tick = PriceDepth.json_parse(tick)
+ return price_depth_event_obj
+
+ SubscribeClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/sub_pricedepth_bbo.py b/huobi/service/market/sub_pricedepth_bbo.py
new file mode 100644
index 0000000..b44597b
--- /dev/null
+++ b/huobi/service/market/sub_pricedepth_bbo.py
@@ -0,0 +1,29 @@
+import time
+
+from huobi.utils import *
+from huobi.model.market import *
+from huobi.connection.subscribe_client import SubscribeClient
+
+
+class SubPriceDepthBboService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(price_depth_bbo_channel(symbol))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return default_parse(dict_data, PriceDepthBboEvent, PriceDepthBbo)
+
+ SubscribeClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/market/sub_trade_detail.py b/huobi/service/market/sub_trade_detail.py
new file mode 100644
index 0000000..1bd539b
--- /dev/null
+++ b/huobi/service/market/sub_trade_detail.py
@@ -0,0 +1,32 @@
+import time
+
+from huobi.model.market import *
+from huobi.utils import *
+from huobi.connection.subscribe_client import SubscribeClient
+
+
+class SubTradeDetailService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(trade_detail_channel(symbol))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ tick = dict_data.get("tick", {})
+ trade_detail_event = default_parse(tick, TradeDetailEvent, TradeDetail)
+ trade_detail_event.ch = dict_data.get("ch", "")
+ return trade_detail_event
+
+ SubscribeClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler)
+
+
+
diff --git a/huobi/service/subuser/__init__.py b/huobi/service/subuser/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/subuser/get_uid.py b/huobi/service/subuser/get_uid.py
new file mode 100644
index 0000000..1e16c7c
--- /dev/null
+++ b/huobi/service/subuser/get_uid.py
@@ -0,0 +1,20 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.subuser import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+
+class GetUidService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/user/uid"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/subuser/get_user_apikey_info.py b/huobi/service/subuser/get_user_apikey_info.py
new file mode 100644
index 0000000..a37c392
--- /dev/null
+++ b/huobi/service/subuser/get_user_apikey_info.py
@@ -0,0 +1,18 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.subuser import *
+
+
+class GetUserApikeyInfoService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/user/api-key"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", {}), UserApikeyInfo)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
diff --git a/huobi/service/subuser/post_create_subuser.py b/huobi/service/subuser/post_create_subuser.py
new file mode 100644
index 0000000..45dd123
--- /dev/null
+++ b/huobi/service/subuser/post_create_subuser.py
@@ -0,0 +1,18 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.subuser import *
+
+
+class PostSubuserCreationService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/creation"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", {}), SubuserCreation)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/subuser/post_set_transferability.py b/huobi/service/subuser/post_set_transferability.py
new file mode 100644
index 0000000..47fe674
--- /dev/null
+++ b/huobi/service/subuser/post_set_transferability.py
@@ -0,0 +1,18 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.subuser import *
+
+
+class PostSetSubuserTransferability:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/transferability"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", {}), SubuserTransferability)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/subuser/post_subuser_apikey_deletion.py b/huobi/service/subuser/post_subuser_apikey_deletion.py
new file mode 100644
index 0000000..821fbfa
--- /dev/null
+++ b/huobi/service/subuser/post_subuser_apikey_deletion.py
@@ -0,0 +1,17 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+
+
+class PostSubuserApikeyDeletionService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/api-key-deletion"
+
+ # {'code': 200, 'data': None, 'ok': True}
+ def parse(dict_data):
+ return dict_data
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/subuser/post_subuser_apikey_generation.py b/huobi/service/subuser/post_subuser_apikey_generation.py
new file mode 100644
index 0000000..76243a5
--- /dev/null
+++ b/huobi/service/subuser/post_subuser_apikey_generation.py
@@ -0,0 +1,18 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.subuser import *
+
+
+class PostSubuserApikeyGenerationService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/api-key-generation"
+
+ def parse(dict_data):
+ return default_parse(dict_data.get("data", {}), SubuserApikeyGeneration)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/subuser/post_subuser_apikey_modification.py b/huobi/service/subuser/post_subuser_apikey_modification.py
new file mode 100644
index 0000000..6671abe
--- /dev/null
+++ b/huobi/service/subuser/post_subuser_apikey_modification.py
@@ -0,0 +1,18 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.utils import *
+from huobi.model.subuser import *
+
+
+class PostSubuserApikeyModificationService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/api-key-modification"
+
+ def parse(dict_data):
+ return default_parse(dict_data.get("data", {}), SubuserApikeyModification)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/subuser/post_tradable_market.py b/huobi/service/subuser/post_tradable_market.py
new file mode 100644
index 0000000..4e644eb
--- /dev/null
+++ b/huobi/service/subuser/post_tradable_market.py
@@ -0,0 +1,17 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.subuser.trade_market import TradeMarket
+from huobi.utils import *
+
+
+class PostTradableMarketService:
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/tradable-market"
+
+ def parse(dict_data):
+ return default_parse_list_dict(dict_data.get("data", {}), TradeMarket)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
diff --git a/huobi/service/trade/__init__.py b/huobi/service/trade/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/trade/get_feerate.py b/huobi/service/trade/get_feerate.py
new file mode 100644
index 0000000..f3c7358
--- /dev/null
+++ b/huobi/service/trade/get_feerate.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class GetFeeRateService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/fee/fee-rate/get"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, FeeRate, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_history_orders.py b/huobi/service/trade/get_history_orders.py
new file mode 100644
index 0000000..eeafe6c
--- /dev/null
+++ b/huobi/service/trade/get_history_orders.py
@@ -0,0 +1,24 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.trade import *
+
+
+class GetHistoryOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/history"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return Order.json_parse_list(data_list)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_match_results.py b/huobi/service/trade/get_match_results.py
new file mode 100644
index 0000000..579bde1
--- /dev/null
+++ b/huobi/service/trade/get_match_results.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class GetMatchResultsService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/matchresults"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, MatchResult, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_match_results_by_order_id.py b/huobi/service/trade/get_match_results_by_order_id.py
new file mode 100644
index 0000000..84094d5
--- /dev/null
+++ b/huobi/service/trade/get_match_results_by_order_id.py
@@ -0,0 +1,60 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#client-order-id-2
+成交明细
+API Key 权限:读取
+限频值(NEW):50次/2s
+
+此接口返回指定订单的成交明细。
+
+返回的主数据对象为一个对象数组,其中每一个元件代表一个交易结果。:
+{
+ "data": [
+ {
+ "id": 29553,
+ "order-id": 59378,
+ "match-id": 59335,
+ "trade-id": 100282808529,
+ "symbol": "ethusdt",
+ "type": "buy-limit",
+ "source": "api",
+ "price": "100.1000000000",
+ "filled-amount": "9.1155000000", 成交数量
+ "filled-fees": "0.0182310000", 交易手续费(正值)或交易返佣金(负值)
+ "created-at": 1494901400435, 该成交记录创建的时间戳(略晚于成交时间)
+ "role": "maker",
+ "filled-points": "0.0",
+ “fee-deduct-state”:"done",
+ "fee-deduct-currency": "" 如果为空,代表扣除的手续费是原币;如果为"ht",代表抵扣手续费的是HT;如果为"hbpoint",代表抵扣手续费的是点卡
+ }
+ ...
+ ]
+}
+
+'''
+
+class GetMatchResultsByOrderIdService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ order_id = self.params["order_id"]
+ def get_channel():
+ path = "/v1/order/orders/{}/matchresults"
+ return path.format(order_id)
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, MatchResult, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, get_channel(), self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_open_orders.py b/huobi/service/trade/get_open_orders.py
new file mode 100644
index 0000000..c26ce81
--- /dev/null
+++ b/huobi/service/trade/get_open_orders.py
@@ -0,0 +1,41 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.trade import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#75a79bcd48
+查询当前未成交订单
+API Key 权限:读取
+限频值(NEW):50次/2s
+查询已提交但是仍未完全成交或未被撤销的订单。
+
+请求参数:
+account-id 账户 ID,取值参考 GET /v1/account/accounts。现货交易使用‘spot’账户的 account-id;逐仓杠杆交易,请使用 ‘margin’ 账户的 account-id;全仓杠杆交易,请使用 ‘super-margin’ 账户的 account-id;c2c杠杆交易,请使用borrow账户的account-id
+symbol 交易对,即btcusdt, ethbtc...(取值参考GET /v1/common/symbols)
+side 指定只返回某一个方向的订单,可能的值有: buy, sell. 默认两个方向都返回。
+from 查询起始 ID,如果是向后查询,则赋值为上一次查询结果中得到的最后一条id ;如果是向前查询,则赋值为上一次查询结果中得到的第一条id
+direct 查询方向,prev 向前;next 向后 如字段'from'已设定,此字段'direct'为必填
+size 返回订单的数量,最大值500。
+
+
+
+'''
+
+class GetOpenOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/openOrders"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return Order.json_parse_list(data_list)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_order_by_client_order_id.py b/huobi/service/trade/get_order_by_client_order_id.py
new file mode 100644
index 0000000..a076e72
--- /dev/null
+++ b/huobi/service/trade/get_order_by_client_order_id.py
@@ -0,0 +1,35 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#92d59b6aad
+查询订单详情(基于client order ID)
+API Key 权限:读取
+限频值(NEW):50次/2s
+
+此接口返回指定用户自编订单号(24小时内)的订单最新状态和详情。通过API创建的订单,撤销超过2小时后无法查询。建议通过GET /v1/order/orders/{order-id}来撤单,比使用clientOrderId更快更稳定
+
+
+
+
+'''
+
+class GetOrderByClientOrderIdService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/orders/getClientOrder"
+
+ def parse(dict_data):
+ data_dict = dict_data.get("data", {})
+ return Order.json_parse(data_dict)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_order_by_id.py b/huobi/service/trade/get_order_by_id.py
new file mode 100644
index 0000000..fbdd269
--- /dev/null
+++ b/huobi/service/trade/get_order_by_id.py
@@ -0,0 +1,59 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#5f8b337a4c
+查询订单详情
+API Key 权限:读取
+限频值(NEW):50次/2s
+
+此接口返回指定订单的最新状态和详情。通过API创建的订单,撤销超过2小时后无法查询。
+请求参数:
+order-id 订单ID,填在path中
+
+响应数据:
+{
+ "data":
+ {
+ "id": 59378, 订单ID
+ "symbol": "ethusdt", 交易对
+ "account-id": 100009, 账户 ID
+ "amount": "10.1000000000", 订单数量
+ "price": "100.1000000000", 订单价格
+ "created-at": 1494901162595, 订单创建时间
+ "type": "buy-limit", 订单类型
+ "field-amount": "10.1000000000", 已成交数量
+ "field-cash-amount": "1011.0100000000", 已成交总金额
+ "field-fees": "0.0202000000", 已成交手续费(准确数值请参考matchresults接口)
+ "finished-at": 1494901400468, 订单变为终结态的时间,不是成交时间,包含“已撤单”状态
+ "user-id": 1000,
+ "source": "api", 订单来源
+ "state": "filled", 订单状态
+ "canceled-at": 0 订单撤销时间
+ }
+}
+'''
+
+class GetOrderByIdService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ order_id = self.params["order_id"]
+ def get_channel():
+ path = "/v1/order/orders/{}"
+ return path.format(order_id)
+
+ def parse(dict_data):
+ data_dict = dict_data.get("data")
+ return Order.json_parse(data_dict)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, get_channel(), self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_orders.py b/huobi/service/trade/get_orders.py
new file mode 100644
index 0000000..958b0e6
--- /dev/null
+++ b/huobi/service/trade/get_orders.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class GetOrdersService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/orders"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return Order.json_parse_list(data_list)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/get_transact_feerate.py b/huobi/service/trade/get_transact_feerate.py
new file mode 100644
index 0000000..9113dee
--- /dev/null
+++ b/huobi/service/trade/get_transact_feerate.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant.system import HttpMethod
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class GetTransactFeeRateService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/reference/transact-fee-rate"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, TransactFeeRate, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/post_batch_cancel_open_order.py b/huobi/service/trade/post_batch_cancel_open_order.py
new file mode 100644
index 0000000..d8c7656
--- /dev/null
+++ b/huobi/service/trade/post_batch_cancel_open_order.py
@@ -0,0 +1,56 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#95f2078356
+批量撤销所有订单
+API Key 权限:交易
+限频值(NEW):50次/2s
+
+此接口发送批量撤销所有(单次最大100个)订单的请求。
+此接口只提交取消请求,实际取消结果需要通过订单状态,撮合状态等接口来确认。
+请求参数:
+account-id 账户ID,取值参考 GET /v1/account/accounts
+symbol 交易代码列表(最多10 个symbols,多个交易代码间以逗号分隔),btcusdt, ethbtc...(取值参考/v1/common/symbols)
+types 订单类型组合,使用逗号分割
+side “buy”或“sell”,缺省将返回所有符合条件尚未成交订单
+size 撤销订单的数量 [0,100] 默认值100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+class PostBatchCancelOpenOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/orders/batchCancelOpenOrders"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse(data, BatchCancelCount)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/post_batch_cancel_order.py b/huobi/service/trade/post_batch_cancel_order.py
new file mode 100644
index 0000000..ab599af
--- /dev/null
+++ b/huobi/service/trade/post_batch_cancel_order.py
@@ -0,0 +1,38 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils import *
+from huobi.model.trade import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#b9af010185
+批量撤销指定订单
+API Key 权限:交易
+限频值(NEW):50次/2s
+此接口同时为多个订单(基于id)发送取消请求,建议通过order-ids来撤单,比client-order-ids更快更稳定。
+请求参数:
+order-ids 订单编号列表(order-ids和client-order-ids必须且只能选一个填写,不超过50张订单),建议通过order-ids来撤单,比client-order-ids更快更稳定
+client-order-ids 用户自编订单号列表(order-ids和client-order-ids必须且只能选一个填写,不超过50张订单),必须已有该订单存在,否则下次下单时不允许用此值
+
+
+
+'''
+
+
+class PostBatchCancelOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/orders/batchcancel"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ return default_parse_fill_directly(data, BatchCancelResult)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/post_batch_create_order.py b/huobi/service/trade/post_batch_create_order.py
new file mode 100644
index 0000000..a56c6e4
--- /dev/null
+++ b/huobi/service/trade/post_batch_create_order.py
@@ -0,0 +1,32 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade.batch_create_order import BatchCreateOrder
+from huobi.utils import *
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#fd6ce2a756
+批量下单
+API Key 权限:交易
+限频值(NEW):50次/2s
+
+一个批量最多10张订单
+
+'''
+class PostBatchCreateOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/batch-orders"
+
+ def parse(dict_data):
+ data = dict_data.get("data", [])
+ return default_parse_list_dict(data, BatchCreateOrder, [])
+
+ return RestApiSyncClient(**kwargs).request_process_post_batch(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/post_cancel_client_order.py b/huobi/service/trade/post_cancel_client_order.py
new file mode 100644
index 0000000..7542f94
--- /dev/null
+++ b/huobi/service/trade/post_cancel_client_order.py
@@ -0,0 +1,33 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_data_as_long
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#4e53c0fccd
+撤销订单(基于client order ID)
+API Key 权限:交易
+限频值(NEW):100次/2s
+
+此接口基于client-order-id(24小时内有效)发送一个撤销订单的请求。
+撤单个订单建议通过接口/v1/order/orders/{order-id}/submitcancel,会更快更稳定
+请求参数:
+client-order-id 用户自编订单号,必须24小时内已有该订单存在,否则下次下单时不允许用此值
+'''
+
+class PostCancelClientOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/orders/submitCancelClientOrder"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/post_cancel_order.py b/huobi/service/trade/post_cancel_order.py
new file mode 100644
index 0000000..5873d35
--- /dev/null
+++ b/huobi/service/trade/post_cancel_order.py
@@ -0,0 +1,49 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_data_as_long
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#de93fae07b
+API Key 权限:交易
+限频值(NEW):100次/2s
+
+此接口发送一个撤销订单的请求。
+此接口只提交取消请求,实际取消结果需要通过订单状态,撮合状态等接口来确认。
+
+请求参数:
+order-id 订单ID,填在path中
+
+Success response:
+{
+ "data": "59378" 返回的主数据对象是一个对应下单单号的字符串。
+}
+
+Failure response:
+{
+ "status": "error",
+ "err-code": "order-orderstate-error",
+ "err-msg": "订单状态错误",
+ "order-state":-1 // 当前订单状态
+}
+'''
+
+class PostCancelOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ order_id = self.params["order_id"]
+
+ def get_channel():
+ path = "/v1/order/orders/{}/submitcancel"
+ return path.format(order_id)
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, get_channel(), self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/post_create_order.py b/huobi/service/trade/post_create_order.py
new file mode 100644
index 0000000..158d740
--- /dev/null
+++ b/huobi/service/trade/post_create_order.py
@@ -0,0 +1,57 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.utils.json_parser import default_parse_data_as_long
+'''
+https://huobiapi.github.io/docs/spot/v1/cn/#5ea2e0cde2-7
+下单
+API Key 权限:交易 限频值;100次/2s
+发送一个新订单到火币以进行撮合。
+请求参数:
+account-id 账户 ID,取值参考 GET /v1/account/accounts。现货交易使用 ‘spot’ 账户的 account-id;逐仓杠杆交易,请使用 ‘margin’ 账户的 account-id;全仓杠杆交易,请使用 ‘super-margin’ 账户的 account-id
+symbol 交易对,即btcusdt, ethbtc...(取值参考GET /v1/common/symbols)
+type 订单类型,包括buy-market, sell-market, buy-limit, sell-limit, buy-ioc, sell-ioc, buy-limit-maker, sell-limit-maker(说明见下文), buy-stop-limit, sell-stop-limit, buy-limit-fok, sell-limit-fok, buy-stop-limit-fok, sell-stop-limit-fok
+amount 订单交易量(市价买单为订单交易额)
+price 订单价格(对市价单无效)
+source 现货交易填写“spot-api”,逐仓杠杆交易填写“margin-api”,全仓杠杆交易填写“super-margin-api”, C2C杠杆交易填写"c2c-margin-api"
+client-order-id 用户自编订单号(最大长度64个字符,须在24小时内保持唯一性)
+stop-price 止盈止损订单触发价格
+operator 止盈止损订单触发价运算符 gte – greater than and equal (>=), lte – less than and equal (<=)
+
+buy-limit-maker
+当“下单价格”>=“市场最低卖出价”,订单提交后,系统将拒绝接受此订单;
+当“下单价格”<“市场最低卖出价”,提交成功后,此订单将被系统接受。
+
+sell-limit-maker
+当“下单价格”<=“市场最高买入价”,订单提交后,系统将拒绝接受此订单;
+当“下单价格”>“市场最高买入价”,提交成功后,此订单将被系统接受。
+
+响应数据:
+{
+ "account-id": "100009",
+ "amount": "10.1",
+ "price": "100.1",
+ "source": "api",
+ "symbol": "ethusdt",
+ "type": "buy-limit",
+ "client-order-id": "a0001" 如client order ID(在24小时内)被复用,节点将返回错误消息invalid.client.order.id。
+}
+'''
+
+class PostCreateOrderService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/order/orders/place"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/post_transfer_futures_pro.py b/huobi/service/trade/post_transfer_futures_pro.py
new file mode 100644
index 0000000..0723221
--- /dev/null
+++ b/huobi/service/trade/post_transfer_futures_pro.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostTransferFuturesProService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/futures/transfer"
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/trade/req_order_detail.py b/huobi/service/trade/req_order_detail.py
new file mode 100644
index 0000000..9f2df0c
--- /dev/null
+++ b/huobi/service/trade/req_order_detail.py
@@ -0,0 +1,35 @@
+
+
+from huobi.connection.websocket_req_client import *
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class ReqOrderDetailService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ order_id = self.params["order-id"]
+ client_req_id = self.params["cid"]
+
+ def subscription(connection):
+ connection.send(request_order_detail_channel(order_id, client_req_id))
+
+ def parse(dict_data):
+ order_update_event = default_parse(dict_data, OrderDetailReq, OrderListItem)
+
+ return order_update_event
+
+ WebSocketReqClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler,
+ is_trade=True)
+
+
+
+
+
+
+
diff --git a/huobi/service/trade/req_order_list.py b/huobi/service/trade/req_order_list.py
new file mode 100644
index 0000000..51a63c0
--- /dev/null
+++ b/huobi/service/trade/req_order_list.py
@@ -0,0 +1,38 @@
+import time
+
+
+from huobi.connection.websocket_req_client import *
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class ReqOrderListService:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol = self.params["symbol"]
+ account_id = self.params["account-id"]
+ order_states = self.params["states"]
+ client_req_id = self.params["client-req-id"]
+
+ def subscription(connection):
+ connection.send(request_order_list_channel(symbol=symbol, account_id=account_id, states_str=order_states, client_req_id=client_req_id, more_key=self.params))
+
+ def parse(dict_data):
+ order_update_event = default_parse(dict_data, OrderListReq, OrderListItem)
+
+ return order_update_event
+
+ WebSocketReqClient(**kwargs).execute_subscribe_v1(subscription,
+ parse,
+ callback,
+ error_handler,
+ is_trade=True)
+
+
+
+
+
+
+
diff --git a/huobi/service/trade/sub_order_update_v2.py b/huobi/service/trade/sub_order_update_v2.py
new file mode 100644
index 0000000..406e3fe
--- /dev/null
+++ b/huobi/service/trade/sub_order_update_v2.py
@@ -0,0 +1,35 @@
+import time
+
+
+from huobi.connection.subscribe_client import SubscribeClient
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class SubOrderUpdateV2Service:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+
+ def subscription(connection):
+ for val in symbol_list:
+ connection.send(orders_update_channel(val))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return default_parse(dict_data, OrderUpdateEvent, OrderUpdate)
+
+ SubscribeClient(**kwargs).execute_subscribe_v2(subscription,
+ parse,
+ callback,
+ error_handler,
+ is_trade=True)
+
+
+
+
+
+
+
diff --git a/huobi/service/trade/sub_trade_clearing_v2.py b/huobi/service/trade/sub_trade_clearing_v2.py
new file mode 100644
index 0000000..520fd54
--- /dev/null
+++ b/huobi/service/trade/sub_trade_clearing_v2.py
@@ -0,0 +1,35 @@
+import time
+
+
+from huobi.connection.subscribe_client import SubscribeClient
+from huobi.model.trade import *
+from huobi.utils import *
+
+
+class SubTradeClearingV2Service:
+ def __init__(self, params):
+ self.params = params
+
+ def subscribe(self, callback, error_handler, **kwargs):
+ symbol_list = self.params["symbol_list"]
+
+ def subscription(connection):
+ for symbol in symbol_list:
+ connection.send(trade_clearing_channel(symbol))
+ time.sleep(0.01)
+
+ def parse(dict_data):
+ return TradeClearingEvent.json_parse(dict_data)
+
+ SubscribeClient(**kwargs).execute_subscribe_v2(subscription,
+ parse,
+ callback,
+ error_handler,
+ is_trade=True)
+
+
+
+
+
+
+
diff --git a/huobi/service/wallet/__init__.py b/huobi/service/wallet/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/huobi/service/wallet/get_account_deposit_address.py b/huobi/service/wallet/get_account_deposit_address.py
new file mode 100644
index 0000000..33b2bb8
--- /dev/null
+++ b/huobi/service/wallet/get_account_deposit_address.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.wallet import *
+from huobi.utils import *
+
+
+class GetAccountDepositAddressService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/account/deposit/address"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, ChainDepositAddress)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/wallet/get_account_withdraw_address.py b/huobi/service/wallet/get_account_withdraw_address.py
new file mode 100644
index 0000000..ade6e3a
--- /dev/null
+++ b/huobi/service/wallet/get_account_withdraw_address.py
@@ -0,0 +1,25 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.wallet import *
+from huobi.utils import *
+
+
+class GetAccountWithdrawAddressService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/account/withdraw/address"
+
+ def parse(dict_data):
+ data_list = dict_data.get("data", [])
+ return default_parse_list_dict(data_list, ChainWithdrawAddress)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/wallet/get_account_withdraw_quota.py b/huobi/service/wallet/get_account_withdraw_quota.py
new file mode 100644
index 0000000..18d0b20
--- /dev/null
+++ b/huobi/service/wallet/get_account_withdraw_quota.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.wallet import *
+from huobi.utils import *
+
+
+class GetAccountWithdrawQuotaService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/account/withdraw/quota"
+
+ def parse(dict_data):
+ data = dict_data.get("data", {})
+ chains = data.get("chains", [])
+ return default_parse_list_dict(chains, WithdrawQuota)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/wallet/get_deposit_withdraw.py b/huobi/service/wallet/get_deposit_withdraw.py
new file mode 100644
index 0000000..a011af8
--- /dev/null
+++ b/huobi/service/wallet/get_deposit_withdraw.py
@@ -0,0 +1,30 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.wallet import *
+from huobi.utils import *
+
+
+class GetDepositWithdrawService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/query/deposit-withdraw"
+
+ def parse(dict_data):
+ op_type = self.params["type"]
+ data_list = dict_data.get("data", [])
+ if op_type == DepositWithdraw.DEPOSIT:
+ return default_parse_list_dict(data_list, Deposit)
+ elif op_type == DepositWithdraw.WITHDRAW:
+ return default_parse_list_dict(data_list, Withdraw)
+ return []
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/wallet/get_sub_user_deposit_address.py b/huobi/service/wallet/get_sub_user_deposit_address.py
new file mode 100644
index 0000000..374b729
--- /dev/null
+++ b/huobi/service/wallet/get_sub_user_deposit_address.py
@@ -0,0 +1,22 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.wallet import *
+from huobi.utils import *
+
+
+class GetSubUserDepositAddressService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/deposit-address"
+
+ def parse(dict_data):
+ json_data_list = dict_data.get("data", [])
+ return default_parse_list_dict(json_data_list, ChainDepositAddress, [])
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
diff --git a/huobi/service/wallet/get_sub_user_deposit_history.py b/huobi/service/wallet/get_sub_user_deposit_history.py
new file mode 100644
index 0000000..401160d
--- /dev/null
+++ b/huobi/service/wallet/get_sub_user_deposit_history.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.wallet import *
+from huobi.utils import *
+
+
+class GetSubUserDepositHistoryService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v2/sub-user/query-deposit"
+
+ def parse(dict_data):
+ deposit_history = DepositHistory()
+ deposit_history.nextId = dict_data.get("nextId", 0)
+ json_data_list = dict_data.get("data", [])
+ deposit_history_item_list = default_parse_list_dict(json_data_list, DepositHistoryItem)
+ deposit_history.data = deposit_history_item_list
+ return deposit_history
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.GET_SIGN, channel, self.params, parse)
+
+
+
diff --git a/huobi/service/wallet/post_cancel_withdraw.py b/huobi/service/wallet/post_cancel_withdraw.py
new file mode 100644
index 0000000..1141aca
--- /dev/null
+++ b/huobi/service/wallet/post_cancel_withdraw.py
@@ -0,0 +1,28 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostCancelWithdrawService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ withdraw_id_params = self.params["withdraw-id"]
+ def get_channel():
+ path = "/v1/dw/withdraw-virtual/{}/cancel"
+ return path.format(withdraw_id_params)
+
+ def parse(dict_data):
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, get_channel(), self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/service/wallet/post_create_withdraw.py b/huobi/service/wallet/post_create_withdraw.py
new file mode 100644
index 0000000..7fb66cb
--- /dev/null
+++ b/huobi/service/wallet/post_create_withdraw.py
@@ -0,0 +1,26 @@
+from huobi.connection.restapi_sync_client import RestApiSyncClient
+from huobi.constant import *
+from huobi.model.trade import *
+from huobi.utils import *
+from huobi.utils.json_parser import default_parse_data_as_long
+
+
+class PostCreateWithdrawService:
+
+ def __init__(self, params):
+ self.params = params
+
+ def request(self, **kwargs):
+ channel = "/v1/dw/withdraw/api/create"
+
+ def parse(dict_data):
+ # return dict_data.get("data", 0)
+ return default_parse_data_as_long(dict_data, None)
+
+ return RestApiSyncClient(**kwargs).request_process(HttpMethod.POST_SIGN, channel, self.params, parse)
+
+
+
+
+
+
diff --git a/huobi/utils/__init__.py b/huobi/utils/__init__.py
new file mode 100644
index 0000000..7bf0054
--- /dev/null
+++ b/huobi/utils/__init__.py
@@ -0,0 +1,11 @@
+from huobi.utils.print_mix_object import PrintMix, PrintBasic, PrintList
+from huobi.utils.channels import *
+from huobi.utils.channels_request import *
+from huobi.utils.json_parser import default_parse, default_parse_list_dict, default_parse_fill_directly
+from huobi.utils.api_signature import create_signature, utc_now
+from huobi.utils.api_signature_v2 import create_signature_v2, utc_now
+from huobi.utils.url_params_builder import UrlParamsBuilder
+from huobi.utils.time_service import get_current_timestamp, convert_cst_in_millisecond_to_utc, convert_cst_in_second_to_utc
+from huobi.utils.input_checker import check_symbol, check_symbol_list, check_currency, check_range, check_should_none, \
+ check_should_not_none, check_list, greater_or_equal, format_date
+from huobi.utils.log_info import LogLevel, LogInfo
diff --git a/huobi/utils/api_signature.py b/huobi/utils/api_signature.py
new file mode 100644
index 0000000..bb3e22c
--- /dev/null
+++ b/huobi/utils/api_signature.py
@@ -0,0 +1,36 @@
+import base64
+import hashlib
+import hmac
+import datetime
+from urllib import parse
+import urllib.parse
+from huobi.exception.huobi_api_exception import HuobiApiException
+
+
+def create_signature(api_key, secret_key, method, url, builder):
+ if api_key is None or secret_key is None or api_key == "" or secret_key == "":
+ raise HuobiApiException(HuobiApiException.KEY_MISSING, "API key and secret key are required")
+
+ timestamp = utc_now()
+ builder.put_url("AccessKeyId", api_key)
+ builder.put_url("SignatureVersion", "2")
+ builder.put_url("SignatureMethod", "HmacSHA256")
+ builder.put_url("Timestamp", timestamp)
+
+ host = urllib.parse.urlparse(url).hostname
+ path = urllib.parse.urlparse(url).path
+
+ # 对参数进行排序:
+ keys = sorted(builder.param_map.keys())
+ # 加入&
+ qs0 = '&'.join(['%s=%s' % (key, parse.quote(builder.param_map[key], safe='')) for key in keys])
+ # 请求方法,域名,路径,参数 后加入`\n`
+ payload0 = '%s\n%s\n%s\n%s' % (method, host, path, qs0)
+ dig = hmac.new(secret_key.encode('utf-8'), msg=payload0.encode('utf-8'), digestmod=hashlib.sha256).digest()
+ # 进行base64编码
+ s = base64.b64encode(dig).decode()
+ builder.put_url("Signature", s)
+
+
+def utc_now():
+ return datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
diff --git a/huobi/utils/api_signature_v2.py b/huobi/utils/api_signature_v2.py
new file mode 100644
index 0000000..c5e1bbd
--- /dev/null
+++ b/huobi/utils/api_signature_v2.py
@@ -0,0 +1,60 @@
+import base64
+import hashlib
+import hmac
+import datetime
+from urllib import parse
+import urllib.parse
+from huobi.exception.huobi_api_exception import HuobiApiException
+
+
+def create_signature_v2(api_key, secret_key, method, url, builder):
+ if api_key is None or secret_key is None or api_key == "" or secret_key == "":
+ raise HuobiApiException(HuobiApiException.KEY_MISSING, "API key and secret key are required")
+
+ timestamp = utc_now()
+ builder.put_url("accessKey", api_key)
+ builder.put_url("signatureVersion", "2.1")
+ builder.put_url("signatureMethod", "HmacSHA256")
+ builder.put_url("timestamp", timestamp)
+
+ host = urllib.parse.urlparse(url).hostname
+ path = urllib.parse.urlparse(url).path
+
+ # 对参数进行排序:
+ keys = sorted(builder.param_map.keys())
+ # 加入&
+ qs0 = '&'.join(['%s=%s' % (key, parse.quote(builder.param_map[key], safe='')) for key in keys])
+ # 请求方法,域名,路径,参数 后加入`\n`
+ payload0 = '%s\n%s\n%s\n%s' % (method, host, path, qs0)
+ dig = hmac.new(secret_key.encode('utf-8'), msg=payload0.encode('utf-8'), digestmod=hashlib.sha256).digest()
+ # 进行base64编码
+ s = base64.b64encode(dig).decode()
+ builder.put_url("signature", s)
+ builder.put_url("authType", "api")
+
+ params = {
+ "accessKey": api_key,
+ "signatureVersion": "2.1",
+ "signatureMethod": "HmacSHA256",
+ "timestamp": timestamp,
+ "signature":s,
+ "authType":"api"
+ }
+
+ builder.put_url("action", "req")
+ builder.put_url("ch", "auth")
+ builder.put_url("params", params)
+
+ """
+ # for test
+ ret_maps = {
+ "action": "req",
+ "ch": "auth",
+ "params" : params
+ }
+
+ return json.dumps(ret_maps)
+ """
+
+def utc_now():
+ return datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
diff --git a/huobi/utils/channel_parser.py b/huobi/utils/channel_parser.py
new file mode 100644
index 0000000..3e29e70
--- /dev/null
+++ b/huobi/utils/channel_parser.py
@@ -0,0 +1,5 @@
+class ChannelParser:
+ def __init__(self, input):
+ fields = input.split(".")
+ if len(fields) >= 2:
+ self.symbol = fields[1]
diff --git a/huobi/utils/channels.py b/huobi/utils/channels.py
new file mode 100644
index 0000000..96dfac1
--- /dev/null
+++ b/huobi/utils/channels.py
@@ -0,0 +1,83 @@
+import json
+from huobi.utils.time_service import get_current_timestamp
+from huobi.constant import DepthStep
+
+
+def kline_channel(symbol, interval):
+ channel = dict()
+ channel["sub"] = "market." + symbol + ".kline." + interval
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def trade_detail_channel(symbol):
+ channel = dict()
+ channel["sub"] = "market." + symbol + ".trade.detail"
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def price_depth_channel(symbol, step_type=DepthStep.STEP0):
+ channel = dict()
+ channel["sub"] = "market." + symbol + ".depth." + step_type
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def price_depth_bbo_channel(symbol):
+ channel = dict()
+ channel["sub"] = "market." + symbol + ".bbo"
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def orders_update_channel(symbol):
+ channel = dict()
+ channel["action"] = "sub"
+ channel["ch"] = "orders#{symbol}".format(symbol=symbol)
+ return json.dumps(channel)
+
+
+def market_detail_channel(symbol):
+ channel = dict()
+ channel["sub"] = "market." + symbol + ".detail"
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def accounts_update_channel(mode=0):
+ channel = dict()
+ channel["action"] = "sub"
+ if mode is None:
+ channel["ch"] = "accounts.update"
+ else:
+ channel["ch"] = "accounts.update#{mode}".format(mode=mode)
+ return json.dumps(channel)
+
+
+def mbp_increase_channel(symbol, levels):
+ channel = dict()
+ channel["sub"] = "market.{symbol}.mbp.{levels}".format(symbol=symbol, levels=levels)
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def mbp_full_channel(symbol, levels):
+ channel = dict()
+ channel["sub"] = "market.{symbol}.mbp.refresh.{levels}".format(symbol=symbol, levels=levels)
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def request_mbp_channel(symbol, levels):
+ channel = dict()
+ channel["req"] = "market.{symbol}.mbp.{levels}".format(symbol=symbol, levels=levels)
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def trade_clearing_channel(symbol="*"):
+ channel = dict()
+ channel["action"] = "sub"
+ channel["ch"] = "trade.clearing#" + symbol
+ return json.dumps(channel)
diff --git a/huobi/utils/channels_request.py b/huobi/utils/channels_request.py
new file mode 100644
index 0000000..a9b14b2
--- /dev/null
+++ b/huobi/utils/channels_request.py
@@ -0,0 +1,79 @@
+import json
+from huobi.utils.time_service import get_current_timestamp
+
+def dict_add_new(old_dict, new_dict):
+ if old_dict is None:
+ old_dict = {}
+
+ if new_dict and len(new_dict):
+ for key_val, val in new_dict.items():
+ if val:
+ exist_val = old_dict.get(key_val, None)
+ if exist_val and len(str(exist_val)):
+ pass
+ else:
+ old_dict[key_val] = str(val)
+
+ return old_dict
+
+def request_kline_channel(symbol, interval, from_ts_second = None, to_ts_second = None):
+ channel = dict()
+ channel["req"] = "market." + symbol + ".kline." + interval
+ channel["id"] = str(get_current_timestamp())
+ if from_ts_second:
+ channel["from"] = int(from_ts_second)
+ if to_ts_second:
+ channel["to"] = int(to_ts_second)
+ return json.dumps(channel)
+
+
+def request_trade_detail_channel(symbol):
+ channel = dict()
+ channel["req"] = "market." + symbol + ".trade.detail"
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+
+def request_price_depth_channel(symbol, step_type = "step0"):
+ channel = dict()
+ channel["req"] = "market." + symbol + ".depth." + step_type
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+def request_market_detail_channel(symbol):
+ channel = dict()
+ channel["req"] = "market." + symbol + ".detail"
+ channel["id"] = str(get_current_timestamp())
+ return json.dumps(channel)
+
+def request_account_list_channel(client_req_id = None):
+ channel = dict()
+ channel["op"] = "req"
+ channel["topic"] = "accounts.list"
+ channel["cid"] = str(client_req_id) if client_req_id else str(get_current_timestamp())
+ return json.dumps(channel)
+
+def request_order_list_channel(symbol, account_id, states_str= None, client_req_id = None, more_key={}):
+ channel = dict()
+ try:
+ channel["op"] = "req"
+ channel["account-id"] = account_id
+ channel["topic"] = "orders.list"
+ channel["symbol"] = symbol
+ if states_str and len(states_str):
+ channel["states"] = str(states_str)
+ channel["cid"] = str(client_req_id) if client_req_id else str(get_current_timestamp())
+ channel = dict_add_new(channel, more_key)
+
+ except Exception as e:
+ print(e)
+ return json.dumps(channel)
+
+def request_order_detail_channel(order_id, client_req_id = None):
+ channel = dict()
+ channel["op"] = "req"
+ channel["topic"] = "orders.detail"
+ channel["order-id"] = str(order_id)
+ channel["cid"] = str(client_req_id) if client_req_id else str(get_current_timestamp())
+ return json.dumps(channel)
+
diff --git a/huobi/utils/etf_result.py b/huobi/utils/etf_result.py
new file mode 100644
index 0000000..fdf693c
--- /dev/null
+++ b/huobi/utils/etf_result.py
@@ -0,0 +1,29 @@
+def etf_result_check(code):
+ code = int(code)
+ if code == 200:
+ return ""
+ elif code == 10400:
+ return "Invalid ETF name"
+ elif code == 13403:
+ return "Insufficient asset to create ETF"
+ elif code == 13404:
+ return "Create and redemption disabled due to system setup"
+ elif code == 13405:
+ return "Create and redemption disabled due to configuration issue"
+ elif code == 13406:
+ return "Invalid API call"
+ elif code == 13410:
+ return "API authentication fails"
+ elif code == 13500:
+ return "System error"
+ elif code == 13601:
+ return "Create and redemption disabled during rebalance"
+ elif code == 13603:
+ return "Create and redemption disabled due to other reason"
+ elif code == 13604:
+ return "Create suspended"
+ elif code == 13605:
+ return "Redemption suspended"
+ elif code == 13606:
+ return "Amount incorrect. For the cases when creation amount or redemption amount is not in the range of min/max amount"
+ return ""
diff --git a/huobi/utils/input_checker.py b/huobi/utils/input_checker.py
new file mode 100644
index 0000000..2de456c
--- /dev/null
+++ b/huobi/utils/input_checker.py
@@ -0,0 +1,90 @@
+import re
+import time
+from huobi.exception.huobi_api_exception import HuobiApiException
+from huobi.constant.definition import *
+
+reg_ex = "[ _`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]|\n|\t"
+
+
+def check_symbol(symbol):
+ if not isinstance(symbol, str):
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] symbol must be string")
+ if re.match(reg_ex, symbol):
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] " + symbol + " is invalid symbol")
+
+
+def check_time_in_force(time_in_force, order_type):
+ if time_in_force is None:
+ return
+
+ if order_type in [OrderType.BUY_MARKET, OrderType.SELL_MARKET] \
+ and time_in_force in [TimeInForceType.GTC, TimeInForceType.BOC, TimeInForceType.FOK]:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] timeInForce not supported for market order")
+
+
+def check_symbol_list(symbols):
+ if not isinstance(symbols, list):
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] symbols in subscription is not a list")
+ for symbol in symbols:
+ check_symbol(symbol)
+
+
+def check_currency(currency):
+ if not isinstance(currency, str):
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] currency must be string")
+ if re.match(reg_ex, currency) is not None:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] " + currency + " is invalid currency")
+
+
+def check_range(value, min_value, max_value, name):
+ if value is None:
+ return
+ if min_value > value or value > max_value:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR,
+ "[Input] " + name + " is out of bound. " + str(value) + " is not in [" + str(
+ min_value) + "," + str(max_value) + "]")
+
+
+def check_should_not_none(value, name):
+ if value is None:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] " + name + " should not be null")
+
+
+def check_should_none(value, name):
+ if value is not None:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] " + name + " should be null")
+
+
+def check_in_list(value, list_configed, name):
+ if (value is not None) and (value not in list_configed):
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR,
+ "[Input] " + name + " should be one in " + (",".join(list_configed)))
+
+
+def check_list(list_value, min_value, max_value, name):
+ if list_value is None:
+ return
+ if len(list_value) > max_value:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR,
+ "[Input] " + name + " is out of bound, the max size is " + str(max_value))
+ if len(list_value) < min_value:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR,
+ "[Input] " + name + " should contain " + str(min_value) + " item(s) at least")
+
+
+def greater_or_equal(value, base, name):
+ if value is not None and value < base:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR,
+ "[Input] " + name + " should be greater than " + base)
+
+
+def format_date(value, name):
+ if value is None:
+ return None
+ if not isinstance(value, str):
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] " + name + " must be string")
+ try:
+ new_time = time.strptime(value, "%Y-%m-%d")
+ return time.strftime("%Y-%m-%d", new_time)
+ except:
+ raise HuobiApiException(HuobiApiException.INPUT_ERROR, "[Input] " + name + " is not invalid date format")
diff --git a/huobi/utils/json_parser.py b/huobi/utils/json_parser.py
new file mode 100644
index 0000000..61c38fb
--- /dev/null
+++ b/huobi/utils/json_parser.py
@@ -0,0 +1,122 @@
+from huobi.utils.print_mix_object import *
+
+
+def key_trans(key_origin):
+ if key_origin and len(key_origin) > 1:
+ return key_origin.replace("-", "_")
+ else:
+ return ""
+
+
+def fill_obj(dict_data, class_name=object):
+ obj = class_name()
+ for ks, vs in dict_data.items():
+ obj_key = key_trans(ks)
+ # print("===== fill_obj =====", ks, obj_key, str(vs))
+ if hasattr(obj, obj_key):
+ setattr(obj, obj_key, vs)
+ continue
+ return obj
+
+
+def fill_obj_list(list_data, class_name):
+ if (TypeCheck.is_list(list_data)):
+ inner_obj_list = list()
+ for idx, row in enumerate(list_data):
+ inner_obj = fill_obj(row, class_name)
+ inner_obj_list.append(inner_obj)
+ return inner_obj_list
+
+ return list()
+
+
+def default_parse(dict_data, outer_class_name=object, inner_class_name=object):
+ from huobi.utils.print_mix_object import TypeCheck
+ rsp_obj = outer_class_name()
+
+ for outer_key, outer_value in dict_data.items():
+ obj_key = key_trans(outer_key)
+ # print("===", outer_key, obj_key, str(outer_value))
+ # PrintBasic.print_basic_bool(hasattr(rsp_obj, obj_key))
+ if hasattr(rsp_obj, obj_key):
+ new_value = outer_value
+ # print("==========", type(outer_value), outer_value)
+ if (TypeCheck.is_list(outer_value)):
+ new_value = fill_obj_list(outer_value, inner_class_name)
+ elif (TypeCheck.is_dict(outer_value)):
+ new_value = fill_obj(outer_value, inner_class_name)
+
+ setattr(rsp_obj, obj_key, new_value)
+ continue
+
+ return rsp_obj
+
+
+def default_parse_data_as_long(ret_original_json, key_name=None, default_value=0):
+ if ret_original_json:
+ # from data get value by key_name
+ if key_name and len(key_name):
+ data_json = ret_original_json.get("data", {})
+ ret_val = data_json.get(key_name, default_value)
+ else:
+ # get data value
+ ret_val = ret_original_json.get("data", 0)
+ return None if ret_val is None else int(ret_val)
+ else:
+ return default_value
+
+
+def default_parse_list_dict(inner_data, inner_class_name=object, default_value=None):
+ from huobi.utils.print_mix_object import TypeCheck
+
+ new_value = default_value
+ if inner_data and len(inner_data):
+ if (TypeCheck.is_list(inner_data)):
+ new_value = fill_obj_list(inner_data, inner_class_name)
+ elif (TypeCheck.is_dict(inner_data)):
+ new_value = fill_obj(inner_data, inner_class_name)
+ else:
+ new_value = default_value
+
+ return new_value
+
+
+def default_parse_fill_directly(dict_data, outer_class_name=object):
+ rsp_obj = outer_class_name()
+
+ for outer_key, outer_value in dict_data.items():
+ obj_key = key_trans(outer_key)
+ if hasattr(rsp_obj, obj_key):
+ new_value = outer_value
+ setattr(rsp_obj, obj_key, new_value)
+ continue
+
+ return rsp_obj
+
+
+if __name__ == "__main__":
+ # json_str = """{"id":1571037900,"open":8306.850000000000000000,"close":8307.990000000000000000,"low":8305.800000000000000000,"high":8308.000000000000000000,"amount":4.314954363225502510,"vol":35845.853207199999993966820000000000000000,"count":45}"""
+ # obj = parse(json_str, outer_class_name=Candlestick, inner_class_name=None)
+
+ # obj.print_object()
+
+ # json_str = """{"status":"ok","ch":"market.btcusdt.kline.1min","ts":1571038189274,"data":[{"id":1571038140,"open":8304.130000000000000000,"close":8305.000000000000000000,"low":8300.010000000000000000,"high":8305.000000000000000000,"amount":41.791380418639796061,"vol":347038.873910589999990613890000000000000000,"count":165},{"id":1571038080,"open":8306.060000000000000000,"close":8304.130000000000000000,"low":8304.130000000000000000,"high":8306.060000000000000000,"amount":3.440305012281757977,"vol":28571.704804969999998985000000000000000000,"count":70},{"id":1571038020,"open":8303.890000000000000000,"close":8305.990000000000000000,"low":8303.890000000000000000,"high":8307.000000000000000000,"amount":6.164746957847080072,"vol":51200.094613737306439860000000000000000000,"count":89},{"id":1571037960,"open":8308.000000000000000000,"close":8303.660000000000000000,"low":8303.610000000000000000,"high":8308.430000000000000000,"amount":9.449641840557003640,"vol":78488.394246429999983963200000000000000000,"count":128},{"id":1571037900,"open":8306.850000000000000000,"close":8307.990000000000000000,"low":8305.800000000000000000,"high":8308.000000000000000000,"amount":4.314954363225502510,"vol":35845.853207199999993966820000000000000000,"count":45},{"id":1571037840,"open":8305.290000000000000000,"close":8306.820000000000000000,"low":8305.040000000000000000,"high":8307.300000000000000000,"amount":3.661411574656800047,"vol":30412.286333819999984471300000000000000000,"count":68},{"id":1571037780,"open":8306.500000000000000000,"close":8305.490000000000000000,"low":8305.100000000000000000,"high":8307.690000000000000000,"amount":2.958112866447550895,"vol":24569.573928609999984901150000000000000000,"count":59},{"id":1571037720,"open":8306.300000000000000000,"close":8307.560000000000000000,"low":8305.440000000000000000,"high":8309.000000000000000000,"amount":8.506926000000000000,"vol":70665.848347860000000000000000000000000000,"count":90},{"id":1571037660,"open":8306.920000000000000000,"close":8306.300000000000000000,"low":8306.200000000000000000,"high":8307.000000000000000000,"amount":5.084311000000000000,"vol":42233.572201310000000000000000000000000000,"count":57},{"id":1571037600,"open":8307.200000000000000000,"close":8306.920000000000000000,"low":8306.920000000000000000,"high":8309.160000000000000000,"amount":4.540090141728746275,"vol":37715.585646199999989163880000000000000000,"count":54}]}"""
+ # obj_event = parse(json_str, outer_class_name=CandlestickRsp, inner_class_name=Candlestick)
+ # obj_event.print_object()
+
+ # json_str = """{"status":"ok","ch":"market.btcusdt.kline.1min","ts":1571038189274,"data":{"id":1571038140,"open":8304.130000000000000000,"close":8305.000000000000000000,"low":8300.010000000000000000,"high":8305.000000000000000000,"amount":41.791380418639796061,"vol":347038.873910589999990613890000000000000000,"count":165}}"""
+ # obj_event = parse(json_str, outer_class_name=CandlestickEvent, inner_class_name=Candlestick)
+ # obj_event.print_object()
+
+ # json_str = """{"status":"ok","ch":"market.btcusdt.kline.1min","ts":1571038189274,"data":{"id":1571038140,"open":8304.130000000000000000,"close":8305.000000000000000000,"low":8300.010000000000000000,"high":8305.000000000000000000,"amount":41.791380418639796061,"vol":347038.873910589999990613890000000000000000,"count":165}}"""
+ # ret = default_parse_restful(json_str, inner_class_name=Candlestick, default_value=None)
+ # ret.print_object()
+
+ # json_str = """{"status":"ok","ch":"market.btcusdt.kline.1min","ts":1571038189274,"data":[{"id":1571038140,"open":8304.130000000000000000,"close":8305.000000000000000000,"low":8300.010000000000000000,"high":8305.000000000000000000,"amount":41.791380418639796061,"vol":347038.873910589999990613890000000000000000,"count":165},{"id":1571038080,"open":8306.060000000000000000,"close":8304.130000000000000000,"low":8304.130000000000000000,"high":8306.060000000000000000,"amount":3.440305012281757977,"vol":28571.704804969999998985000000000000000000,"count":70},{"id":1571038020,"open":8303.890000000000000000,"close":8305.990000000000000000,"low":8303.890000000000000000,"high":8307.000000000000000000,"amount":6.164746957847080072,"vol":51200.094613737306439860000000000000000000,"count":89},{"id":1571037960,"open":8308.000000000000000000,"close":8303.660000000000000000,"low":8303.610000000000000000,"high":8308.430000000000000000,"amount":9.449641840557003640,"vol":78488.394246429999983963200000000000000000,"count":128},{"id":1571037900,"open":8306.850000000000000000,"close":8307.990000000000000000,"low":8305.800000000000000000,"high":8308.000000000000000000,"amount":4.314954363225502510,"vol":35845.853207199999993966820000000000000000,"count":45},{"id":1571037840,"open":8305.290000000000000000,"close":8306.820000000000000000,"low":8305.040000000000000000,"high":8307.300000000000000000,"amount":3.661411574656800047,"vol":30412.286333819999984471300000000000000000,"count":68},{"id":1571037780,"open":8306.500000000000000000,"close":8305.490000000000000000,"low":8305.100000000000000000,"high":8307.690000000000000000,"amount":2.958112866447550895,"vol":24569.573928609999984901150000000000000000,"count":59},{"id":1571037720,"open":8306.300000000000000000,"close":8307.560000000000000000,"low":8305.440000000000000000,"high":8309.000000000000000000,"amount":8.506926000000000000,"vol":70665.848347860000000000000000000000000000,"count":90},{"id":1571037660,"open":8306.920000000000000000,"close":8306.300000000000000000,"low":8306.200000000000000000,"high":8307.000000000000000000,"amount":5.084311000000000000,"vol":42233.572201310000000000000000000000000000,"count":57},{"id":1571037600,"open":8307.200000000000000000,"close":8306.920000000000000000,"low":8306.920000000000000000,"high":8309.160000000000000000,"amount":4.540090141728746275,"vol":37715.585646199999989163880000000000000000,"count":54}]}"""
+ # ret = default_parse_restful(json_str, inner_class_name=Candlestick, default_value=None)
+ # if ret and len(ret):
+ # for row in ret:
+ # row.print_object()
+ # print("========")
+
+ pass
diff --git a/huobi/utils/log_info.py b/huobi/utils/log_info.py
new file mode 100644
index 0000000..41da0d5
--- /dev/null
+++ b/huobi/utils/log_info.py
@@ -0,0 +1,31 @@
+class LogLevel:
+ DEBUG = "debug"
+ INFO = "info"
+ WARNINGS = "warning"
+ ERROR = "error"
+ FATAL = "fatal"
+
+
+class LogInfo:
+ @staticmethod
+ def output(message, log_level=LogLevel.DEBUG):
+ # if (message and len(message)):
+ # if log_level == LogLevel.DEBUG:
+ # logging.debug(message)
+ # elif log_level == LogLevel.INFO:
+ # logging.info(message)
+ # elif log_level == LogLevel.WARNINGS:
+ # logging.warnings(message)
+ # elif log_level == LogLevel.ERROR:
+ # logging.error(message)
+ # elif log_level == LogLevel.FATAL:
+ # logging.fatal(message)
+
+ print(message)
+
+ @staticmethod
+ def output_list(data_list, log_level=LogLevel.DEBUG):
+ if data_list and len(data_list):
+ for obj in data_list:
+ obj.print_object()
+ print()
diff --git a/huobi/utils/print_mix_object.py b/huobi/utils/print_mix_object.py
new file mode 100644
index 0000000..894dc43
--- /dev/null
+++ b/huobi/utils/print_mix_object.py
@@ -0,0 +1,232 @@
+import sys
+import time
+
+BASIC_DATA_TYPE = (int, str, float)
+BASIC_DATA_TYPE_BOOL = (bool)
+
+TYPE_BASIC = "type_basic"
+TYPE_BOOL = "type_bool"
+TYPE_OBJECT = "type_object"
+TYPE_LIST = "type_list"
+TYPE_DICT = "type_dict"
+TYPE_UNDEFINED = "type_undefined"
+
+
+class TypeCheck:
+ @staticmethod
+ def is_list(obj):
+ return type(obj) == list and isinstance(obj, list)
+
+ @staticmethod
+ def is_dict(obj):
+ return type(obj) == dict and isinstance(obj, dict)
+
+ @staticmethod
+ def is_object(obj):
+ return isinstance(obj, object)
+
+ @staticmethod
+ def is_basic(obj):
+ return isinstance(obj, BASIC_DATA_TYPE)
+
+ @staticmethod
+ def is_bool(obj):
+ return isinstance(obj, bool)
+
+ @staticmethod
+ def get_obj_type(obj):
+ if TypeCheck.is_basic(obj):
+ return TYPE_BASIC
+ elif TypeCheck.is_bool(obj):
+ return TYPE_BOOL
+ elif TypeCheck.is_list(obj):
+ return TYPE_LIST
+ elif TypeCheck.is_dict(obj):
+ return TYPE_DICT
+ elif TypeCheck.is_object(obj):
+ return TYPE_OBJECT
+ else:
+ return TYPE_UNDEFINED
+
+
+class PrintBasic:
+ @staticmethod
+ def print_basic(data, name=None):
+ if name and len(name):
+ print(str(name) + " : " + str(data))
+ else:
+ print(str(data))
+
+ @staticmethod
+ def print_basic_bool(data, name=None):
+ bool_desc = "True"
+ if not data:
+ bool_desc = "False"
+
+ if name and len(name):
+ print(str(name) + " : " + str(bool_desc))
+ else:
+ print(str(bool_desc))
+
+ @staticmethod
+ def print_obj(obj):
+ if not obj:
+ return -1
+
+ members = [attr for attr in dir(obj) if not callable(attr) and not attr.startswith("__")]
+ for member_def in members:
+ val_str = str(getattr(obj, member_def))
+ print(member_def + ":" + val_str)
+ return 0
+
+
+class PrintList:
+ @staticmethod
+ def print_list_data(obj):
+ if not obj:
+ print("object is None")
+ return -1
+
+ if TypeCheck.get_obj_type(obj) == TYPE_LIST:
+ for idx, row in enumerate(obj):
+ PrintBasic.print_basic(row)
+ else:
+ return -2
+
+ return 0
+
+ @staticmethod
+ def print_origin_object(obj):
+ if not obj:
+ print("object is None")
+ return -1
+ obj_type = TypeCheck.get_obj_type(obj)
+
+ if obj_type == TYPE_BASIC:
+ PrintBasic.print_basic(obj)
+ elif obj_type == TYPE_BOOL:
+ PrintBasic.print_basic_bool(obj)
+ elif obj_type == TYPE_OBJECT:
+ PrintBasic.print_obj(obj)
+ else:
+ return 1
+
+ return 0
+
+ @staticmethod
+ def print_object_list(obj_list):
+ if not obj_list:
+ return -1
+
+ obj_type = TypeCheck.get_obj_type(obj_list)
+ if obj_type != TYPE_LIST:
+ return -2
+
+ print ("data count : ", (len(obj_list)))
+ print ("\n")
+ for idx, row in enumerate(obj_list):
+ print("data number " + (str(idx)) + " :")
+ PrintList.print_origin_object(row)
+ print("\n")
+ print("\n\n")
+
+ return 0
+
+ @staticmethod
+ def print_object_dict(obj_dict):
+ if not obj_dict:
+ return -1
+
+ obj_type = TypeCheck.get_obj_type(obj_dict)
+ if obj_type != TYPE_DICT:
+ return -2
+
+ print ("data count : ", (len(obj_dict)))
+ print ("\n")
+ for key, row in obj_dict.items():
+ PrintBasic.print_basic(str(key) + " :")
+ PrintList.print_origin_object(row)
+ print("\n")
+ print("\n\n")
+
+ return 0
+
+
+class PrintMix:
+ @staticmethod
+ def print_data(data):
+ if not data:
+ print (sys._getframe().f_code.co_name + " none data")
+ return -1
+
+ obj_type = TypeCheck.get_obj_type(data)
+
+ if obj_type == TYPE_BASIC:
+ PrintBasic.print_basic(data)
+ elif obj_type == TYPE_BOOL:
+ PrintBasic.print_basic_bool(data)
+ elif obj_type == TYPE_LIST:
+ PrintList.print_object_list(data)
+ elif obj_type == TYPE_DICT:
+ PrintList.print_object_dict(data)
+ elif obj_type == TYPE_OBJECT:
+ PrintList.print_origin_object(data)
+ else:
+ print (sys._getframe().f_code.co_name + " enter unknown")
+ return -2
+
+ return 0
+
+
+
+class PrintDate:
+ @staticmethod
+ def timestamp_to_date(ts_minsecond):
+ try:
+ ts_minsecond = int(ts_minsecond)
+ time_local = time.localtime(int(ts_minsecond / 1000))
+ dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
+ print("ping " + str(ts_minsecond) + ":" + dt)
+ except Exception as e:
+ print(e)
+
+"""
+if __name__ == "__main__":
+ ping_ts = 1569319465421
+ PrintDate.timestamp_to_date(ping_ts)
+ PrintDate.timestamp_to_date(int(ping_ts), ("ping " + str(ping_ts)))
+"""
+
+
+if __name__ == "__main__":
+ """
+ from huobi.model.symbol import Symbol
+
+ symbol_1 = Symbol()
+ symbol_1.amount_precision = 10009
+ symbol_1.symbol = "btcusdt"
+
+ symbol_2 = Symbol()
+ symbol_2.amount_precision = 28
+ symbol_2.symbol = "htusdt"
+
+ symbol_3 = Symbol()
+ symbol_3.amount_precision = 26
+ symbol_3.symbol = "eosusdt"
+
+ symbol_list = [symbol_1, symbol_2, symbol_3]
+ symbol_dict = {"one": symbol_1, "two": symbol_2, "three": symbol_3}
+ PrintMix.print_data(symbol_list)
+ PrintMix.print_data(symbol_dict)
+
+ print(type(symbol_list) == list)
+ print(type(symbol_dict) == dict)
+ print(type(symbol_list) == object)
+ print(isinstance(symbol_list, list))
+ print(isinstance(symbol_list, object))
+ print(isinstance(symbol_dict, dict))
+ print(isinstance(symbol_dict, object))
+ """
+
+ a=['s', 'h', 'i']
+ PrintList.print_list_data(a)
diff --git a/huobi/utils/time_service.py b/huobi/utils/time_service.py
new file mode 100644
index 0000000..d2af8e9
--- /dev/null
+++ b/huobi/utils/time_service.py
@@ -0,0 +1,22 @@
+import time
+
+
+def get_current_timestamp():
+ return int(round(time.time() * 1000))
+
+
+def convert_cst_in_second_to_utc(time_in_second):
+ time_in_second = int(time_in_second)
+ if time_in_second > 946656000 and time_in_second < 946656000000:
+ return (time_in_second - 8 * 60 * 60) * 1000
+ else:
+ return 0
+
+
+def convert_cst_in_millisecond_to_utc(time_in_ms):
+ time_in_ms = int(time_in_ms)
+ if time_in_ms > 946656000000:
+ return time_in_ms - 8 * 60 * 60 * 1000
+ else:
+ return 0
+
diff --git a/huobi/utils/url_params_builder.py b/huobi/utils/url_params_builder.py
new file mode 100644
index 0000000..fd689cc
--- /dev/null
+++ b/huobi/utils/url_params_builder.py
@@ -0,0 +1,33 @@
+import json
+import urllib.parse
+
+
+class UrlParamsBuilder(object):
+
+ def __init__(self):
+ self.param_map = dict()
+ self.post_map = dict()
+ self.post_list = list()
+
+ def put_url(self, name, value):
+ if value is not None:
+ if isinstance(value, (list, dict)):
+ self.param_map[name] = value
+ else:
+ self.param_map[name] = str(value)
+
+ def put_post(self, name, value):
+ if value is not None:
+ if isinstance(value, (list, dict)):
+ self.post_map[name] = value
+ else:
+ self.post_map[name] = str(value)
+
+ def build_url(self):
+ if len(self.param_map) == 0:
+ return ""
+ encoded_param = urllib.parse.urlencode(self.param_map)
+ return "?" + encoded_param
+
+ def build_url_to_json(self):
+ return json.dumps(self.param_map)
diff --git a/huobi_alert.py b/huobi_alert.py
new file mode 100644
index 0000000..08e9982
--- /dev/null
+++ b/huobi_alert.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+from utils import utility
+from utils import config
+
+from huobi.client.account import AccountClient
+from huobi.client.trade import TradeClient
+from huobi.constant import *
+from huobi.utils import *
+
+
+
+if __name__ == '__main__':
+
+ config.loads('./conf_huobi/config.json')
+ order_list_submitted = []
+
+ trade_client = TradeClient(api_key=config.api_key, secret_key=config.api_secret)
+ account_client = AccountClient(api_key=config.api_key, secret_key=config.api_secret)
+
+ account_spot = account_client.get_account_by_type_and_symbol(account_type=AccountType.SPOT, symbol=None)
+ account_id_test = account_spot.id
+ direct_tmp = QueryDirection.NEXT
+ LogInfo.output("==============test case 1 for {direct}===============".format(direct=direct_tmp))
+ for symbol in config.symbol_spot:
+ print(symbol)
+ list_obj = trade_client.get_open_orders(symbol=symbol, account_id=account_id_test, direct=direct_tmp)
+ for order in list_obj:
+ if order.state == 'submitted':
+ order_list_submitted.append(str(order.id))
+ print(order.id)
+ print(order)
+ #LogInfo.output_list(list_obj)
+
+
+ # direct_tmp = QueryDirection.PREV
+ # LogInfo.output("==============test case 2 for {direct}===============".format(direct=direct_tmp))
+ # list_obj = trade_client.get_open_orders(symbol=symbol, account_id=account_id_test, direct=direct_tmp)
+ # LogInfo.output_list(list_obj)
+
+
+
+
+ # #启动新实例 发个通知
+ # utility.alert(f"{utility.cn_time()} 火币订单监控启动")
+
+ # while True:
+ # try:
+ # time.sleep(20)
+ #
+ # except Exception as error:
+ # utility.alert(error)
+ # time.sleep(5)
+
diff --git a/k8s.md b/k8s.md
new file mode 100644
index 0000000..f6ecb4d
--- /dev/null
+++ b/k8s.md
@@ -0,0 +1,57 @@
+
+# configmap
+```yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: binance-config-qy-xrp
+ namespace: sre
+data:
+ config.json: |
+ {
+ "platform": "binance_spot",
+ "symbol": "XRPUSDT",
+ "api_key": "your api_ky 1111111111111111111111111111111111111",
+ "api_secret": "your api_secret 222222222222222222222222222222",
+ "gap_percent": 0.005,
+ "quantity": 40,
+ "min_price": 0.00001,
+ "min_qty": 40,
+ "max_orders": 5,
+ "proxy_host": "",
+ "proxy_port": 0
+ }
+```
+
+# k8s Deployment api-wechat
+
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: binance-trader-xrp
+ namespace: sre
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ binance: trader
+ template:
+ metadata:
+ labels:
+ binance: trader
+ spec:
+ imagePullSecrets:
+ - name: wzaqqqaliyunsecret
+ containers:
+ - name: binance-trader
+ image: mmmmmmmmmmmmmmmm/binance_grid_trader:6
+ imagePullPolicy: IfNotPresent
+ volumeMounts:
+ - name: binance-config
+ mountPath: /app/conf/
+ volumes:
+ - name: binance-config
+ configMap:
+ name: binance-config-qy-xrp
+```
diff --git a/main.dev.py b/main.dev.py
new file mode 100644
index 0000000..fe6658f
--- /dev/null
+++ b/main.dev.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+import time
+import logging
+from binance.binance_trader import BinanceTrader
+from binance.utils import config
+
+format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+logging.basicConfig(level=logging.INFO, format=format, filename='grid_trader_log.txt')
+logger = logging.getLogger('binance')
+
+
+if __name__ == '__main__':
+
+
+ config.loads('./conf/config.json')
+ trader = BinanceTrader()
+
+
+
+ while True:
+ try:
+ print(trader.get_avg_price_5min())
+ print(trader.get_price_change_percent_24h())
+ #trader.grid_trader()
+ time.sleep(200)
+
+ except Exception as error:
+ print(f"BA catch error: {error}")
+ time.sleep(5)
+
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..ff2ddd2
--- /dev/null
+++ b/main.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+import time
+import logging
+from binance.binance_trader import BinanceTrader
+from binance.binance_future_trader import BinanceFutureTrader
+from utils import utility
+from utils import config
+format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+logging.basicConfig(level=logging.INFO, format=format, filename='grid_trader_log.txt')
+logger = logging.getLogger('binance')
+
+
+if __name__ == '__main__':
+
+
+ config.loads('./conf/config.json')
+ sleep_time = 5
+ if config.sleep_time:
+ sleep_time = config.sleep_time
+
+ if config.platform == 'binance_spot':
+ trader = BinanceTrader()
+ else:
+ trader = BinanceFutureTrader()
+
+ #启动新实例, 取消客户端创建的订单
+ print("启动新实例, 同步本客户端创建的订单:")
+ trader.sync_open_orders()
+
+ #启动新实例 发个通知
+ utility.alert(f"{utility.cn_time()} 交易对{config.symbol}启动")
+
+
+
+
+
+ while True:
+ try:
+ trader.grid_trader()
+ time.sleep(sleep_time)
+
+ except Exception as error:
+ utility.alert(f"交易{config.symbol}: {error}")
+ time.sleep(sleep_time)
+
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..48f6028
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+requests==2.22.0
+dingtalkchatbot
+pytz
+APScheduler==3.6.3
+aiohttp
+websocket_client==0.57.0
\ No newline at end of file
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..0bae355
--- /dev/null
+++ b/test.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+import time
+from binance.binance_trader import BinanceTrader
+from binance.binance_future_trader import BinanceFutureTrader
+from utils import config
+
+
+
+if __name__ == '__main__':
+
+
+ config.loads('./conf/config.json')
+
+ trader_spot = BinanceTrader()
+ trader_future = BinanceFutureTrader()
+
+
+
+
+ while True:
+ try:
+ price_spot = trader_spot.get_latest_price()
+ price_future = trader_future.get_latest_price()
+ print("diff:")
+ print(f"price_spot:{price_spot} price_future:{price_future}")
+ print(f"spot/future: {price_spot/price_future}")
+ time.sleep(5)
+
+ except Exception as error:
+ print(f"交易{config.symbol}: {error}")
+ time.sleep(5)
+
diff --git a/test1.py b/test1.py
new file mode 100644
index 0000000..d2eed2a
--- /dev/null
+++ b/test1.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+#coding: utf-8
+import smtplib
+from email.mime.text import MIMEText
+sender = '44444444444444444444g'
+receiver = '44444444444444444rg'
+subject = 'python email test'
+smtpserver = '4444444444444444444rg'
+username = '4444444444444444g'
+password = '44444444444444444444444'
+msg = MIMEText('ja2磁盘使用超过80%
','html','utf-8')
+msg['Subject'] = subject
+smtp = smtplib.SMTP()
+smtp.connect(smtpserver)
+smtp.login(username, password)
+smtp.sendmail(sender, receiver, msg.as_string())
+smtp.quit()
diff --git a/test2.py b/test2.py
new file mode 100644
index 0000000..8d16f6f
--- /dev/null
+++ b/test2.py
@@ -0,0 +1,7 @@
+def as_num(x):
+ y='{:.32f}'.format(x) # 5f表示保留5位小数点的float型
+ return(y)
+
+
+
+print(as_num(1.2e-4))
diff --git a/test_depth.py b/test_depth.py
new file mode 100644
index 0000000..294b8fc
--- /dev/null
+++ b/test_depth.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+import time
+import logging
+from binance.binance_trader import BinanceTrader
+from binance.binance_future_trader import BinanceFutureTrader
+from utils import utility
+from utils import config
+format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+logging.basicConfig(level=logging.INFO, format=format, filename='grid_trader_log.txt')
+logger = logging.getLogger('binance')
+
+
+if __name__ == '__main__':
+
+
+ config.loads('./conf/config.json')
+
+ if config.platform == 'binance_spot':
+ trader = BinanceTrader()
+ else:
+ trader = BinanceFutureTrader()
+
+
+
+ while True:
+ try:
+ trader.get_depth(5)
+ time.sleep(20)
+
+ except Exception as error:
+ utility.alert(f"交易{config.symbol}: {error}")
+ time.sleep(5)
+
diff --git a/utils/__init__.py b/utils/__init__.py
new file mode 100644
index 0000000..9430428
--- /dev/null
+++ b/utils/__init__.py
@@ -0,0 +1,4 @@
+from .config import config
+from .utility import *
+from .smtp import send_email
+from .dingding import *
\ No newline at end of file
diff --git a/utils/config.py b/utils/config.py
new file mode 100644
index 0000000..1c4f0e6
--- /dev/null
+++ b/utils/config.py
@@ -0,0 +1,54 @@
+# -*- coding:utf-8 -*-
+import json
+
+
+class Config:
+
+ def __init__(self):
+
+ self.platform: str = "binance_spot" # 交易的平台
+ self.symbol:str = "XRPUSDT" # 交易对.
+ self.gap_percent: float = 0.005 # 网格变化交易的单位.
+ self.api_key: str = None
+ self.api_secret: str = None
+ self.pass_phrase = None
+ self.quantity:float = 1
+ self.min_price = 0.0001
+ self.min_qty = 0.01
+ self.max_orders = 1
+ self.proxy_host = "" # proxy host
+ self.proxy_port = 0 # proxy port
+
+
+ def loads(self, config_file=None):
+ """ Load config file.
+
+ Args:
+ config_file: config json file.
+ """
+ configures = {}
+ if config_file:
+ try:
+ with open(config_file) as f:
+ data = f.read()
+ configures = json.loads(data)
+ except Exception as e:
+ print(e)
+ exit(0)
+ if not configures:
+ print("config json file error!")
+ exit(0)
+ self._update(configures)
+
+ def _update(self, update_fields):
+ """
+ 更新update fields.
+ :param update_fields:
+ :return: None
+
+ """
+
+ for k, v in update_fields.items():
+ setattr(self, k, v)
+
+config = Config()
\ No newline at end of file
diff --git a/utils/dingding.py b/utils/dingding.py
new file mode 100644
index 0000000..814fe52
--- /dev/null
+++ b/utils/dingding.py
@@ -0,0 +1,34 @@
+from dingtalkchatbot.chatbot import DingtalkChatbot
+from utils.config import config
+
+def dingding_at_me(msg):
+ # WebHook地址
+ webhook = config.dingding_robot_url
+ #dingding_user = config.dingding_user_phone
+ secret = 'SEC11b9...这里填写自己的加密设置密钥' # 可选:创建机器人勾选“加签”选项时使用
+ # 初始化机器人小丁
+ xiaoding = DingtalkChatbot(webhook) # 方式一:通常初始化方式
+ #xiaoding = DingtalkChatbot(webhook, secret=secret) # 方式二:勾选“加签”选项时使用(v1.5以上新功能)
+ #xiaoding = DingtalkChatbot(webhook, pc_slide=True) # 方式三:设置消息链接在PC端侧边栏打开(v1.5以上新功能)
+ # Text消息@所有人
+ #xiaoding.send_text(msg='我就是小丁,小丁就是我!', is_at_all=True)
+ dingding_user = [config.dingding_user_phone]
+ #xiaoding.send_text(msg=msg, at_mobiles=dingding_user)
+ xiaoding.send_text(msg=msg, at_mobiles=dingding_user)
+
+def dingding_at_all(msg):
+ webhook = config.dingding_robot_url
+ xiaoding = DingtalkChatbot(webhook) # 方式一:通常初始化方式
+ xiaoding.send_text(msg=msg, is_at_all=True)
+
+def dingding_say(msg):
+ webhook = config.dingding_robot_url
+ xiaoding = DingtalkChatbot(webhook) # 方式一:通常初始化方式
+ xiaoding.send_text(msg=msg)
+
+if __name__ == '__main__':
+ config.loads('../conf/config.json')
+ dingding_say("test")
+ print("dingding utils")
+
+
diff --git a/utils/queue.py b/utils/queue.py
new file mode 100644
index 0000000..b5686f3
--- /dev/null
+++ b/utils/queue.py
@@ -0,0 +1,52 @@
+# 先进先出
+class Queue():
+
+ def __init__(self,size):
+ self.size=size
+ self.front=-1 #队列前面的指针
+ self.rear=-1 #队列后面的指针
+ #类中设置两个属性分别为front和rear来模拟队列的头尾指针,通过它们值的关系可以判定队列是空还是满
+ self.queue=[]
+
+ ## 入队操作
+ def enqueue(self,ele):
+ if self.isfull():
+ print("queue is full")
+ else:
+ self.queue.append(ele)
+ self.rear=self.rear+1
+
+ ## 出队操作
+ def dequeue(self):
+ if self.isempty():
+ print("queue is empty")
+ else:
+ self.queue.pop(0)
+ self.front=self.front+1
+
+ def isfull(self):
+ if self.rear - self.front + 1 == self.size :
+ return True
+ else:
+ return False
+ def isempty(self):
+ if self.front == self.rear:
+ return True
+ else:
+ return False
+ def showQueue(self):
+ print(self.queue)
+
+if __name__ == '__main__':
+ q = Queue(10)
+ for i in range(6):
+ #print(i)
+ q.enqueue(i)
+ q.showQueue()
+ for i in range(3):
+ q.dequeue()
+ q.showQueue()
+ print(q.isempty())
+
+
+
diff --git a/utils/smtp.py b/utils/smtp.py
new file mode 100644
index 0000000..75f05ab
--- /dev/null
+++ b/utils/smtp.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+from utils.config import config
+import smtplib
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
+from email.header import Header
+
+#自适应邮件标题
+def get_subject(text):
+ default_subject='交易通知'
+ if "买单成交" in text:
+ return "买单成交"
+ elif "卖单成交" in text:
+ return "卖单成交"
+ elif "启动" in text:
+ return "机器人启动"
+ elif "配置不当" in text:
+ return "配置不当"
+ elif "单创建失败" in text:
+ return "订单创建失败"
+ elif "撤销失败" in text:
+ return "订单撤销失败"
+ else:
+ return default_subject
+
+
+#发出邮件
+def send_email(text):
+ #连接邮箱服务器
+ #con = smtplib.SMTP_SSL('mail.yangqiao.org', 465)
+ con = smtplib.SMTP(config.SMTP_HOST, config.SMTP_PORT)
+ #登录邮箱
+ con.login(config.SMTP_USER, config.SMTP_PASSWORD)
+
+ # 准备数据 创建邮件对象
+ msg = MIMEMultipart()
+
+ # 设置邮件主题 设置邮件发送者 设置邮件接受者
+ subject_and_sender=get_subject(text)
+ subject = Header(subject_and_sender, 'utf-8').encode()
+ msg['Subject'] = subject
+ msg['From'] = (f"{subject_and_sender} <{config.SMTP_USER}>")
+ msg['To'] = config.user_email
+
+ # 添加文字内容
+ text = MIMEText(text, 'plain', 'utf-8')
+ msg.attach(text)
+
+ # 发送邮件
+ con.sendmail(config.SMTP_USER,config.user_email, msg.as_string())
+ con.quit()
+
+
+
+
+
+if __name__ == '__main__':
+ config.loads('../conf/config.json')
+ send_email("卖单成交")
+
+
diff --git a/utils/utility.py b/utils/utility.py
new file mode 100644
index 0000000..9c5fbc7
--- /dev/null
+++ b/utils/utility.py
@@ -0,0 +1,110 @@
+import json
+from pathlib import Path
+from decimal import Decimal
+from utils import dingding, smtp
+import datetime
+import time
+import pytz
+
+def as_num(x):
+ # 32f表示保留32位小数点的float型
+ y = '{:.32f}'.format(x)
+ return(y)
+
+
+def get_current_timestamp():
+ """
+ 获取系统的时间.
+ :return:
+ """
+ return int(time.time() * 1000)
+
+def alert(msg):
+ print(msg)
+ dingding.dingding_at_me(msg)
+ smtp.send_email(msg)
+
+def cn_time():
+ """
+ chinese time
+ https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ """
+ return datetime.datetime.now(pytz.timezone('Asia/Shanghai'))
+
+
+def _get_trader_dir(temp_name: str):
+ """
+ Get path where trader is running in.
+ """
+ cwd = Path.cwd()
+ temp_path = cwd.joinpath(temp_name)
+
+ if temp_path.exists():
+ return cwd, temp_path
+
+ if not temp_path.exists():
+ temp_path.mkdir()
+
+ return cwd, temp_path
+
+
+TRADER_DIR, TEMP_DIR = _get_trader_dir("trader")
+
+
+def get_file_path(filename: str):
+ """
+ Get path for temp file with filename.
+ """
+ return TEMP_DIR.joinpath(filename)
+
+
+def get_folder_path(folder_name: str):
+ """
+ Get path for temp folder with folder name.
+ """
+ folder_path = TEMP_DIR.joinpath(folder_name)
+ if not folder_path.exists():
+ folder_path.mkdir()
+ return folder_path
+
+def load_json(filename: str):
+ """
+ Load data from json file in temp path.
+ """
+ filepath = get_file_path(filename)
+
+ if filepath.exists():
+ with open(filepath, mode="r", encoding="UTF-8") as f:
+ data = json.load(f)
+ return data
+ else:
+ save_json(filename, {})
+ return {}
+
+
+def save_json(filename: str, data: dict):
+ """
+ Save data into json file in temp path.
+ """
+ filepath = get_file_path(filename)
+ with open(filepath, mode="w+", encoding="UTF-8") as f:
+ json.dump(
+ data,
+ f,
+ indent=4,
+ ensure_ascii=False
+ )
+
+
+def round_to(value: float, target: float) -> float:
+ """
+ Round price to price tick value.
+ """
+ value = Decimal(str(value))
+ target = Decimal(str(target))
+ #round() 方法返回浮点数x的四舍五入值。
+ ##eg:
+ ## quantity = round_to(float(config.quantity), float(config.min_qty))
+ rounded = float(int(round(value / target)) * target)
+ return rounded
+