add
This commit is contained in:
70
ccxt/pro/test/Exchange/test_un_watch_positions.py
Normal file
70
ccxt/pro/test/Exchange/test_un_watch_positions.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def create_order_after_delay(exchange):
|
||||
await exchange.sleep(3000)
|
||||
await exchange.create_order('BTC/USDT:USDT', 'market', 'buy', 0.001)
|
||||
|
||||
|
||||
async def test_un_watch_positions(exchange, skipped_properties, symbol):
|
||||
method = 'unWatchPositions'
|
||||
exchange.set_sandbox_mode(True)
|
||||
# First, we need to subscribe to positions to test the unsubscribe functionality
|
||||
positions_subscription = None
|
||||
try:
|
||||
# First call uses snapshot
|
||||
positions_subscription = await exchange.watch_positions()
|
||||
# trigger a position update
|
||||
exchange.spawn(create_order_after_delay, exchange)
|
||||
# Second call uses subscription
|
||||
positions_subscription = await exchange.watch_positions()
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
# If we can't subscribe, we can't test unsubscribe, so skip this test
|
||||
return False
|
||||
# Verify that we have a subscription
|
||||
assert isinstance(positions_subscription, list), exchange.id + ' ' + method + ' requires a valid positions subscription to test unsubscribe'
|
||||
# Assert unWatchPositions for one symbol is not supported
|
||||
error_response = None
|
||||
try:
|
||||
error_response = await exchange.un_watch_positions([symbol])
|
||||
except Exception as e:
|
||||
error_response = e
|
||||
assert error_response is not None, exchange.id + ' ' + method + ' must throw an error when unwatching a specific symbol, returned ' + exchange.json(error_response)
|
||||
# Test unwatching all positions (without specific symbols)
|
||||
response_all = None
|
||||
try:
|
||||
response_all = await exchange.un_watch_positions()
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
raise e
|
||||
# Verify the response for unwatching all positions
|
||||
assert response_all is not None, exchange.id + ' ' + method + ' must return a response when unwatching all positions, returned ' + exchange.json(response_all)
|
||||
# Test that we can resubscribe after unwatching (to ensure cleanup was proper)
|
||||
resubscribe_response = None
|
||||
try:
|
||||
resubscribe_response = await exchange.watch_positions()
|
||||
exchange.spawn(create_order_after_delay, exchange)
|
||||
resubscribe_response = await exchange.watch_positions()
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
raise Error(exchange.id + ' ' + method + ' failed to resubscribe after unwatch, indicating potential cleanup issues')
|
||||
# Verify resubscription works
|
||||
assert isinstance(resubscribe_response, list), exchange.id + ' ' + method + ' must allow resubscription after unwatch, returned ' + exchange.json(resubscribe_response)
|
||||
return True
|
||||
36
ccxt/pro/test/Exchange/test_watch_balance.py
Normal file
36
ccxt/pro/test/Exchange/test_watch_balance.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_balance # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_balance(exchange, skipped_properties, code):
|
||||
method = 'watchBalance'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_balance()
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success is False:
|
||||
continue
|
||||
test_balance(exchange, skipped_properties, method, response)
|
||||
now = exchange.milliseconds()
|
||||
62
ccxt/pro/test/Exchange/test_watch_bids_asks.py
Normal file
62
ccxt/pro/test/Exchange/test_watch_bids_asks.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
from ccxt.base.errors import ArgumentsRequired # noqa E402
|
||||
from ccxt.test.exchange.base import test_ticker # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_bids_asks(exchange, skipped_properties, symbol):
|
||||
without_symbol = test_watch_bids_asks_helper(exchange, skipped_properties, None)
|
||||
with_symbol = test_watch_bids_asks_helper(exchange, skipped_properties, [symbol])
|
||||
await asyncio.gather(*[with_symbol, without_symbol])
|
||||
|
||||
|
||||
async def test_watch_bids_asks_helper(exchange, skipped_properties, arg_symbols, arg_params={}):
|
||||
method = 'watchBidsAsks'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
success = True
|
||||
should_return = False
|
||||
response = None
|
||||
try:
|
||||
response = await exchange.watch_bids_asks(arg_symbols, arg_params)
|
||||
except Exception as e:
|
||||
# for some exchanges, multi symbol methods might require symbols array to be present, so
|
||||
# so, if method throws "arguments-required" exception, we don't fail test, but just skip silently,
|
||||
# because tests will make a second call of this method with symbols array
|
||||
if (isinstance(e, ArgumentsRequired)) and (arg_symbols is None or len(arg_symbols) == 0):
|
||||
# todo: provide random symbols to try
|
||||
# return false;
|
||||
should_return = True
|
||||
elif not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if should_return:
|
||||
return False
|
||||
if success:
|
||||
assert isinstance(response, dict), exchange.id + ' ' + method + ' ' + exchange.json(arg_symbols) + ' must return an object. ' + exchange.json(response)
|
||||
values = list(response.values())
|
||||
checked_symbol = None
|
||||
if arg_symbols is not None and len(arg_symbols) == 1:
|
||||
checked_symbol = arg_symbols[0]
|
||||
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, values, checked_symbol)
|
||||
for i in range(0, len(values)):
|
||||
ticker = values[i]
|
||||
test_ticker(exchange, skipped_properties, method, ticker, checked_symbol)
|
||||
now = exchange.milliseconds()
|
||||
return True
|
||||
47
ccxt/pro/test/Exchange/test_watch_liquidations.py
Normal file
47
ccxt/pro/test/Exchange/test_watch_liquidations.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.base.errors import NetworkError # noqa E402
|
||||
from ccxt.test.exchange.base import test_liquidation # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_liquidations(exchange, skipped_properties, symbol):
|
||||
# log (symbol.green, 'watching trades...')
|
||||
method = 'watchLiquidations'
|
||||
# we have to skip some exchanges here due to the frequency of trading
|
||||
skipped_exchanges = []
|
||||
if exchange.in_array(exchange.id, skipped_exchanges):
|
||||
print(exchange.id, method + '() test skipped')
|
||||
return False
|
||||
if not exchange.has[method]:
|
||||
print(exchange.id, 'does not support', method + '() method')
|
||||
return False
|
||||
response = None
|
||||
now = int(time.time() * 1000)
|
||||
ends = now + 10000
|
||||
while now < ends:
|
||||
try:
|
||||
response = await exchange[method](symbol)
|
||||
now = int(time.time() * 1000)
|
||||
is_array = isinstance(response, list)
|
||||
assert is_array, 'response must be an array'
|
||||
print(exchange.iso8601(now), exchange.id, symbol, method, len(list(response.values())), 'liquidations')
|
||||
# log.noLocate (asTable (response))
|
||||
for i in range(0, len(response)):
|
||||
test_liquidation(exchange, skipped_properties, method, response[i], symbol)
|
||||
except Exception as e:
|
||||
if not (isinstance(e, NetworkError)):
|
||||
raise e
|
||||
now = int(time.time() * 1000)
|
||||
return response
|
||||
@@ -0,0 +1,45 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.base.errors import NetworkError # noqa E402
|
||||
from ccxt.test.exchange.base import test_liquidation # noqa E402
|
||||
|
||||
async def test_watch_liquidations_for_symbols(exchange, skipped_properties, symbol):
|
||||
method = 'watchLiquidationsForSymbols'
|
||||
# we have to skip some exchanges here due to the frequency of trading
|
||||
skipped_exchanges = []
|
||||
if exchange.in_array(exchange.id, skipped_exchanges):
|
||||
print(exchange.id, method + '() test skipped')
|
||||
return False
|
||||
if not exchange.has[method]:
|
||||
print(exchange.id, method + '() is not supported')
|
||||
return False
|
||||
response = None
|
||||
now = int(time.time() * 1000)
|
||||
ends = now + 10000
|
||||
while now < ends:
|
||||
try:
|
||||
response = await exchange[method]([symbol])
|
||||
now = int(time.time() * 1000)
|
||||
is_array = isinstance(response, list)
|
||||
assert is_array, 'response must be an array'
|
||||
print(exchange.iso8601(now), exchange.id, symbol, method, len(list(response.values())), 'liquidations')
|
||||
# log.noLocate (asTable (response))
|
||||
for i in range(0, len(response)):
|
||||
test_liquidation(exchange, skipped_properties, method, response[i], symbol)
|
||||
except Exception as e:
|
||||
if not (isinstance(e, NetworkError)):
|
||||
raise e
|
||||
now = int(time.time() * 1000)
|
||||
return response
|
||||
39
ccxt/pro/test/Exchange/test_watch_my_trades.py
Normal file
39
ccxt/pro/test/Exchange/test_watch_my_trades.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_trade # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_my_trades(exchange, skipped_properties, symbol):
|
||||
method = 'watchMyTrades'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
success = True
|
||||
response = None
|
||||
try:
|
||||
response = await exchange.watch_my_trades(symbol)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, response, symbol)
|
||||
now = exchange.milliseconds()
|
||||
for i in range(0, len(response)):
|
||||
test_trade(exchange, skipped_properties, method, response[i], symbol, now)
|
||||
test_shared_methods.assert_timestamp_order(exchange, method, symbol, response)
|
||||
return True
|
||||
47
ccxt/pro/test/Exchange/test_watch_ohlcv.py
Normal file
47
ccxt/pro/test/Exchange/test_watch_ohlcv.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_ohlcv # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_ohlcv(exchange, skipped_properties, symbol):
|
||||
method = 'watchOHLCV'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
timeframe_keys = list(exchange.timeframes.keys())
|
||||
assert len(timeframe_keys), exchange.id + ' ' + method + ' - no timeframes found'
|
||||
# prefer 1m timeframe if available, otherwise return the first one
|
||||
chosen_timeframe_key = '1m'
|
||||
if not exchange.in_array(chosen_timeframe_key, timeframe_keys):
|
||||
chosen_timeframe_key = timeframe_keys[0]
|
||||
limit = 10
|
||||
duration = exchange.parse_timeframe(chosen_timeframe_key)
|
||||
since = exchange.milliseconds() - duration * limit * 1000 - 1000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_ohlcv(symbol, chosen_timeframe_key, since, limit)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, response, symbol)
|
||||
now = exchange.milliseconds()
|
||||
for i in range(0, len(response)):
|
||||
test_ohlcv(exchange, skipped_properties, method, response[i], symbol, now)
|
||||
return True
|
||||
54
ccxt/pro/test/Exchange/test_watch_ohlcv_for_symbols.py
Normal file
54
ccxt/pro/test/Exchange/test_watch_ohlcv_for_symbols.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_ohlcv # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_ohlcv_for_symbols(exchange, skipped_properties, symbol):
|
||||
method = 'watchOHLCVForSymbols'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
timeframe_keys = list(exchange.timeframes.keys())
|
||||
assert len(timeframe_keys), exchange.id + ' ' + method + ' - no timeframes found'
|
||||
# prefer 1m timeframe if available, otherwise return the first one
|
||||
chosen_timeframe_key = '1m'
|
||||
if not exchange.in_array(chosen_timeframe_key, timeframe_keys):
|
||||
chosen_timeframe_key = timeframe_keys[0]
|
||||
limit = 10
|
||||
duration = exchange.parse_timeframe(chosen_timeframe_key)
|
||||
since = exchange.milliseconds() - duration * limit * 1000 - 1000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_ohlcv_for_symbols([[symbol, chosen_timeframe_key]], since, limit)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
assertion_message = exchange.id + ' ' + method + ' ' + symbol + ' ' + chosen_timeframe_key + ' | ' + exchange.json(response)
|
||||
assert isinstance(response, dict), 'Response must be a dictionary. ' + assertion_message
|
||||
assert symbol in response, 'Response should contain the symbol as key. ' + assertion_message
|
||||
symbol_obj = response[symbol]
|
||||
assert isinstance(symbol_obj, dict), 'Response.Symbol should be a dictionary. ' + assertion_message
|
||||
assert chosen_timeframe_key in symbol_obj, 'Response.symbol should contain the timeframe key. ' + assertion_message
|
||||
ohlcvs = symbol_obj[chosen_timeframe_key]
|
||||
assert isinstance(ohlcvs, list), 'Response.symbol.timeframe should be an array. ' + assertion_message
|
||||
now = exchange.milliseconds()
|
||||
for i in range(0, len(ohlcvs)):
|
||||
test_ohlcv(exchange, skipped_properties, method, ohlcvs[i], symbol, now)
|
||||
return True
|
||||
38
ccxt/pro/test/Exchange/test_watch_order_book.py
Normal file
38
ccxt/pro/test/Exchange/test_watch_order_book.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_order_book # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_order_book(exchange, skipped_properties, symbol):
|
||||
method = 'watchOrderBook'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_order_book(symbol)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
# [ response, skippedProperties ] = fixPhpObjectArray (exchange, response, skippedProperties);
|
||||
assert isinstance(response, dict), exchange.id + ' ' + method + ' ' + symbol + ' must return an object. ' + exchange.json(response)
|
||||
now = exchange.milliseconds()
|
||||
test_order_book(exchange, skipped_properties, method, response, symbol)
|
||||
return True
|
||||
41
ccxt/pro/test/Exchange/test_watch_order_book_for_symbols.py
Normal file
41
ccxt/pro/test/Exchange/test_watch_order_book_for_symbols.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.base.errors import InvalidNonce # noqa E402
|
||||
from ccxt.test.exchange.base import test_order_book # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_order_book_for_symbols(exchange, skipped_properties, symbols):
|
||||
method = 'watchOrderBookForSymbols'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_order_book_for_symbols(symbols)
|
||||
except Exception as e:
|
||||
# temporary fix for InvalidNonce for c#
|
||||
if not test_shared_methods.is_temporary_failure(e) and not (isinstance(e, InvalidNonce)):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
# [ response, skippedProperties ] = fixPhpObjectArray (exchange, response, skippedProperties);
|
||||
assert isinstance(response, dict), exchange.id + ' ' + method + ' ' + exchange.json(symbols) + ' must return an object. ' + exchange.json(response)
|
||||
now = exchange.milliseconds()
|
||||
test_shared_methods.assert_in_array(exchange, skipped_properties, method, response, 'symbol', symbols)
|
||||
test_order_book(exchange, skipped_properties, method, response, None)
|
||||
return True
|
||||
39
ccxt/pro/test/Exchange/test_watch_orders.py
Normal file
39
ccxt/pro/test/Exchange/test_watch_orders.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_order # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_orders(exchange, skipped_properties, symbol):
|
||||
method = 'watchOrders'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_orders(symbol)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, response, symbol)
|
||||
now = exchange.milliseconds()
|
||||
for i in range(0, len(response)):
|
||||
test_order(exchange, skipped_properties, method, response[i], symbol, now)
|
||||
test_shared_methods.assert_timestamp_order(exchange, method, symbol, response)
|
||||
return True
|
||||
37
ccxt/pro/test/Exchange/test_watch_position.py
Normal file
37
ccxt/pro/test/Exchange/test_watch_position.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_position # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_position(exchange, skipped_properties, symbol):
|
||||
method = 'watchPosition'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_position(symbol)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
assert isinstance(response, dict), exchange.id + ' ' + method + ' ' + symbol + ' must return an object. ' + exchange.json(response)
|
||||
now = exchange.milliseconds()
|
||||
test_position(exchange, skipped_properties, method, response, None, now)
|
||||
return True
|
||||
60
ccxt/pro/test/Exchange/test_watch_positions.py
Normal file
60
ccxt/pro/test/Exchange/test_watch_positions.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_position # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_positions(exchange, skipped_properties, symbol):
|
||||
method = 'watchPositions'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_positions([symbol])
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, response, symbol)
|
||||
now = exchange.milliseconds()
|
||||
for i in range(0, len(response)):
|
||||
test_position(exchange, skipped_properties, method, response[i], None, now)
|
||||
test_shared_methods.assert_timestamp_order(exchange, method, symbol, response)
|
||||
#
|
||||
# Test with specific symbol
|
||||
#
|
||||
positions_for_symbols = None
|
||||
success2 = True
|
||||
try:
|
||||
positions_for_symbols = await exchange.watch_positions([symbol])
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success2 = False
|
||||
if success2:
|
||||
assert isinstance(positions_for_symbols, list), exchange.id + ' ' + method + ' must return an array, returned ' + exchange.json(positions_for_symbols)
|
||||
# max theoretical 4 positions: two for one-way-mode and two for two-way mode
|
||||
assert len(positions_for_symbols) <= 4, exchange.id + ' ' + method + ' positions length for particular symbol should be less than 4, returned ' + exchange.json(positions_for_symbols)
|
||||
now = exchange.milliseconds()
|
||||
for i in range(0, len(positions_for_symbols)):
|
||||
test_position(exchange, skipped_properties, method, positions_for_symbols[i], symbol, now)
|
||||
test_shared_methods.assert_timestamp_order(exchange, method, symbol, positions_for_symbols)
|
||||
return True
|
||||
37
ccxt/pro/test/Exchange/test_watch_ticker.py
Normal file
37
ccxt/pro/test/Exchange/test_watch_ticker.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_ticker # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_ticker(exchange, skipped_properties, symbol):
|
||||
method = 'watchTicker'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_ticker(symbol)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
assert isinstance(response, dict), exchange.id + ' ' + method + ' ' + symbol + ' must return an object. ' + exchange.json(response)
|
||||
now = exchange.milliseconds()
|
||||
test_ticker(exchange, skipped_properties, method, response, symbol)
|
||||
return True
|
||||
64
ccxt/pro/test/Exchange/test_watch_tickers.py
Normal file
64
ccxt/pro/test/Exchange/test_watch_tickers.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
from ccxt.base.errors import ArgumentsRequired # noqa E402
|
||||
from ccxt.test.exchange.base import test_ticker # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_tickers(exchange, skipped_properties, symbol):
|
||||
without_symbol = test_watch_tickers_helper(exchange, skipped_properties, None)
|
||||
with_symbol = test_watch_tickers_helper(exchange, skipped_properties, [symbol])
|
||||
await asyncio.gather(*[with_symbol, without_symbol])
|
||||
|
||||
|
||||
async def test_watch_tickers_helper(exchange, skipped_properties, arg_symbols, arg_params={}):
|
||||
method = 'watchTickers'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
should_return = False
|
||||
try:
|
||||
response = await exchange.watch_tickers(arg_symbols, arg_params)
|
||||
except Exception as e:
|
||||
# for some exchanges, specifically watchTickers method not subscribe
|
||||
# to "all tickers" itself, and it requires symbols to be set
|
||||
# so, in such case, if it's arguments-required exception, we don't
|
||||
# mark tests as failed, but just skip them
|
||||
if (isinstance(e, ArgumentsRequired)) and (arg_symbols is None or len(arg_symbols) == 0):
|
||||
# todo: provide random symbols to try
|
||||
# return;
|
||||
# return false;
|
||||
should_return = True
|
||||
elif not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if should_return:
|
||||
return False
|
||||
if success:
|
||||
assert isinstance(response, dict), exchange.id + ' ' + method + ' ' + exchange.json(arg_symbols) + ' must return an object. ' + exchange.json(response)
|
||||
values = list(response.values())
|
||||
checked_symbol = None
|
||||
if arg_symbols is not None and len(arg_symbols) == 1:
|
||||
checked_symbol = arg_symbols[0]
|
||||
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, values, checked_symbol)
|
||||
for i in range(0, len(values)):
|
||||
ticker = values[i]
|
||||
test_ticker(exchange, skipped_properties, method, ticker, checked_symbol)
|
||||
now = exchange.milliseconds()
|
||||
return True
|
||||
39
ccxt/pro/test/Exchange/test_watch_trades.py
Normal file
39
ccxt/pro/test/Exchange/test_watch_trades.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_trade # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_trades(exchange, skipped_properties, symbol):
|
||||
method = 'watchTrades'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_trades(symbol)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
# continue;
|
||||
success = False
|
||||
if success:
|
||||
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, response)
|
||||
now = exchange.milliseconds()
|
||||
for i in range(0, len(response)):
|
||||
test_trade(exchange, skipped_properties, method, response[i], symbol, now)
|
||||
if not ('timestampSort' in skipped_properties):
|
||||
test_shared_methods.assert_timestamp_order(exchange, method, symbol, response)
|
||||
42
ccxt/pro/test/Exchange/test_watch_trades_for_symbols.py
Normal file
42
ccxt/pro/test/Exchange/test_watch_trades_for_symbols.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.test.exchange.base import test_trade # noqa E402
|
||||
from ccxt.test.exchange.base import test_shared_methods # noqa E402
|
||||
|
||||
async def test_watch_trades_for_symbols(exchange, skipped_properties, symbols):
|
||||
method = 'watchTradesForSymbols'
|
||||
now = exchange.milliseconds()
|
||||
ends = now + 15000
|
||||
while now < ends:
|
||||
response = None
|
||||
success = True
|
||||
try:
|
||||
response = await exchange.watch_trades_for_symbols(symbols)
|
||||
except Exception as e:
|
||||
if not test_shared_methods.is_temporary_failure(e):
|
||||
raise e
|
||||
now = exchange.milliseconds()
|
||||
if success:
|
||||
assert isinstance(response, list), exchange.id + ' ' + method + ' ' + exchange.json(symbols) + ' must return an array. ' + exchange.json(response)
|
||||
now = exchange.milliseconds()
|
||||
symbol = None
|
||||
for i in range(0, len(response)):
|
||||
trade = response[i]
|
||||
symbol = trade['symbol']
|
||||
test_trade(exchange, skipped_properties, method, trade, symbol, now)
|
||||
test_shared_methods.assert_in_array(exchange, skipped_properties, method, trade, 'symbol', symbols)
|
||||
if not ('timestamp' in skipped_properties):
|
||||
test_shared_methods.assert_timestamp_order(exchange, method, symbol, response)
|
||||
return True
|
||||
0
ccxt/pro/test/base/.gitkeep
Normal file
0
ccxt/pro/test/base/.gitkeep
Normal file
72
ccxt/pro/test/base/test_abnormal_close.py
Normal file
72
ccxt/pro/test/base/test_abnormal_close.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
import ccxt.pro as ccxt
|
||||
import logging
|
||||
import psutil
|
||||
import socket
|
||||
|
||||
|
||||
KILL_AFTER = 20
|
||||
|
||||
|
||||
async def tcp_kill_after(seconds):
|
||||
await asyncio.sleep(seconds)
|
||||
logging.debug("tcp_kill")
|
||||
current_process = psutil.Process(os.getpid())
|
||||
connections = current_process.net_connections(kind='tcp')
|
||||
for conn in connections:
|
||||
if conn.status == psutil.CONN_ESTABLISHED:
|
||||
try:
|
||||
sock = socket.fromfd(conn.fd, socket.AF_INET, socket.SOCK_STREAM)
|
||||
local_address = conn.laddr.ip
|
||||
local_port = conn.laddr.port
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
logging.info(f"Connection closed: {local_address}:{local_port} -> {conn.raddr.ip}:{conn.raddr.port}")
|
||||
except Exception as e:
|
||||
logging.error(f"Error closing connection: {e}")
|
||||
|
||||
|
||||
async def test_abnormal_close():
|
||||
print('test_abnormal_close')
|
||||
received_network_error = False
|
||||
ex = ccxt.binance({
|
||||
# 'verbose': True
|
||||
})
|
||||
ex.set_sandbox_mode(True)
|
||||
asyncio.create_task(tcp_kill_after(15))
|
||||
start_time = ex.seconds()
|
||||
while True:
|
||||
if ex.seconds() - start_time > KILL_AFTER:
|
||||
break
|
||||
try:
|
||||
logging.info("calling watch_trades")
|
||||
await ex.watch_trades('BTC/USDT:USDT')
|
||||
logging.info("watch_trades returned")
|
||||
except ccxt.NetworkError as e:
|
||||
logging.info(f"received network error: {e}")
|
||||
if not received_network_error:
|
||||
received_network_error = True
|
||||
else:
|
||||
break
|
||||
except Exception as e:
|
||||
logging.info(f"received unexpected exception: {e}")
|
||||
assert received_network_error, "Failed to receive network error"
|
||||
print("test_abnormal_close between watch calls")
|
||||
await ex.watch_trades('BTC/USDT:USDT')
|
||||
await tcp_kill_after(1)
|
||||
await asyncio.sleep(2)
|
||||
await ex.watch_trades('BTC/USDT:USDT')
|
||||
await ex.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
asyncio.run(test_abnormal_close())
|
||||
616
ccxt/pro/test/base/test_cache.py
Normal file
616
ccxt/pro/test/base/test_cache.py
Normal file
@@ -0,0 +1,616 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide # noqa: F402
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def equals(a, b):
|
||||
return a == b
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
def test_ws_cache():
|
||||
array_cache = ArrayCache(3)
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 1,
|
||||
})
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 2,
|
||||
})
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 3,
|
||||
})
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 4,
|
||||
})
|
||||
assert(equals(array_cache, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 2,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 3,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 4,
|
||||
}]))
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 5,
|
||||
})
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 6,
|
||||
})
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 7,
|
||||
})
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 8,
|
||||
})
|
||||
assert(equals(array_cache, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 6,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 7,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 8,
|
||||
}]))
|
||||
array_cache.clear()
|
||||
array_cache.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 1,
|
||||
})
|
||||
assert(equals(array_cache, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 1,
|
||||
}]))
|
||||
# ----------------------------------------------------------------------------
|
||||
arraycache2 = ArrayCache(1)
|
||||
arraycache2.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 1,
|
||||
})
|
||||
arraycache2.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 2,
|
||||
})
|
||||
assert(equals(arraycache2, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'data': 2,
|
||||
}]))
|
||||
# ----------------------------------------------------------------------------
|
||||
timestamp_cache = ArrayCacheByTimestamp()
|
||||
ohlcv1 = [100, 1, 2, 3]
|
||||
ohlcv2 = [200, 5, 6, 7]
|
||||
timestamp_cache.append(ohlcv1)
|
||||
timestamp_cache.append(ohlcv2)
|
||||
assert equals(timestamp_cache, [ohlcv1, ohlcv2])
|
||||
modify2 = [200, 10, 11, 12]
|
||||
timestamp_cache.append(modify2)
|
||||
assert equals(timestamp_cache, [ohlcv1, modify2])
|
||||
# ----------------------------------------------------------------------------
|
||||
cache_symbol_id = ArrayCacheBySymbolById()
|
||||
object1 = {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': 'abcdef',
|
||||
'i': 1,
|
||||
}
|
||||
object2 = {
|
||||
'symbol': 'ETH/USDT',
|
||||
'id': 'qwerty',
|
||||
'i': 2,
|
||||
}
|
||||
object3 = {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': 'abcdef',
|
||||
'i': 3,
|
||||
}
|
||||
cache_symbol_id.append(object1)
|
||||
cache_symbol_id.append(object2)
|
||||
cache_symbol_id.append(object3) # should update index 0
|
||||
assert equals(cache_symbol_id, [object2, object3])
|
||||
# ----------------------------------------------------------------------------
|
||||
cache_symbol_id_5 = ArrayCacheBySymbolById(5)
|
||||
for i in range(1, 11):
|
||||
cache_symbol_id_5.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': str(i),
|
||||
'i': i,
|
||||
})
|
||||
assert(equals(cache_symbol_id_5, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '6',
|
||||
'i': 6,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 7,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 8,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '9',
|
||||
'i': 9,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '10',
|
||||
'i': 10,
|
||||
}]))
|
||||
for i in range(1, 11):
|
||||
cache_symbol_id_5.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': str(i),
|
||||
'i': i + 10,
|
||||
})
|
||||
assert(equals(cache_symbol_id_5, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '6',
|
||||
'i': 16,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 17,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 18,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '9',
|
||||
'i': 19,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '10',
|
||||
'i': 20,
|
||||
}]))
|
||||
middle = {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 28,
|
||||
}
|
||||
cache_symbol_id_5.append(middle)
|
||||
assert(equals(cache_symbol_id_5, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '6',
|
||||
'i': 16,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 17,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '9',
|
||||
'i': 19,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '10',
|
||||
'i': 20,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 28,
|
||||
}]))
|
||||
other_middle = {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 27,
|
||||
}
|
||||
cache_symbol_id_5.append(other_middle)
|
||||
assert(equals(cache_symbol_id_5, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '6',
|
||||
'i': 16,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '9',
|
||||
'i': 19,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '10',
|
||||
'i': 20,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 28,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 27,
|
||||
}]))
|
||||
for i in range(30, 33):
|
||||
cache_symbol_id_5.append({
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': str(i),
|
||||
'i': i + 10,
|
||||
})
|
||||
assert(equals(cache_symbol_id_5, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 28,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 27,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '30',
|
||||
'i': 40,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '31',
|
||||
'i': 41,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '32',
|
||||
'i': 42,
|
||||
}]))
|
||||
first = {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 38,
|
||||
}
|
||||
cache_symbol_id_5.append(first)
|
||||
assert(equals(cache_symbol_id_5, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 27,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '30',
|
||||
'i': 40,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '31',
|
||||
'i': 41,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '32',
|
||||
'i': 42,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 38,
|
||||
}]))
|
||||
another = {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '30',
|
||||
'i': 50,
|
||||
}
|
||||
cache_symbol_id_5.append(another)
|
||||
assert(equals(cache_symbol_id_5, [{
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '7',
|
||||
'i': 27,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '31',
|
||||
'i': 41,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '32',
|
||||
'i': 42,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '8',
|
||||
'i': 38,
|
||||
}, {
|
||||
'symbol': 'BTC/USDT',
|
||||
'id': '30',
|
||||
'i': 50,
|
||||
}]))
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolById limit with symbol set
|
||||
symbol = 'BTC/USDT'
|
||||
cache_symbol_id_2 = ArrayCacheBySymbolById()
|
||||
initial_length = 5
|
||||
for i in range(0, initial_length):
|
||||
cache_symbol_id_2.append({
|
||||
'symbol': symbol,
|
||||
'id': str(i),
|
||||
'i': i,
|
||||
})
|
||||
limited = cache_symbol_id_2.get_limit(symbol, None)
|
||||
assert initial_length == limited
|
||||
# ----------------------------------------------------------------------------
|
||||
cache_symbol_id_3 = ArrayCacheBySymbolById()
|
||||
append_items_length = 3
|
||||
for i in range(0, append_items_length):
|
||||
cache_symbol_id_3.append({
|
||||
'symbol': symbol,
|
||||
'id': str(i),
|
||||
'i': i,
|
||||
})
|
||||
outside_limit = 5
|
||||
limited = cache_symbol_id_3.get_limit(symbol, outside_limit)
|
||||
assert append_items_length == limited
|
||||
outside_limit = 2 # if limit < newsUpdate that should be returned
|
||||
limited = cache_symbol_id_3.get_limit(symbol, outside_limit)
|
||||
assert outside_limit == limited
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolById limit with symbol undefined
|
||||
symbol = 'BTC/USDT'
|
||||
cache_symbol_id_4 = ArrayCacheBySymbolById()
|
||||
initial_length = 5
|
||||
for i in range(0, initial_length):
|
||||
cache_symbol_id_4.append({
|
||||
'symbol': symbol,
|
||||
'id': str(i),
|
||||
'i': i,
|
||||
})
|
||||
limited = cache_symbol_id_4.get_limit(None, None)
|
||||
assert initial_length == limited
|
||||
# ----------------------------------------------------------------------------
|
||||
cache_symbol_id_6 = ArrayCacheBySymbolById()
|
||||
append_items_length = 3
|
||||
for i in range(0, append_items_length):
|
||||
cache_symbol_id_6.append({
|
||||
'symbol': symbol,
|
||||
'id': str(i),
|
||||
'i': i,
|
||||
})
|
||||
outside_limit = 5
|
||||
limited = cache_symbol_id_6.get_limit(symbol, outside_limit)
|
||||
assert append_items_length == limited
|
||||
outside_limit = 2 # if limit < newsUpdate that should be returned
|
||||
limited = cache_symbol_id_6.get_limit(symbol, outside_limit)
|
||||
assert outside_limit == limited
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolById, same order should not increase the limit
|
||||
cache_symbol_id_7 = ArrayCacheBySymbolById()
|
||||
symbol = 'BTC/USDT'
|
||||
other_symbol = 'ETH/USDT'
|
||||
cache_symbol_id_7.append({
|
||||
'symbol': symbol,
|
||||
'id': 'singleId',
|
||||
'i': 3,
|
||||
})
|
||||
cache_symbol_id_7.append({
|
||||
'symbol': symbol,
|
||||
'id': 'singleId',
|
||||
'i': 3,
|
||||
})
|
||||
cache_symbol_id_7.append({
|
||||
'symbol': other_symbol,
|
||||
'id': 'singleId',
|
||||
'i': 3,
|
||||
})
|
||||
outside_limit = 5
|
||||
limited = cache_symbol_id_7.get_limit(symbol, outside_limit)
|
||||
limited2 = cache_symbol_id_7.get_limit(None, outside_limit)
|
||||
assert limited == 1
|
||||
assert limited2 == 2
|
||||
# ----------------------------------------------------------------------------
|
||||
# test testLimitArrayCacheByTimestamp limit
|
||||
timestamp_cache_2 = ArrayCacheByTimestamp()
|
||||
initial_length = 5
|
||||
for i in range(0, initial_length):
|
||||
timestamp_cache_2.append([i * 10, i * 10, i * 10, i * 10])
|
||||
limited = timestamp_cache_2.get_limit(None, None)
|
||||
assert initial_length == limited
|
||||
append_items_length = 3
|
||||
for i in range(0, append_items_length):
|
||||
timestamp_cache_2.append([i * 4, i * 4, i * 4, i * 4])
|
||||
outside_limit = 5
|
||||
limited = timestamp_cache_2.get_limit(None, outside_limit)
|
||||
assert append_items_length == limited
|
||||
outside_limit = 2 # if limit < newsUpdate that should be returned
|
||||
limited = timestamp_cache_2.get_limit(None, outside_limit)
|
||||
assert outside_limit == limited
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolById, watch all orders, same symbol and order id gets updated
|
||||
cache_symbol_id_8 = ArrayCacheBySymbolById()
|
||||
symbol = 'BTC/USDT'
|
||||
outside_limit = 5
|
||||
cache_symbol_id_8.append({
|
||||
'symbol': symbol,
|
||||
'id': 'oneId',
|
||||
'i': 3,
|
||||
}) # create first order
|
||||
cache_symbol_id_8.get_limit(None, outside_limit) # watch all orders
|
||||
cache_symbol_id_8.append({
|
||||
'symbol': symbol,
|
||||
'id': 'oneId',
|
||||
'i': 4,
|
||||
}) # first order is closed
|
||||
cache_symbol_id_8.get_limit(None, outside_limit) # watch all orders
|
||||
cache_symbol_id_8.append({
|
||||
'symbol': symbol,
|
||||
'id': 'twoId',
|
||||
'i': 5,
|
||||
}) # create second order
|
||||
cache_symbol_id_8.get_limit(None, outside_limit) # watch all orders
|
||||
cache_symbol_id_8.append({
|
||||
'symbol': symbol,
|
||||
'id': 'twoId',
|
||||
'i': 6,
|
||||
}) # second order is closed
|
||||
limited = cache_symbol_id_8.get_limit(None, outside_limit) # watch all orders
|
||||
assert limited == 1 # one new update
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolById, watch all orders, and watchOrders (symbol) work independently
|
||||
cache_symbol_id_9 = ArrayCacheBySymbolById()
|
||||
symbol = 'BTC/USDT'
|
||||
symbol2 = 'ETH/USDT'
|
||||
outside_limit = 5
|
||||
cache_symbol_id_9.append({
|
||||
'symbol': symbol,
|
||||
'id': 'one',
|
||||
'i': 1,
|
||||
}) # create first order
|
||||
cache_symbol_id_9.append({
|
||||
'symbol': symbol2,
|
||||
'id': 'two',
|
||||
'i': 1,
|
||||
}) # create second order
|
||||
assert cache_symbol_id_9.get_limit(None, outside_limit) == 2 # watch all orders
|
||||
assert cache_symbol_id_9.get_limit(symbol, outside_limit) == 1 # watch by symbol
|
||||
cache_symbol_id_9.append({
|
||||
'symbol': symbol,
|
||||
'id': 'one',
|
||||
'i': 2,
|
||||
}) # update first order
|
||||
cache_symbol_id_9.append({
|
||||
'symbol': symbol2,
|
||||
'id': 'two',
|
||||
'i': 2,
|
||||
}) # update second order
|
||||
assert cache_symbol_id_9.get_limit(symbol, outside_limit) == 1 # watch by symbol
|
||||
assert cache_symbol_id_9.get_limit(None, outside_limit) == 2 # watch all orders
|
||||
cache_symbol_id_9.append({
|
||||
'symbol': symbol2,
|
||||
'id': 'two',
|
||||
'i': 3,
|
||||
}) # update second order
|
||||
cache_symbol_id_9.append({
|
||||
'symbol': symbol2,
|
||||
'id': 'three',
|
||||
'i': 3,
|
||||
}) # create third order
|
||||
assert cache_symbol_id_9.get_limit(None, outside_limit) == 2 # watch all orders
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolBySide, watch all positions, same symbol and side id gets updated
|
||||
cache_symbol_side = ArrayCacheBySymbolBySide()
|
||||
symbol = 'BTC/USDT'
|
||||
outside_limit = 5
|
||||
cache_symbol_side.append({
|
||||
'symbol': symbol,
|
||||
'side': 'short',
|
||||
'contracts': 1,
|
||||
}) # create first position
|
||||
cache_symbol_side.append({
|
||||
'symbol': symbol,
|
||||
'side': 'short',
|
||||
'contracts': 0,
|
||||
}) # first position is closed
|
||||
assert cache_symbol_side.get_limit(symbol, outside_limit) == 1 # limit position
|
||||
cache_symbol_side.append({
|
||||
'symbol': symbol,
|
||||
'side': 'short',
|
||||
'contracts': 1,
|
||||
}) # create first position
|
||||
assert cache_symbol_side.get_limit(symbol, outside_limit) == 1 # watch all positions
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolBySide, watch all positions, same symbol and side id gets updated
|
||||
cache_symbol_side_2 = ArrayCacheBySymbolBySide()
|
||||
symbol = 'BTC/USDT'
|
||||
outside_limit = 5
|
||||
cache_symbol_side_2.append({
|
||||
'symbol': symbol,
|
||||
'side': 'short',
|
||||
'contracts': 1,
|
||||
}) # create first position
|
||||
assert cache_symbol_side_2.get_limit(None, outside_limit) == 1 # watch all positions
|
||||
cache_symbol_side_2.append({
|
||||
'symbol': symbol,
|
||||
'side': 'short',
|
||||
'contracts': 0,
|
||||
}) # first position is closed
|
||||
assert cache_symbol_side_2.get_limit(None, outside_limit) == 1 # watch all positions
|
||||
cache_symbol_side_2.append({
|
||||
'symbol': symbol,
|
||||
'side': 'long',
|
||||
'contracts': 3,
|
||||
}) # create second position
|
||||
assert cache_symbol_side_2.get_limit(None, outside_limit) == 1 # watch all positions
|
||||
cache_symbol_side_2.append({
|
||||
'symbol': symbol,
|
||||
'side': 'long',
|
||||
'contracts': 2,
|
||||
}) # second position is reduced
|
||||
cache_symbol_side_2.append({
|
||||
'symbol': symbol,
|
||||
'side': 'long',
|
||||
'contracts': 1,
|
||||
}) # second position is reduced
|
||||
assert cache_symbol_side_2.get_limit(None, outside_limit) == 1 # watch all orders
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolBySide, watchPositions, and watchPosition (symbol) work independently
|
||||
cache_symbol_side_3 = ArrayCacheBySymbolBySide()
|
||||
symbol = 'BTC/USDT'
|
||||
symbol2 = 'ETH/USDT'
|
||||
cache_symbol_side_3.append({
|
||||
'symbol': symbol,
|
||||
'side': 'short',
|
||||
'contracts': 1,
|
||||
}) # create first position
|
||||
cache_symbol_side_3.append({
|
||||
'symbol': symbol2,
|
||||
'side': 'long',
|
||||
'contracts': 1,
|
||||
}) # create second position
|
||||
assert cache_symbol_side_3.get_limit(None, outside_limit) == 2 # watch all positions
|
||||
assert cache_symbol_side_3.get_limit(symbol, outside_limit) == 1 # watch by symbol
|
||||
cache_symbol_side_3.append({
|
||||
'symbol': symbol,
|
||||
'side': 'short',
|
||||
'contracts': 2,
|
||||
}) # update first position
|
||||
cache_symbol_side_3.append({
|
||||
'symbol': symbol2,
|
||||
'side': 'long',
|
||||
'contracts': 2,
|
||||
}) # update second position
|
||||
assert cache_symbol_side_3.get_limit(symbol, outside_limit) == 1 # watch by symbol
|
||||
assert cache_symbol_side_3.get_limit(None, outside_limit) == 2 # watch all positions
|
||||
cache_symbol_side_3.append({
|
||||
'symbol': symbol2,
|
||||
'side': 'long',
|
||||
'contracts': 3,
|
||||
}) # update second position
|
||||
assert cache_symbol_side_3.get_limit(None, outside_limit) == 1 # watch all positions
|
||||
# ----------------------------------------------------------------------------
|
||||
# test ArrayCacheBySymbolBySide, watchPositions does not override
|
||||
cache_symbol_side_4 = ArrayCacheBySymbolBySide()
|
||||
symbol = 'BTC/USDT'
|
||||
symbol2 = 'ETH/USDT'
|
||||
symbol3 = 'XRP/USDT'
|
||||
cache_symbol_side_4.append({
|
||||
'symbol': symbol,
|
||||
'side': 'long',
|
||||
'contracts': 1,
|
||||
}) # create first position
|
||||
cache_symbol_side_4.append({
|
||||
'symbol': symbol2,
|
||||
'side': 'long',
|
||||
'contracts': 2,
|
||||
}) # create second position
|
||||
cache_symbol_side_4.append({
|
||||
'symbol': symbol3,
|
||||
'side': 'long',
|
||||
'contracts': 3,
|
||||
}) # create short position
|
||||
assert cache_symbol_side_4[0]['symbol'] == symbol
|
||||
assert cache_symbol_side_4[1]['symbol'] == symbol2
|
||||
cache_symbol_side_4.append({
|
||||
'symbol': symbol2,
|
||||
'side': 'long',
|
||||
'contracts': 4,
|
||||
}) # update first position
|
||||
assert cache_symbol_side_4[0]['contracts'] == 1 and cache_symbol_side_4[0]['symbol'] == symbol
|
||||
assert cache_symbol_side_4[1]['contracts'] == 3 and cache_symbol_side_4[1]['symbol'] == symbol3
|
||||
assert cache_symbol_side_4[2]['contracts'] == 4 and cache_symbol_side_4[2]['symbol'] == symbol2
|
||||
array_length = len(cache_symbol_side_4)
|
||||
assert array_length == 3
|
||||
184
ccxt/pro/test/base/test_close.py
Normal file
184
ccxt/pro/test/base/test_close.py
Normal file
@@ -0,0 +1,184 @@
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
from asyncio import sleep, gather
|
||||
import ccxt.pro
|
||||
|
||||
async def watch_ticker_loop(exchange):
|
||||
try:
|
||||
while True:
|
||||
ticker = await exchange.watch_ticker('BTC/USDT')
|
||||
print('ticker received')
|
||||
except Exception as e:
|
||||
print(f"{e}")
|
||||
raise e
|
||||
|
||||
|
||||
async def watch_trades_for_symbols_loop(exchange):
|
||||
try:
|
||||
while True:
|
||||
trades = await exchange.watch_trades_for_symbols(['BTC/USDT', 'ETH/USDT', 'LTC/USDT'])
|
||||
print('trades received')
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
async def close_after(exchange, ms):
|
||||
await sleep(ms)
|
||||
await exchange.close()
|
||||
|
||||
|
||||
async def test_ws_close():
|
||||
exchange = ccxt.pro.binance()
|
||||
# exchange.verbose = True
|
||||
# --------------------------------------------
|
||||
print('---- Testing exchange.close(): No future awaiting, should close with no errors')
|
||||
await exchange.watch_ticker('BTC/USD')
|
||||
print('ticker received')
|
||||
await exchange.close()
|
||||
print('PASSED - exchange closed with no errors')
|
||||
# --------------------------------------------
|
||||
print('---- Testing exchange.close(): Open watch multiple, resolve, should close with no errors')
|
||||
await exchange.watch_trades_for_symbols(['BTC/USD', 'ETH/USD', 'LTC/USD'])
|
||||
print('ticker received')
|
||||
await exchange.close()
|
||||
print('PASSED - exchange closed with no errors')
|
||||
# --------------------------------------------
|
||||
print('---- Testing exchange.close(): Awaiting future should throw ClosedByUser')
|
||||
try:
|
||||
await gather(close_after(exchange, 4), watch_ticker_loop(exchange))
|
||||
assert False, "Expected Future rejected with ClosedByUser"
|
||||
except asyncio.CancelledError:
|
||||
assert True
|
||||
except Exception as e:
|
||||
print(f"Unexpected exception: {e}")
|
||||
assert False
|
||||
await exchange.close() # Added to ensure close finishes correctly
|
||||
# --------------------------------------------
|
||||
print('---- Testing exchange.close(): Call watch_multiple unhandled futures are canceled')
|
||||
try:
|
||||
await gather(close_after(exchange, 4), watch_trades_for_symbols_loop(exchange))
|
||||
assert False, "Expected ExchangeClosedByUser error"
|
||||
except asyncio.CancelledError:
|
||||
assert True
|
||||
except Exception as e:
|
||||
print(f"Unexpected exception: {e}")
|
||||
assert False
|
||||
await exchange.close()
|
||||
# --------------------------------------------
|
||||
await test_cancelled_task_no_invalid_state(exchange)
|
||||
# --------------------------------------------
|
||||
await test_unwatch_tickers_after_cancellation(exchange)
|
||||
|
||||
async def test_cancelled_task_no_invalid_state(exchange):
|
||||
"""
|
||||
Connects to a real exchange, starts two watch_ticker tasks, cancels one,
|
||||
and ensures no InvalidStateError or unhandled exception occurs when messages are received after cancellation.
|
||||
"""
|
||||
print('---- Testing cancelled task does not raise InvalidStateError')
|
||||
symbols = ['BTC/USDT', 'ETH/USDT']
|
||||
received = {s: 0 for s in symbols}
|
||||
errors = []
|
||||
|
||||
async def watch_ticker_task(symbol):
|
||||
try:
|
||||
while True:
|
||||
ticker = await exchange.watch_ticker(symbol)
|
||||
received[symbol] += 1
|
||||
print(f"[TICKER] {symbol} received: {ticker['datetime'] if 'datetime' in ticker else ticker}")
|
||||
except asyncio.CancelledError:
|
||||
print(f"[CANCELLED] {symbol} task cancelled")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] {symbol}: {e}")
|
||||
errors.append(e)
|
||||
|
||||
# Start both tasks
|
||||
task_btc = asyncio.create_task(watch_ticker_task(symbols[0]))
|
||||
task_eth = asyncio.create_task(watch_ticker_task(symbols[1]))
|
||||
|
||||
# Let both run for a bit
|
||||
await asyncio.sleep(5)
|
||||
|
||||
# Cancel ETH/USDT task
|
||||
print("Cancelling ETH/USDT task...")
|
||||
task_eth.cancel()
|
||||
try:
|
||||
await task_eth
|
||||
except asyncio.CancelledError:
|
||||
print("ETH/USDT task cancelled and awaited")
|
||||
|
||||
# Let BTC/USDT keep running, and see if any errors occur for ETH/USDT
|
||||
await asyncio.sleep(5)
|
||||
|
||||
# Check for InvalidStateError or any unhandled exception
|
||||
for err in errors:
|
||||
assert not isinstance(err, asyncio.InvalidStateError), f"InvalidStateError occurred: {err}"
|
||||
print("No InvalidStateError occurred after cancelling ETH/USDT task.")
|
||||
|
||||
# Cleanup
|
||||
task_btc.cancel()
|
||||
await exchange.close()
|
||||
|
||||
async def test_unwatch_tickers_after_cancellation(exchange):
|
||||
"""
|
||||
Tests the specific case where un_watch_tickers() is called after cancelling tasks,
|
||||
which was causing InvalidStateError. This reproduces the user's reported issue, but for tickers.
|
||||
"""
|
||||
print('---- Testing un_watch_tickers() after task cancellation does not raise InvalidStateError')
|
||||
|
||||
async def watch_tickers_task(symbols):
|
||||
try:
|
||||
while True:
|
||||
tickers = await exchange.watch_tickers(symbols)
|
||||
print(f"[TICKERS] {symbols} received: {list(tickers.keys()) if hasattr(tickers, 'keys') else tickers}")
|
||||
except asyncio.CancelledError:
|
||||
print(f"[CANCELLED] {symbols} tickers task cancelled")
|
||||
except Exception as e:
|
||||
assert False, (f"[ERROR] {symbols} tickers: {e}")
|
||||
|
||||
# Start both tasks for different symbols
|
||||
print("Starting BTC/USDT tickers watch...")
|
||||
task_btc = asyncio.create_task(watch_tickers_task(['BTC/USDT']))
|
||||
print("Starting ETH/USDT tickers watch...")
|
||||
task_eth = asyncio.create_task(watch_tickers_task(['ETH/USDT']))
|
||||
|
||||
# Let both run for a bit
|
||||
print("Letting tasks run for 5 seconds...")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
# Cancel ETH/USDT task
|
||||
print("Cancelling ETH/USDT task...")
|
||||
task_eth.cancel()
|
||||
try:
|
||||
await task_eth
|
||||
except asyncio.CancelledError:
|
||||
print("ETH/USDT task cancelled and awaited")
|
||||
|
||||
# Let BTC/USDT keep running
|
||||
print("Letting BTC/USDT task continue for 5 seconds...")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
# This is the critical part - calling un_watch_tickers() after cancellation
|
||||
print("Calling un_watch_tickers() after task cancellation...")
|
||||
try:
|
||||
await exchange.un_watch_tickers()
|
||||
print("un_watch_tickers() completed successfully")
|
||||
except asyncio.InvalidStateError as e:
|
||||
assert False, f"InvalidStateError occurred: {e}"
|
||||
except Exception as e:
|
||||
print(f"Unexpected exception during un_watch_tickers(): {e}")
|
||||
|
||||
# Let it run a bit more to see if any errors occur
|
||||
print("Letting it run for 5 more seconds...")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
# Cleanup
|
||||
task_btc.cancel()
|
||||
await exchange.close()
|
||||
print("PASSED - un_watch_tickers() after cancellation test completed successfully")
|
||||
|
||||
asyncio.run(test_ws_close())
|
||||
194
ccxt/pro/test/base/test_future.py
Normal file
194
ccxt/pro/test/base/test_future.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Assuming the structure of test_shared_methods based on common unittest methods
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
from ccxt import ExchangeClosedByUser
|
||||
from ccxt.async_support.base.ws.future import Future
|
||||
|
||||
# Helper functions
|
||||
async def resolve_later(future, result, delay):
|
||||
await asyncio.sleep(delay)
|
||||
future.resolve(result)
|
||||
|
||||
async def cancel_later(future, delay):
|
||||
await asyncio.sleep(delay)
|
||||
future.cancel()
|
||||
|
||||
async def reject_later(future, err, delay):
|
||||
await asyncio.sleep(delay)
|
||||
future.reject(err)
|
||||
|
||||
async def test_resolve_before():
|
||||
print("test_resolve")
|
||||
future = Future()
|
||||
expected_result = "test"
|
||||
future.resolve(expected_result)
|
||||
assert future.done(), "Future is not marked as done"
|
||||
assert future.result() == expected_result, f"Expected result '{expected_result}', got '{future.result()}'"
|
||||
|
||||
async def test_reject():
|
||||
print("test_reject")
|
||||
future = Future()
|
||||
test_exception = Exception("test error")
|
||||
future.reject(test_exception)
|
||||
assert future.done(), "Future is not marked as done"
|
||||
try:
|
||||
future.result()
|
||||
assert False, "Expected an exception but none was raised"
|
||||
except Exception as e:
|
||||
assert str(e) == "test error", f"Expected 'test error', got '{str(e)}'"
|
||||
|
||||
async def test_race_success_before():
|
||||
print("test_race_success")
|
||||
future1 = Future()
|
||||
future2 = Future()
|
||||
race_future = Future.race([future1, future2])
|
||||
future1.resolve("first")
|
||||
result = await race_future
|
||||
future2.cancel()
|
||||
assert result == "first", f"Expected 'first', got '{result}'"
|
||||
|
||||
async def test_race_success_after():
|
||||
print("test_race_success")
|
||||
future1 = Future()
|
||||
future2 = Future()
|
||||
race_future = Future.race([future1, future2])
|
||||
asyncio.create_task(resolve_later(future1, "first", 0.01))
|
||||
result = await race_future
|
||||
future2.cancel()
|
||||
assert result == "first", f"Expected 'first', got '{result}'"
|
||||
|
||||
async def test_race_return_first_exception():
|
||||
print("test_race_return_first_exception")
|
||||
future1 = Future()
|
||||
race_future = Future.race([future1])
|
||||
future1.reject(Exception("Error in future1"))
|
||||
try:
|
||||
await race_future
|
||||
assert False, "Expected an exception but none was raised"
|
||||
except Exception as e:
|
||||
assert str(e) == "Error in future1", f"Expected 'Error in future1', got '{str(e)}'"
|
||||
|
||||
async def test_await_canceled_future():
|
||||
print("test_await_canceled_future")
|
||||
future = Future()
|
||||
try:
|
||||
future.cancel()
|
||||
await future
|
||||
assert False, "Expected an exception but none was raised"
|
||||
except asyncio.CancelledError as e:
|
||||
assert isinstance(e, asyncio.CancelledError), "Expected asyncio.CancelledError"
|
||||
|
||||
async def test_cancel():
|
||||
print("test_cancel")
|
||||
future = Future()
|
||||
asyncio.create_task(cancel_later(future, 0.1))
|
||||
try:
|
||||
await future
|
||||
assert False, "Expected an exception but none was raised"
|
||||
except asyncio.CancelledError as e:
|
||||
assert isinstance(e, asyncio.CancelledError), "Expected asyncio.CancelledError"
|
||||
|
||||
async def test_race_cancel():
|
||||
print("test_race_cancel")
|
||||
try:
|
||||
future1 = Future()
|
||||
future2 = Future()
|
||||
race_future = Future.race([future1, future2])
|
||||
race_future.cancel()
|
||||
future1.resolve("success")
|
||||
await race_future
|
||||
assert False, "Expected a cancelledError"
|
||||
except asyncio.CancelledError:
|
||||
assert True
|
||||
|
||||
async def test_race_mixed_outcomes():
|
||||
print("test_race_mixed_outcome")
|
||||
future1 = Future()
|
||||
future2 = Future()
|
||||
race_future = Future.race([future1, future2])
|
||||
future1.resolve("first")
|
||||
task = asyncio.create_task(reject_later(future2, Exception("Error in future2"), 0.1))
|
||||
result = await race_future
|
||||
assert result == "first", f"Expected 'first', got '{result}'"
|
||||
task.cancel()
|
||||
future2.cancel()
|
||||
|
||||
async def test_race_with_wait_for_timeout():
|
||||
print("test_race_with_wait_for_timeout")
|
||||
future1 = Future()
|
||||
|
||||
task = asyncio.create_task(resolve_later(future1, "completed first", 2))
|
||||
|
||||
# Attempt to race the futures with a timeout shorter than their resolution time
|
||||
try:
|
||||
race_future = Future.race([future1])
|
||||
await asyncio.wait_for(race_future, timeout=1) # Timeout is set deliberately short
|
||||
assert False, "Expected a timeout but race_future completed"
|
||||
except asyncio.TimeoutError:
|
||||
# Expected outcome, the race_future should not complete within the timeout
|
||||
assert True
|
||||
await task
|
||||
|
||||
async def test_race_with_wait_for_completion():
|
||||
print("test_race_with_wait_for_completion")
|
||||
future1 = Future()
|
||||
future2 = Future()
|
||||
|
||||
task = asyncio.create_task(resolve_later(future1, "completed first", 0.1))
|
||||
|
||||
# Race the futures with a timeout longer than necessary
|
||||
try:
|
||||
race_future = Future.race([future1, future2])
|
||||
result = await asyncio.wait_for(race_future, timeout=1)
|
||||
assert result == "completed first", f"Unexpected race result: {result}"
|
||||
except asyncio.TimeoutError:
|
||||
assert False, "Did not expect a timeout"
|
||||
await task
|
||||
|
||||
async def test_race_with_precompleted_future():
|
||||
print("test_race_with_precompleted_future")
|
||||
future1 = Future()
|
||||
future2 = Future()
|
||||
future1.resolve("immediate success")
|
||||
# Immediately resolved future before race call
|
||||
race_future = Future.race([future1, future2])
|
||||
result = await race_future
|
||||
assert result == "immediate success", "Race did not correctly prioritize already completed future."
|
||||
|
||||
async def test_closed_by_user():
|
||||
print("test_closed_by_user")
|
||||
future1 = Future()
|
||||
future2 = Future()
|
||||
race_future = Future.race([future1, future2])
|
||||
task1 = asyncio.create_task(reject_later(future1, ExchangeClosedByUser(), 0.1))
|
||||
task2 = asyncio.create_task(reject_later(future2, ExchangeClosedByUser(), 0.1))
|
||||
try:
|
||||
await race_future
|
||||
assert False, "Expected an ExchangeClosedByUser"
|
||||
except ExchangeClosedByUser:
|
||||
assert True
|
||||
assert task1.done()
|
||||
assert task2.done()
|
||||
except Exception as e:
|
||||
assert False, f"Received Exception {e}"
|
||||
|
||||
async def test_ws_future():
|
||||
await test_resolve_before()
|
||||
await test_reject()
|
||||
await test_race_success_before()
|
||||
await test_race_return_first_exception()
|
||||
await test_cancel()
|
||||
await test_await_canceled_future()
|
||||
await test_race_cancel()
|
||||
await test_race_mixed_outcomes()
|
||||
await test_race_with_wait_for_timeout()
|
||||
await test_race_with_wait_for_completion()
|
||||
await test_race_with_precompleted_future()
|
||||
await test_closed_by_user()
|
||||
|
||||
345
ccxt/pro/test/base/test_order_book.py
Normal file
345
ccxt/pro/test/base/test_order_book.py
Normal file
@@ -0,0 +1,345 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
|
||||
from ccxt.async_support.base.ws.order_book import OrderBook, IndexedOrderBook, CountedOrderBook # noqa: F402
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def equals(a, b):
|
||||
return a == b
|
||||
|
||||
# --------------------------------------------------------------------------------------------------------------------
|
||||
def test_ws_order_book():
|
||||
order_book_input = {
|
||||
'bids': [[10, 10], [9.1, 11], [8.2, 12], [7.3, 13], [6.4, 14], [4.5, 13], [4.5, 0]],
|
||||
'asks': [[16.6, 10], [15.5, 11], [14.4, 12], [13.3, 13], [12.2, 14], [11.1, 13]],
|
||||
'timestamp': 1574827239000,
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
order_book_target = {
|
||||
'bids': [[10, 10], [9.1, 11], [8.2, 12], [7.3, 13], [6.4, 14]],
|
||||
'asks': [[11.1, 13], [12.2, 14], [13.3, 13], [14.4, 12], [15.5, 11], [16.6, 10]],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
store_bid = {
|
||||
'bids': [[10, 10], [9.1, 11], [8.2, 12], [7.3, 13], [6.4, 14], [3, 4]],
|
||||
'asks': [[11.1, 13], [12.2, 14], [13.3, 13], [14.4, 12], [15.5, 11], [16.6, 10]],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
limited_order_book_target = {
|
||||
'bids': [[10, 10], [9.1, 11], [8.2, 12], [7.3, 13], [6.4, 14]],
|
||||
'asks': [[11.1, 13], [12.2, 14], [13.3, 13], [14.4, 12], [15.5, 11]],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
limited_deleted_order_book_target = {
|
||||
'bids': [[10, 10], [9.1, 11], [8.2, 12], [7.3, 13], [6.4, 14]],
|
||||
'asks': [[11.1, 13], [12.2, 14], [13.3, 13], [14.4, 12]],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
indexed_order_book_input = {
|
||||
'bids': [[10, 10, '1234'], [9.1, 11, '1235'], [8.2, 12, '1236'], [7.3, 13, '1237'], [6.4, 14, '1238'], [4.5, 13, '1239']],
|
||||
'asks': [[16.6, 10, '1240'], [15.5, 11, '1241'], [14.4, 12, '1242'], [13.3, 13, '1243'], [12.2, 14, '1244'], [11.1, 13, '1244']],
|
||||
'timestamp': 1574827239000,
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
indexed_order_book_target = {
|
||||
'bids': [[10, 10, '1234'], [9.1, 11, '1235'], [8.2, 12, '1236'], [7.3, 13, '1237'], [6.4, 14, '1238'], [4.5, 13, '1239']],
|
||||
'asks': [[11.1, 13, '1244'], [13.3, 13, '1243'], [14.4, 12, '1242'], [15.5, 11, '1241'], [16.6, 10, '1240']],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
limited_indexed_order_book_target = {
|
||||
'bids': [[10, 10, '1234'], [9.1, 11, '1235'], [8.2, 12, '1236'], [7.3, 13, '1237'], [6.4, 14, '1238']],
|
||||
'asks': [[11.1, 13, '1244'], [13.3, 13, '1243'], [14.4, 12, '1242'], [15.5, 11, '1241'], [16.6, 10, '1240']],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
# const incrementalIndexedOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 10, '1234' ], [ 9.1, 11, '1235' ], [ 8.2, 12, '1236' ], [ 7.3, 13, '1237' ], [ 6.4, 14, '1238' ], [ 4.5, 13, '1239' ] ],
|
||||
# 'asks': [ [ 11.1, 27, '1244' ], [ 13.3, 13, '1243' ], [ 14.4, 12, '1242' ], [ 15.5, 11, '1241' ], [ 16.6, 10, '1240' ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const incrementalIndexedOrderBookDeletedTarget = {
|
||||
# 'bids': [ [ 9.1, 11, '1235' ], [ 8.2, 12, '1236' ], [ 7.3, 13, '1237' ], [ 6.4, 14, '1238' ], [ 4.5, 13, '1239' ] ],
|
||||
# 'asks': [ [ 11.1, 27, '1244' ], [ 13.3, 13, '1243' ], [ 14.4, 12, '1242' ], [ 15.5, 11, '1241' ], [ 16.6, 10, '1240' ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const limitedIncrementalIndexedOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 10, '1234' ], [ 9.1, 11, '1235' ], [ 8.2, 12, '1236' ], [ 7.3, 13, '1237' ], [ 6.4, 14, '1238' ] ],
|
||||
# 'asks': [ [ 11.1, 27, '1244' ], [ 13.3, 13, '1243' ], [ 14.4, 12, '1242' ], [ 15.5, 11, '1241' ], [ 16.6, 10, '1240' ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const storedIncrementalIndexedOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 13, '1234' ], [ 9.1, 11, '1235' ], [ 8.2, 12, '1236' ], [ 7.3, 13, '1237' ], [ 6.4, 14, '1238' ], [ 4.5, 13, '1239' ] ],
|
||||
# 'asks': [ [ 11.1, 27, '1244' ], [ 13.3, 13, '1243' ], [ 14.4, 12, '1242' ], [ 15.5, 11, '1241' ], [ 16.6, 10, '1240' ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const anotherStoredIncrementalIndexedOrderBookTarget = {
|
||||
# 'bids': [ [ 10.2, 13, '1234' ], [ 9.1, 11, '1235' ], [ 8.2, 12, '1236' ], [ 7.3, 13, '1237' ], [ 6.4, 14, '1238' ], [ 4.5, 13, '1239' ] ],
|
||||
# 'asks': [ [ 11.1, 27, '1244' ], [ 13.3, 13, '1243' ], [ 14.4, 12, '1242' ], [ 15.5, 11, '1241' ], [ 16.6, 10, '1240' ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
overwrite1234 = {
|
||||
'bids': [[9.1, 11, '1235'], [9, 3, '1231'], [9, 1, '1232'], [8.2, 12, '1236'], [7.3, 13, '1237'], [6.4, 14, '1238'], [4.5, 13, '1239'], [4, 2, '12399']],
|
||||
'asks': [[11.1, 13, '1244'], [13.3, 13, '1243'], [14.4, 12, '1242'], [15.5, 11, '1241'], [16.6, 10, '1240']],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
overwrite1244 = {
|
||||
'bids': [[10, 10, '1234'], [9.1, 11, '1235'], [8.2, 12, '1236'], [7.3, 13, '1237'], [6.4, 14, '1238'], [4.5, 13, '1239']],
|
||||
'asks': [[13.3, 13, '1243'], [13.5, 13, '1244'], [14.4, 12, '1242'], [15.5, 11, '1241'], [16.6, 10, '1240']],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
counted_order_book_input = {
|
||||
'bids': [[10, 10, 1], [9.1, 11, 1], [8.2, 12, 1], [7.3, 13, 1], [7.3, 0, 1], [6.4, 14, 5], [4.5, 13, 5], [4.5, 13, 1], [4.5, 13, 0]],
|
||||
'asks': [[16.6, 10, 1], [15.5, 11, 1], [14.4, 12, 1], [13.3, 13, 3], [12.2, 14, 3], [11.1, 13, 3], [11.1, 13, 12]],
|
||||
'timestamp': 1574827239000,
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
counted_order_book_target = {
|
||||
'bids': [[10, 10, 1], [9.1, 11, 1], [8.2, 12, 1], [6.4, 14, 5]],
|
||||
'asks': [[11.1, 13, 12], [12.2, 14, 3], [13.3, 13, 3], [14.4, 12, 1], [15.5, 11, 1], [16.6, 10, 1]],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
stored_counted_orderbook_target = {
|
||||
'bids': [[10, 10, 1], [9.1, 11, 1], [8.2, 12, 1], [6.4, 14, 5], [1, 1, 6]],
|
||||
'asks': [[11.1, 13, 12], [12.2, 14, 3], [13.3, 13, 3], [14.4, 12, 1], [15.5, 11, 1], [16.6, 10, 1]],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
limited_counted_order_book_target = {
|
||||
'bids': [[10, 10, 1], [9.1, 11, 1], [8.2, 12, 1], [6.4, 14, 5]],
|
||||
'asks': [[11.1, 13, 12], [12.2, 14, 3], [13.3, 13, 3], [14.4, 12, 1], [15.5, 11, 1]],
|
||||
'timestamp': 1574827239000,
|
||||
'datetime': '2019-11-27T04:00:39.000Z',
|
||||
'nonce': 69,
|
||||
'symbol': None,
|
||||
}
|
||||
# const incrementalOrderBookInput = {
|
||||
# 'bids': [ [ 10.0, 1 ], [ 10.0, 2 ], [ 9.1, 0 ], [ 8.2, 1 ], [ 7.3, 1 ], [ 6.4, 1 ] ],
|
||||
# 'asks': [ [ 11.1, 5 ], [ 11.1, -6 ], [ 11.1, 2 ], [ 12.2, 10 ], [ 12.2, -9.875 ], [ 12.2, 0 ], [ 13.3, 3 ], [ 14.4, 4 ], [ 15.5, 1 ], [ 16.6, 3 ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const incremetalOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 3 ], [ 8.2, 1 ], [ 7.3, 1 ], [ 6.4, 1 ] ],
|
||||
# 'asks': [ [ 11.1, 2 ], [ 12.2, 0.125 ], [ 13.3, 3 ], [ 14.4, 4 ], [ 15.5, 1 ], [ 16.6, 3 ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const limitedIncremetalOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 3 ], [ 8.2, 1 ], [ 7.3, 1 ], [ 6.4, 1 ] ],
|
||||
# 'asks': [ [ 11.1, 2 ], [ 12.2, 0.125 ], [ 13.3, 3 ], [ 14.4, 4 ], [ 15.5, 1 ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const storedIncremetalOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 3 ], [ 8.2, 1 ], [ 7.3, 1 ], [ 6.4, 1 ], [ 3, 3 ] ],
|
||||
# 'asks': [ [ 11.1, 2 ], [ 12.2, 0.125 ], [ 13.3, 3 ], [ 14.4, 4 ], [ 15.5, 1 ], [ 16.6, 3 ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const doubleStoredIncremetalOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 3 ], [ 8.2, 1 ], [ 7.3, 1 ], [ 6.4, 1 ], [ 3, 10 ] ],
|
||||
# 'asks': [ [ 11.1, 2 ], [ 12.2, 0.125 ], [ 13.3, 3 ], [ 14.4, 4 ], [ 15.5, 1 ], [ 16.6, 3 ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# const negativeStoredIncremetalOrderBookTarget = {
|
||||
# 'bids': [ [ 10.0, 3 ], [ 8.2, 1 ], [ 7.3, 1 ], [ 6.4, 1 ] ],
|
||||
# 'asks': [ [ 11.1, 2 ], [ 12.2, 0.125 ], [ 13.3, 3 ], [ 14.4, 4 ], [ 16.6, 3 ] ],
|
||||
# 'timestamp': 1574827239000,
|
||||
# 'datetime': '2019-11-27T04:00:39.000Z',
|
||||
# 'nonce': 69,
|
||||
# 'symbol': undefined,
|
||||
# };
|
||||
# --------------------------------------------------------------------------------------------------------------------
|
||||
order_book = OrderBook(order_book_input)
|
||||
limited = OrderBook(order_book_input, 5)
|
||||
order_book.limit()
|
||||
assert equals(order_book, order_book_target)
|
||||
limited.limit()
|
||||
assert equals(limited, limited_order_book_target)
|
||||
order_book.limit()
|
||||
assert equals(order_book, order_book_target)
|
||||
bids = order_book['bids']
|
||||
bids.store(1000, 0)
|
||||
order_book.limit()
|
||||
assert equals(order_book, order_book_target)
|
||||
bids.store(3, 4)
|
||||
order_book.limit()
|
||||
assert equals(order_book, store_bid)
|
||||
bids.store(3, 0)
|
||||
order_book.limit()
|
||||
assert equals(order_book, order_book_target)
|
||||
asks = limited['asks']
|
||||
asks.store(15.5, 0)
|
||||
limited.limit()
|
||||
assert equals(limited, limited_deleted_order_book_target)
|
||||
# --------------------------------------------------------------------------------------------------------------------
|
||||
indexed_order_book = IndexedOrderBook(indexed_order_book_input)
|
||||
limited_indexed_order_book = IndexedOrderBook(indexed_order_book_input, 5)
|
||||
indexed_order_book.limit()
|
||||
assert equals(indexed_order_book, indexed_order_book_target)
|
||||
limited_indexed_order_book.limit()
|
||||
assert equals(limited_indexed_order_book, limited_indexed_order_book_target)
|
||||
indexed_order_book.limit()
|
||||
assert equals(indexed_order_book, indexed_order_book_target)
|
||||
indexed_bids = indexed_order_book['bids']
|
||||
indexed_bids.store_array([1000, 0, '12345'])
|
||||
assert equals(indexed_order_book, indexed_order_book_target)
|
||||
indexed_bids.store_array([10, 0, '1234'])
|
||||
indexed_bids.store_array([10, 2, '1231'])
|
||||
indexed_bids.store_array([10, 1, '1232'])
|
||||
indexed_bids.store_array([4, 2, '12399'])
|
||||
indexed_bids.store_array([9, 2, '1231'])
|
||||
indexed_bids.store_array([9, 3, '1231'])
|
||||
indexed_bids.store_array([9, 1, '1232'])
|
||||
indexed_order_book.limit()
|
||||
assert equals(indexed_order_book, overwrite1234)
|
||||
indexed_order_book = IndexedOrderBook(indexed_order_book_input)
|
||||
indexed_asks = indexed_order_book['asks']
|
||||
indexed_asks.store_array([13.5, 13, '1244'])
|
||||
indexed_order_book.limit()
|
||||
assert equals(indexed_order_book, overwrite1244)
|
||||
# --------------------------------------------------------------------------------------------------------------------
|
||||
counted_order_book = CountedOrderBook(counted_order_book_input)
|
||||
limited_counted_order_book = CountedOrderBook(counted_order_book_input, 5)
|
||||
counted_order_book.limit()
|
||||
assert equals(counted_order_book, counted_order_book_target)
|
||||
limited_counted_order_book.limit()
|
||||
assert equals(limited_counted_order_book, limited_counted_order_book_target)
|
||||
counted_order_book.limit()
|
||||
assert equals(counted_order_book, counted_order_book_target)
|
||||
counted_bids = counted_order_book['bids']
|
||||
counted_bids.store_array([5, 0, 6])
|
||||
counted_order_book.limit()
|
||||
assert equals(counted_order_book, counted_order_book_target)
|
||||
counted_bids.store_array([1, 1, 6])
|
||||
counted_order_book.limit()
|
||||
assert equals(counted_order_book, stored_counted_orderbook_target)
|
||||
# --------------------------------------------------------------------------------------------------------------------
|
||||
# let incrementalOrderBook = new IncrementalOrderBook (incrementalOrderBookInput);
|
||||
# const limitedIncrementalOrderBook = new IncrementalOrderBook (incrementalOrderBookInput, 5);
|
||||
# incrementalOrderBook.limit ();
|
||||
# assert (equals (incrementalOrderBook, incremetalOrderBookTarget));
|
||||
# incrementalOrderBook.limit (5);
|
||||
# limitedIncrementalOrderBook.limit ();
|
||||
# assert (equals (incrementalOrderBook, limitedIncremetalOrderBookTarget));
|
||||
# assert (equals (limitedIncrementalOrderBook, limitedIncremetalOrderBookTarget));
|
||||
# incrementalOrderBook.limit ();
|
||||
# assert (equals (incrementalOrderBook, incremetalOrderBookTarget));
|
||||
# bids = incrementalOrderBook['bids'];
|
||||
# bids.store (3, 3);
|
||||
# incrementalOrderBook.limit ();
|
||||
# assert (equals (incrementalOrderBook, storedIncremetalOrderBookTarget));
|
||||
# bids.store (3, 7);
|
||||
# incrementalOrderBook.limit ();
|
||||
# assert (equals (incrementalOrderBook, doubleStoredIncremetalOrderBookTarget));
|
||||
# bids.store (17, 0);
|
||||
# assert (equals (incrementalOrderBook, doubleStoredIncremetalOrderBookTarget));
|
||||
# incrementalOrderBook = new IncrementalOrderBook (incrementalOrderBookInput);
|
||||
# asks = incrementalOrderBook['asks'];
|
||||
# asks.store (15.5, -10);
|
||||
# incrementalOrderBook.limit ();
|
||||
# assert (equals (incrementalOrderBook, negativeStoredIncremetalOrderBookTarget));
|
||||
# --------------------------------------------------------------------------------------------------------------------
|
||||
# let incrementalIndexedOrderBook = new IncrementalIndexedOrderBook (indexedOrderBookInput);
|
||||
# const limitedIncrementalIndexedOrderBook = new IncrementalIndexedOrderBook (indexedOrderBookInput, 5);
|
||||
# incrementalIndexedOrderBook.limit ();
|
||||
# assert (equals (incrementalIndexedOrderBook, incrementalIndexedOrderBookTarget));
|
||||
# incrementalIndexedOrderBook.limit (5);
|
||||
# limitedIncrementalIndexedOrderBook.limit ();
|
||||
# assert (equals (incrementalIndexedOrderBook, limitedIncrementalIndexedOrderBookTarget));
|
||||
# assert (equals (limitedIncrementalIndexedOrderBook, limitedIncrementalIndexedOrderBookTarget));
|
||||
# incrementalIndexedOrderBook.limit ();
|
||||
# assert (equals (incrementalIndexedOrderBook, incrementalIndexedOrderBookTarget));
|
||||
# bids = incrementalIndexedOrderBook['bids'];
|
||||
# bids.store (5, 0, 'xxyy');
|
||||
# incrementalIndexedOrderBook.limit ();
|
||||
# assert (equals (incrementalIndexedOrderBook, incrementalIndexedOrderBookTarget));
|
||||
# bids.store (10.0, 3, '1234'); # price does match merge size
|
||||
# incrementalIndexedOrderBook.limit ();
|
||||
# assert (equals (incrementalIndexedOrderBook, storedIncrementalIndexedOrderBookTarget));
|
||||
# bids.store (0, 0, '1234');
|
||||
# incrementalIndexedOrderBook.limit ();
|
||||
# assert (equals (incrementalIndexedOrderBook, incrementalIndexedOrderBookDeletedTarget));
|
||||
# incrementalIndexedOrderBook = new IncrementalIndexedOrderBook (indexedOrderBookInput);
|
||||
# bids = incrementalIndexedOrderBook['bids'];
|
||||
# bids.store (10.2, 3, '1234'); # price does not match merge size
|
||||
# incrementalIndexedOrderBook.limit ();
|
||||
# assert (equals (incrementalIndexedOrderBook, anotherStoredIncrementalIndexedOrderBookTarget));
|
||||
# --------------------------------------------------------------------------------------------------------------------
|
||||
reset_book = OrderBook(store_bid)
|
||||
reset_book.limit()
|
||||
reset_book.reset(order_book_input)
|
||||
reset_book.limit()
|
||||
assert equals(reset_book, order_book_target)
|
||||
20
ccxt/pro/test/base/tests_init.py
Normal file
20
ccxt/pro/test/base/tests_init.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
sys.path.append(root)
|
||||
|
||||
from asyncio import run
|
||||
|
||||
from ccxt.pro.test.base.test_order_book import test_ws_order_book # noqa: F401
|
||||
from ccxt.pro.test.base.test_cache import test_ws_cache # noqa: F401
|
||||
# todo : from ccxt.pro.test.base.test_close import test_ws_close # noqa: F401
|
||||
from ccxt.pro.test.base.test_future import test_ws_future # noqa: F401
|
||||
from ccxt.pro.test.base.test_abnormal_close import test_abnormal_close # noqa: F401
|
||||
|
||||
def test_base_init_ws():
|
||||
test_ws_order_book()
|
||||
test_ws_cache()
|
||||
# todo : run(test_ws_close())
|
||||
run(test_ws_future())
|
||||
# run(test_abnormal_close()) stays in infinite loop in travis
|
||||
Reference in New Issue
Block a user