stable/freqtrade/tests/exchange/test_exchange_binance.py
2018-02-02 00:33:10 +01:00

350 lines
11 KiB
Python

# pragma pylint: disable=missing-docstring, C0103, protected-access, unused-argument
from unittest.mock import MagicMock
import pytest
from freqtrade.exchange.binance import Binance
import freqtrade.exchange.binance as bin
# Eat this flake8
# +------------------+
# | binance.Binance |
# +------------------+
# |
# (mock Fake_binance)
# |
# +-----------------------------+
# | freqtrade.exchange.Binance |
# +-----------------------------+
# Call into Binance will flow up to the
# external package binance.Binance.
# By inserting a mock, we redirect those
# calls.
# The faked binance API is called just 'fb'
# The freqtrade.exchange.Binance is a
# wrapper, and is called 'wb'
def _stub_config():
return {'key': '',
'secret': ''}
class FakeBinance():
def __init__(self, success=True):
self.success = True # Believe in yourself
self.result = None
self.get_ticker_call_count = 0
# This is really ugly, doing side-effect during instance creation
# But we're allowed to in testing-code
bin._API = MagicMock()
bin._API.order_limit_buy = self.fake_order_limit_buy
bin._API.order_limit_sell = self.fake_order_limit_sell
bin._API.get_asset_balance = self.fake_get_asset_balance
bin._API.get_account = self.fake_get_account
bin._API.get_ticker = self.fake_get_ticker
bin._API.get_klines = self.fake_get_klines
bin._API.get_all_orders = self.fake_get_all_orders
bin._API.cancel_order = self.fake_cancel_order
bin._API.get_all_tickers = self.fake_get_all_tickers
bin._API.get_exchange_info = self.fake_get_exchange_info
bin._EXCHANGE_CONF = {'stake_currency': 'BTC'}
def fake_order_limit_buy(self, symbol, quantity, price):
return {"symbol": "BTCETH",
"orderId": 42,
"clientOrderId": "6gCrw2kRUAF9CvJDGP16IP",
"transactTime": 1507725176595,
"price": "0.00000000",
"origQty": "10.00000000",
"executedQty": "10.00000000",
"status": "FILLED",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "BUY"}
def fake_order_limit_sell(self, symbol, quantity, price):
return {"symbol": "BTCETH",
"orderId": 42,
"clientOrderId": "6gCrw2kRUAF9CvJDGP16IP",
"transactTime": 1507725176595,
"price": "0.00000000",
"origQty": "10.00000000",
"executedQty": "10.00000000",
"status": "FILLED",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "SELL"}
def fake_get_asset_balance(self, asset):
return {
"asset": "BTC",
"free": "4723846.89208129",
"locked": "0.00000000"
}
def fake_get_account(self):
return {
"makerCommission": 15,
"takerCommission": 15,
"buyerCommission": 0,
"sellerCommission": 0,
"canTrade": True,
"canWithdraw": True,
"canDeposit": True,
"balances": [
{
"asset": "BTC",
"free": "4723846.89208129",
"locked": "0.00000000"
},
{
"asset": "LTC",
"free": "4763368.68006011",
"locked": "0.00000000"
}
]
}
def fake_get_ticker(self, symbol=None):
self.get_ticker_call_count += 1
t = {"symbol": "ETHBTC",
"priceChange": "-94.99999800",
"priceChangePercent": "-95.960",
"weightedAvgPrice": "0.29628482",
"prevClosePrice": "0.10002000",
"lastPrice": "4.00000200",
"bidPrice": "4.00000000",
"askPrice": "4.00000200",
"openPrice": "99.00000000",
"highPrice": "100.00000000",
"lowPrice": "0.10000000",
"volume": "8913.30000000",
"openTime": 1499783499040,
"closeTime": 1499869899040,
"fristId": 28385,
"lastId": 28460,
"count": 76}
return t if symbol else [t]
def fake_get_klines(self, symbol, interval):
return [[0,
"0",
"0",
"0",
"0",
"0",
0,
"0",
0,
"0",
"0",
"0"]]
def fake_get_all_orders(self, symbol, orderId):
return [{"symbol": "LTCBTC",
"orderId": 42,
"clientOrderId": "myOrder1",
"price": "0.1",
"origQty": "1.0",
"executedQty": "0.0",
"status": "NEW",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "BUY",
"stopPrice": "0.0",
"icebergQty": "0.0",
"status_code": "200",
"time": 1499827319559}]
def fake_cancel_order(self, symbol, orderId):
return {"symbol": "LTCBTC",
"origClientOrderId": "myOrder1",
"orderId": 42,
"clientOrderId": "cancelMyOrder1"}
def fake_get_all_tickers(self):
return [{"symbol": "LTCBTC",
"price": "4.00000200"},
{"symbol": "ETHBTC",
"price": "0.07946600"}]
def fake_get_exchange_info(self):
return {
"timezone": "UTC",
"serverTime": 1508631584636,
"rateLimits": [
{
"rateLimitType": "REQUESTS",
"interval": "MINUTE",
"limit": 1200
},
{
"rateLimitType": "ORDERS",
"interval": "SECOND",
"limit": 10
},
{
"rateLimitType": "ORDERS",
"interval": "DAY",
"limit": 100000
}
],
"exchangeFilters": [],
"symbols": [
{
"symbol": "ETHBTC",
"status": "TRADING",
"baseAsset": "ETH",
"baseAssetPrecision": 8,
"quoteAsset": "BTC",
"quotePrecision": 8,
"orderTypes": ["LIMIT", "MARKET"],
"icebergAllowed": False,
"filters": [
{
"filterType": "PRICE_FILTER",
"minPrice": "0.00000100",
"maxPrice": "100000.00000000",
"tickSize": "0.00000100"
}, {
"filterType": "LOT_SIZE",
"minQty": "0.00100000",
"maxQty": "100000.00000000",
"stepSize": "0.00100000"
}, {
"filterType": "MIN_NOTIONAL",
"minNotional": "0.00100000"
}
]
}
]
}
# The freqtrade.exchange.binance is called wrap_binance
# to not confuse naming with binance.binance
def make_wrap_binance():
conf = _stub_config()
wb = bin.Binance(conf)
return wb
def test_exchange_binance_class():
conf = _stub_config()
b = Binance(conf)
assert isinstance(b, Binance)
slots = dir(b)
for name in ['fee', 'buy', 'sell', 'get_balance', 'get_balances',
'get_ticker', 'get_ticker_history', 'get_order',
'cancel_order', 'get_pair_detail_url', 'get_markets',
'get_market_summaries', 'get_wallet_health']:
assert name in slots
# FIX: ensure that the slot is also a method in the class
# getattr(b, name) => bound method Binance.buy
# type(getattr(b, name)) => class 'method'
def test_exchange_binance_fee():
fee = Binance.fee.__get__(Binance)
assert fee >= 0 and fee < 0.1 # Fee is 0-10 %
def test_exchange_binance_buy_good():
wb = make_wrap_binance()
fb = FakeBinance()
uuid = wb.buy('BTC_ETH', 1, 1)
assert uuid == fb.fake_order_limit_buy(1, 2, 3)['orderId']
with pytest.raises(IndexError, match=r'.*'):
wb.buy('BAD', 1, 1)
def test_exchange_binance_sell_good():
wb = make_wrap_binance()
fb = FakeBinance()
uuid = wb.sell('BTC_ETH', 1, 1)
assert uuid == fb.fake_order_limit_sell(1, 2, 3)['orderId']
with pytest.raises(IndexError, match=r'.*'):
uuid = wb.sell('BAD', 1, 1)
def test_exchange_binance_get_balance():
wb = make_wrap_binance()
fb = FakeBinance()
bal = wb.get_balance('BTC')
assert str(bal) == fb.fake_get_asset_balance(1)['free']
def test_exchange_binance_get_balances():
wb = make_wrap_binance()
fb = FakeBinance()
bals = wb.get_balances()
assert len(bals) <= len(fb.fake_get_account()['balances'])
def test_exchange_binance_get_ticker():
wb = make_wrap_binance()
FakeBinance()
# Poll ticker, which updates the cache
tick = wb.get_ticker('BTC_ETH')
for x in ['bid', 'ask', 'last']:
assert x in tick
def test_exchange_binance_get_ticker_history_intervals():
wb = make_wrap_binance()
FakeBinance()
for tick_interval in [1, 5]:
assert ([{'O': 0.0, 'H': 0.0,
'L': 0.0, 'C': 0.0,
'V': 0.0, 'T': '1970-01-01T01:00:00', 'BV': 0.0}] ==
wb.get_ticker_history('BTC_ETH', tick_interval))
def test_exchange_binance_get_ticker_history():
wb = make_wrap_binance()
FakeBinance()
assert wb.get_ticker_history('BTC_ETH', 5)
def test_exchange_binance_get_order():
wb = make_wrap_binance()
FakeBinance()
order = wb.get_order('42', 'BTC_LTC')
assert order['id'] == 42
def test_exchange_binance_cancel_order():
wb = make_wrap_binance()
FakeBinance()
assert wb.cancel_order('42', 'BTC_LTC')['orderId'] == 42
def test_exchange_get_pair_detail_url():
wb = make_wrap_binance()
FakeBinance()
assert wb.get_pair_detail_url('BTC_ETH')
def test_exchange_get_markets():
wb = make_wrap_binance()
FakeBinance()
x = wb.get_markets()
assert len(x) > 0
def test_exchange_get_market_summaries():
wb = make_wrap_binance()
FakeBinance()
assert wb.get_market_summaries()
def test_exchange_get_wallet_health():
wb = make_wrap_binance()
FakeBinance()
x = wb.get_wallet_health()
assert x[0]['Currency'] == 'ETH'