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

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