This commit is contained in:
lz_db
2025-11-16 12:31:03 +08:00
commit 0fab423a18
1451 changed files with 743213 additions and 0 deletions

View 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

View 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()

View 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

View 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

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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)

View 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

View File

View 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())

View 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

View 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())

View 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()

View 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)

View 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