You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

order-book

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

order-book - pypi Package Compare versions

Comparing version
0.6.0
to
0.6.1
+555
tests/test_checksums.py
'''
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com
Please see the LICENSE file for the terms and conditions
associated with this software.
'''
from decimal import Decimal
import pytest
from order_book import OrderBook
def test_minimum_depth_kraken():
ob = OrderBook(max_depth=9, checksum_format='KRAKEN')
with pytest.raises(ValueError):
ob.checksum()
def test_kraken_checksum():
# This checksum is from the kraken docs
ob = OrderBook(max_depth=10, checksum_format='KRAKEN')
asks = [
["0.05005", "0.00000500", "1582905487.684110"],
["0.05010", "0.00000500", "1582905486.187983"],
["0.05015", "0.00000500", "1582905484.480241"],
["0.05020", "0.00000500", "1582905486.645658"],
["0.05025", "0.00000500", "1582905486.859009"],
["0.05030", "0.00000500", "1582905488.601486"],
["0.05035", "0.00000500", "1582905488.357312"],
["0.05040", "0.00000500", "1582905488.785484"],
["0.05045", "0.00000500", "1582905485.302661"],
["0.05050", "0.00000500", "1582905486.157467"]
]
bids = [
["0.05000", "0.00000500", "1582905487.439814"],
["0.04995", "0.00000500", "1582905485.119396"],
["0.04990", "0.00000500", "1582905486.432052"],
["0.04980", "0.00000500", "1582905480.609351"],
["0.04975", "0.00000500", "1582905476.793880"],
["0.04970", "0.00000500", "1582905486.767461"],
["0.04965", "0.00000500", "1582905481.767528"],
["0.04960", "0.00000500", "1582905487.378907"],
["0.04955", "0.00000500", "1582905483.626664"],
["0.04950", "0.00000500", "1582905488.509872"]
]
for a in asks:
ob.asks[Decimal(a[0])] = Decimal(a[1])
for b in bids:
ob.bids[Decimal(b[0])] = Decimal(b[1])
assert ob.checksum() == 974947235
# The following checksums are from recorded data
ob = OrderBook(max_depth=11, checksum_format='KRAKEN')
asks = [
["0.620000", "40.00000000"],
["0.830000", "380.86649128"],
["1.500000", "333.33333333"]
]
bids = [
["0.520300", "3943.09454867"],
["0.403200", "454.31671175"],
["0.403100", "1522.68122054"],
["0.403000", "43.31058726"],
["0.353200", "49.38467346"],
["0.261600", "66.67686034"],
["0.111000", "99.09909910"],
["0.110000", "909.09090909"],
["0.000600", "3333.33333333"],
["0.000400", "5000.00000000"],
["0.000100", "1000000.00000000"]
]
for a in asks:
ob.asks[Decimal(a[0])] = Decimal(a[1])
for b in bids:
ob.bids[Decimal(b[0])] = Decimal(b[1])
assert ob.checksum() == 577149452
ob = OrderBook(max_depth=11, checksum_format='KRAKEN')
asks = [
["0.814900", "297.71298000"],
["0.815000", "500.00000000"],
["0.815100", "500.00399385"],
["0.815200", "42.03000000"],
["0.815300", "21.50000000"],
["0.815400", "10.75000000"],
["0.829900", "1442.34063708"],
["0.830000", "380.86649128"],
["1.500000", "333.33333333"]
]
bids = [
["0.473400", "1284.67569684"],
["0.441000", "40.00415721"],
["0.342200", "51.43191116"],
["0.261600", "66.92596839"],
["0.111000", "99.09909910"],
["0.110000", "909.09090909"],
["0.000600", "3333.33333333"],
["0.000400", "5000.00000000"],
["0.000100", "1000000.00000000"]
]
for a in asks:
ob.asks[Decimal(a[0])] = Decimal(a[1])
for b in bids:
ob.bids[Decimal(b[0])] = Decimal(b[1])
assert ob.checksum() == 2369158246
ob = OrderBook(max_depth=11, checksum_format='KRAKEN')
asks = [
["0.000017680", "38663.54992198"],
["0.000017690", "20623.74841086"],
["0.000017700", "103797.62636430"],
["0.000017710", "40745.97057228"],
["0.000017720", "13296.04740856"],
["0.000017730", "42078.86085768"],
["0.000017740", "64.38065876"],
["0.000017760", "1131.70427847"],
["0.000017780", "43891.46024565"],
["0.000017790", "43908.00000000"],
["0.000017810", "0.00005437"]
]
bids = [
["0.000017670", "0.00000048"], # small volume causes scientific notation
["0.000017660", "50.29884341"],
["0.000017650", "16958.37856622"],
["0.000017640", "16735.08043085"],
["0.000017630", "61895.21671233"],
["0.000017620", "86958.66158205"],
["0.000017610", "8564.64738216"],
["0.000017600", "59539.93801826"],
["0.000017580", "52578.63046424"],
["0.000017570", "46812.60266777"],
["0.000017560", "640.09877588"]
]
for a in asks:
ob.asks[Decimal(a[0])] = Decimal(a[1])
for b in bids:
ob.bids[Decimal(b[0])] = Decimal(b[1])
assert ob.checksum() == 1611253991
ob = OrderBook(checksum_format='KRAKEN')
assert ob.checksum() == 0
def test_okx_checksum():
ob = OrderBook(checksum_format='OKX')
asks = {Decimal("3366.8"): Decimal("9"), Decimal("3368"): Decimal("8"), Decimal("3372"): Decimal("8")}
bids = {Decimal("3366.1"): Decimal("7")}
ob.bids = bids
ob.asks = asks
assert ob.checksum() == 831078360
ob = OrderBook(checksum_format='OKX')
asks = [
["28923.3", "1.87800235"],
["28923.4", "0.04"],
["28924.3", "0.06867412"],
["28925.6", "0.09861686"],
["28925.8", "0.16453656"],
["28925.9", "2.23"],
["28926", "0.32274814"],
["28926.1", "0.6232534"],
["28926.4", "1.04"],
["28926.8", "0.01707781"],
["28928.5", "0.07061975"],
["28929", "0.144"],
["28930.2", "0.1"],
["28930.3", "6.40922866"],
["28931.9", "0.54902396"],
["28932", "0.17306693"],
["28932.7", "2.5672"],
["28932.8", "1.19528238"],
["28932.9", "0.05"],
["28933", "0.34194526"],
["28933.5", "0.02319626"],
["28933.6", "0.764"],
["28933.7", "0.01"],
["28934.4", "0.05"],
["28935", "0.0115"],
["28935.1", "0.48"],
["28935.8", "0.00696447"],
["28935.9", "0.144"],
["28936", "0.12700417"],
["28936.5", "1.58799999"]
]
bids = [
["28923.2", "0.0000001"],
["28923", "0.49101312"],
["28922.4", "0.01251868"],
["28921.2", "0.00020273"],
["28920.9", "0.00009315"],
["28920.3", "0.00008106"],
["28920", "0.03448414"],
["28919.6", "0.00002087"],
["28919.3", "0.00026015"],
["28919", "0.0000519"],
["28917.7", "0.00014609"],
["28916.7", "0.00002008"],
["28915.9", "0.00009415"],
["28915", "0.0454951"],
["28914.6", "0.007"],
["28914.3", "0.12709954"],
["28913.7", "0.0060879"],
["28913.6", "0.00342"],
["28913.3", "0.00574477"],
["28911.8", "0.00106132"],
["28911.1", "0.00011083"],
["28911", "0.00008173"],
["28910", "0.0006918"],
["28909.9", "0.06897"],
["28909.8", "0.00975481"],
["28909.6", "0.00001002"],
["28909", "0.00134995"],
["28908.5", "0.00796815"],
["28908.4", "0.17308796"],
["28908.1", "0.67728285"]
]
for a in asks:
ob.asks[Decimal(a[0])] = Decimal(a[1])
for b in bids:
ob.bids[Decimal(b[0])] = Decimal(b[1])
assert ob.checksum() == 2399502091
ob = OrderBook(checksum_format='OKX')
assert ob.checksum() == 0
def test_bitget_checksum():
ob = OrderBook(checksum_format='BITGET')
asks = [
["0.000000001461", "3422313485"],
["0.000000001462", "174253432795"],
["0.000000001465", "530000463264"],
["0.000000001466", "799507026717"],
["0.000000001467", "458580845176"],
["0.000000001468", "1300000000000"],
["0.000000001470", "29814639074"],
["0.000000001471", "6904159343"],
["0.000000001472", "71139049304"],
["0.000000001473", "5735877536"],
["0.000000001474", "3392130259"],
["0.000000001475", "3389830509"],
["0.000000001476", "81662587311"],
["0.000000001477", "9142169664"],
["0.000000001479", "87063042280"],
["0.000000001480", "7035327151"],
["0.000000001481", "193948520465"],
["0.000000001482", "36857236128"],
["0.000000001486", "3364737551"],
["0.000000001487", "94365814068"],
["0.000000001488", "7047935390"],
["0.000000001489", "3357958362"],
["0.000000001490", "23979201076"],
["0.000000001491", "3353454059"],
["0.000000001492", "3351206435"],
["0.000000001493", "34245570920"],
["0.000000001495", "3344481606"],
["0.000000001496", "3342245990"],
["0.000000001497", "5928111901"],
["0.000000001498", "536314945995"]
]
bids = [
["0.000000001452", "26041231119"],
["0.000000001451", "765679503030"],
["0.000000001450", "24988603767"],
["0.000000001449", "1138718088876"],
["0.000000001447", "12557585692"],
["0.000000001446", "53377804919"],
["0.000000001445", "3460207613"],
["0.000000001444", "499764868141"],
["0.000000001443", "523229389780"],
["0.000000001442", "972284349540"],
["0.000000001441", "3469812631"],
["0.000000001440", "24023644819"],
["0.000000001439", "34783252746"],
["0.000000001438", "3477051461"],
["0.000000001437", "96154818726"],
["0.000000001436", "3481894151"],
["0.000000001435", "436716489370"],
["0.000000001433", "801993699520"],
["0.000000001432", "1242648129244"],
["0.000000001431", "79364670199"],
["0.000000001429", "163983130634"],
["0.000000001428", "68235008969"],
["0.000000001426", "179767647586"],
["0.000000001425", "1300000000000"],
["0.000000001424", "1300000000000"],
["0.000000001423", "75814709870"],
["0.000000001421", "164996453397"],
["0.000000001420", "16901408450"],
["0.000000001419", "58875214144"],
["0.000000001418", "4612402959"]
]
for a in asks:
ob.asks[Decimal(a[0])] = Decimal(a[1])
for b in bids:
ob.bids[Decimal(b[0])] = Decimal(b[1])
assert ob.checksum() == 3720106698
ob = OrderBook(checksum_format='BITGET')
assert ob.checksum() == 0
def test_okcoin_checksum():
ob = OrderBook(checksum_format='OKCOIN')
asks = {Decimal("3366.8"): Decimal("9"), Decimal("3368"): Decimal("8"), Decimal("3372"): Decimal("8")}
bids = {Decimal("3366.1"): Decimal("7")}
ob.bids = bids
ob.asks = asks
assert ob.checksum() == 831078360
def test_ftx_checksum():
ob = OrderBook(checksum_format='FTX')
asks = [
["0.003385", "142011.0"],
["0.003395", "221850.0"],
["0.0034", "120.0"],
["0.003405", "149119.0"],
["0.003415", "3.0"],
["0.003425", "46851.0"],
["0.00343", "162552.0"],
["0.003445", "3.0"],
["0.003455", "182323.0"],
["0.003465", "123.0"],
["0.003475", "171058.0"],
["0.00348", "4.0"],
["0.0035", "3.0"],
["0.003515", "3.0"],
["0.00352", "17.0"],
["0.00353", "120.0"],
["0.003535", "3.0"],
["0.00355", "196293.0"],
["0.003565", "168196.0"],
["0.00357", "4.0"],
["0.003585", "3.0"],
["0.003595", "120.0"],
["0.003605", "3.0"],
["0.00361", "1.0"],
["0.003615", "354598.0"],
["0.00362", "3.0"],
["0.003635", "5.0"],
["0.00364", "181628.0"],
["0.00365", "4.0"],
["0.003655", "3.0"],
["0.00366", "121.0"],
["0.003675", "8.0"],
["0.00368", "202178.0"],
["0.00369", "3.0"],
["0.0037", "179932.0"],
["0.00371", "295577.0"],
["0.003725", "123.0"],
["0.003745", "3.0"],
["0.00375", "1.0"],
["0.00376", "3.0"],
["0.003775", "208662.0"],
["0.00378", "3.0"],
["0.00379", "120.0"],
["0.003795", "3.0"],
["0.0038", "1.0"],
["0.003815", "3.0"],
["0.003825", "16.0"],
["0.00383", "3.0"],
["0.00385", "4.0"],
["0.003855", "120.0"],
["0.003865", "3.0"],
["0.003885", "3.0"],
["0.00389", "1.0"],
["0.0039", "876.0"],
["0.00392", "123.0"],
["0.003935", "3.0"],
["0.00394", "1.0"],
["0.003955", "3.0"],
["0.00397", "3.0"],
["0.00398", "5.0"],
["0.003985", "120.0"],
["0.00399", "4.0"],
["0.004005", "3.0"],
["0.00402", "3.0"],
["0.00403", "3.0"],
["0.00405", "120.0"],
["0.00406", "3.0"],
["0.00409", "3.0"],
["0.004115", "120.0"],
["0.00412", "3.0"],
["0.004135", "5.0"],
["0.00414", "1764.0"],
["0.00415", "3.0"],
["0.00418", "123.0"],
["0.00421", "3.0"],
["0.00424", "3.0"],
["0.004245", "120.0"],
["0.00427", "3.0"],
["0.00429", "16.0"],
["0.0043", "3.0"],
["0.00431", "120.0"],
["0.00433", "3.0"],
["0.00436", "3.0"],
["0.004375", "120.0"],
["0.00439", "3.0"],
["0.00442", "3.0"],
["0.00443", "147145.0"],
["0.00444", "120.0"],
["0.004445", "16.0"],
["0.00445", "3.0"],
["0.00448", "3.0"],
["0.004505", "120.0"],
["0.004515", "3.0"],
["0.004545", "3.0"],
["0.00457", "120.0"],
["0.004575", "3.0"],
["0.004595", "16.0"],
["0.004605", "3.0"],
["0.004635", "123.0"],
["0.004665", "3.0"]
]
bids = [
["0.00336", "3.0"],
["0.00335", "39492.0"],
["0.003345", "3.0"],
["0.00334", "219821.0"],
["0.00333", "17115.0"],
["0.003325", "205654.0"],
["0.003315", "8049.0"],
["0.00331", "602.0"],
["0.00329", "4.0"],
["0.00328", "7.0"],
["0.003275", "509455.0"],
["0.00327", "120.0"],
["0.003265", "148571.0"],
["0.003255", "3.0"],
["0.00324", "4.0"],
["0.003235", "612.0"],
["0.00322", "148150.0"],
["0.003215", "169503.0"],
["0.00321", "16.0"],
["0.003205", "184421.0"],
["0.0032", "1.0"],
["0.003185", "3.0"],
["0.00317", "3.0"],
["0.00315", "8575.0"],
["0.003145", "247853.0"],
["0.003135", "3.0"],
["0.00312", "3060.0"],
["0.003115", "186021.0"],
["0.00311", "1.0"],
["0.003105", "229810.0"],
["0.0031", "3.0"],
["0.003095", "206811.0"],
["0.00308", "3.0"],
["0.003065", "3.0"],
["0.003055", "16.0"],
["0.003045", "3.0"],
["0.00303", "3.0"],
["0.00301", "288639.0"],
["0.003", "272946.0"],
["0.002995", "3.0"],
["0.002975", "212459.0"],
["0.002965", "244636.0"],
["0.00296", "3.0"],
["0.00294", "3.0"],
["0.002925", "3.0"],
["0.002905", "19.0"],
["0.00289", "3.0"],
["0.00287", "3.0"],
["0.002855", "3.0"],
["0.00284", "3.0"],
["0.00282", "3.0"],
["0.002805", "3.0"],
["0.002785", "3.0"],
["0.00277", "3.0"],
["0.00275", "3.0"],
["0.002735", "3.0"],
["0.002715", "3.0"],
["0.0027", "3.0"],
["0.00268", "3.0"],
["0.002665", "3.0"],
["0.002645", "3.0"],
["0.00263", "3.0"],
["0.00261", "3.0"],
["0.002595", "3.0"],
["0.002575", "3.0"],
["0.00256", "3.0"],
["0.002555", "147658.0"],
["0.00254", "3.0"],
["0.002525", "3.0"],
["0.002505", "3.0"],
["0.00249", "3.0"],
["0.00247", "3.0"],
["0.002455", "3.0"],
["0.002435", "3.0"],
["0.00242", "3.0"],
["0.0024", "3.0"],
["0.002385", "3.0"],
["0.002365", "3.0"],
["0.00235", "3.0"],
["0.00233", "3.0"],
["0.002315", "3.0"],
["0.0023", "3.0"],
["0.002035", "3440.0"],
["0.0015", "1333.0"],
["0.0012", "5010.0"],
["0.00033", "303030.0"],
["0.00003", "371115.0"]
]
for a in asks:
ob.asks[Decimal(a[0])] = Decimal(a[1])
for b in bids:
ob.bids[Decimal(b[0])] = Decimal(b[1])
assert ob.checksum() == 3169411573
ob = OrderBook(checksum_format='FTX')
assert ob.checksum() == 0
'''
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com
Please see the LICENSE file for the terms and conditions
associated with this software.
'''
from decimal import Decimal
import random
import pytest
import requests
from order_book import OrderBook
def populate_orderbook():
ob = OrderBook()
data = requests.get("https://api.pro.coinbase.com/products/BTC-USD/book?level=2").json()
for side, d in data.items():
if side == 'bids':
for price, size, _ in d:
ob.bids[Decimal(price)] = size
elif side == 'asks':
for price, size, _ in d:
ob.asks[Decimal(price)] = size
assert ob.bids.index(0)[0] < ob.asks.index(0)[0]
assert ob.bids.index(-1)[0] < ob.asks.index(-1)[0]
assert ob.bids.index(-1)[0] < ob.bids.index(0)[0]
assert ob.asks.index(-1)[0] > ob.asks.index(0)[0]
return ob
def test_orderbook():
populate_orderbook()
def test_to_dict():
ob = populate_orderbook()
data = ob.to_dict()
assert list(data['bid'].keys()) == list(ob.bids.keys())
assert list(data['ask'].keys()) == list(ob.asks.keys())
def test_orderbook_getitem():
ob = OrderBook()
data = requests.get("https://api.pro.coinbase.com/products/BTC-USD/book?level=2").json()
for side, d in data.items():
if side in {'bids', 'asks'}:
for price, size, _ in d:
ob[side][Decimal(price)] = size
assert ob.bids.index(0)[0] < ob.asks.index(0)[0]
assert ob.bids.index(-1)[0] < ob.asks.index(-1)[0]
assert ob.bids.index(-1)[0] < ob.bids.index(0)[0]
assert ob.asks.index(-1)[0] > ob.asks.index(0)[0]
with pytest.raises(KeyError):
# legal keys are BID, bid, BIDS, bids, ASK, ask, ASKS, asks
ob['invalid'][1] = 3
def test_orderbook_init_errors():
with pytest.raises(TypeError):
OrderBook('a')
with pytest.raises(TypeError):
OrderBook(blah=3)
with pytest.raises(TypeError):
OrderBook(max_depth='a')
def test_orderbook_init():
ob = OrderBook(max_depth=10)
ob.bids = {val: val for val in range(20)}
ob.asks = {val: val for val in range(10, 30)}
assert len(ob) == 20
def test_orderbook_len():
random.seed()
bids = []
asks = []
for _ in range(500):
bids.append(random.uniform(0.0, 100000.0))
bids = list(set(bids))
for _ in range(500):
asks.append(random.uniform(0.0, 100000.0))
asks = list(set(asks))
ob = OrderBook()
for b in bids:
ob['BIDS'][b] = str(b)
for a in asks:
ob['ASKS'][a] = str(a)
assert len(ob) == len(asks) + len(bids)
def test_orderbook_keys():
ob = OrderBook()
ob['bids'][1] = 1
ob['BIDS'][1] = 2
ob['bid'][1] = 3
ob['BID'][1] = 4
assert ob.bids.to_dict() == {1: 4}
assert ob.bid.to_dict() == {1: 4}
ob['asks'][1] = 1
ob['ASKS'][1] = 2
ob['ask'][1] = 3
ob['ASK'][1] = 4
assert ob.asks.to_dict() == {1: 4}
assert ob.ask.to_dict() == {1: 4}
def test_orderbook_setitem():
ob = OrderBook()
data = requests.get("https://api.pro.coinbase.com/products/BTC-USD/book?level=2").json()
ob.bids = {Decimal(price): size for price, size, _ in data['bids']}
ob.asks = {Decimal(price): size for price, size, _ in data['asks']}
assert ob.bids.index(0)[0] < ob.asks.index(0)[0]
assert ob.bids.index(-1)[0] < ob.asks.index(-1)[0]
assert ob.bids.index(-1)[0] < ob.bids.index(0)[0]
assert ob.asks.index(-1)[0] > ob.asks.index(0)[0]
def test_orderbook_getitem_invalid():
ob = OrderBook()
with pytest.raises(ValueError):
ob[1][1] = 'a'
def test_orderbook_setitem_invalid():
ob = OrderBook()
with pytest.raises(ValueError):
ob[123] = {}
with pytest.raises(ValueError):
ob['invalid'] = {}
with pytest.raises(ValueError):
del ob['bids']
with pytest.raises(ValueError):
ob['bids'] = 'a'
def test_checksum_raises():
with pytest.raises(ValueError):
ob = OrderBook()
ob.checksum()
def test_l3_orderbook():
ob = OrderBook()
raw_dict = {}
data = requests.get("https://api.pro.coinbase.com/products/BTC-USD/book?level=3").json()
for side, d in data.items():
if side in {'bids', 'asks'}:
raw_dict[side] = {}
for price, size, orderid in d:
p = Decimal(price)
if p in ob[side]:
ob[side][p][orderid] = size
raw_dict[side][p][orderid] = size
assert p in ob[side]
assert len(ob[side][p]) > 1
else:
ob[side][p] = {orderid: size}
raw_dict[side][p] = {orderid: size}
assert p in ob[side]
assert len(ob[side][p]) == 1
assert ob.bids.index(0)[0] < ob.asks.index(0)[0]
assert ob.bids.index(-1)[0] < ob.asks.index(-1)[0]
assert ob.bids.index(-1)[0] < ob.bids.index(0)[0]
assert ob.asks.index(-1)[0] > ob.asks.index(0)[0]
ob_dict = ob.to_dict()
for side in raw_dict:
assert raw_dict[side] == ob[side].to_dict() == ob_dict[side[:-1]]
# test to_dict with type conversion
def convert(x):
if isinstance(x, Decimal):
return float(x)
if isinstance(x, dict):
return {k: float(v) for k, v in x.items()}
print("DOING")
ob_dict = ob.to_dict(to_type=convert)
for price in ob_dict['bid']:
assert isinstance(price, float)
for value in list(ob_dict['bid'][price].values()):
assert isinstance(value, float)
def test_del():
ob = OrderBook()
ob.bids = {1: 1, 2: 2}
ob.asks = {5: 5, 6: 6, 7: 7}
assert len(ob.bids) == 2
del ob.bids[1]
assert 1 not in ob.bids
assert 2 in ob.bids
assert len(ob.bids) == 1
assert ob.bids.to_dict() == {2: 2}
def test_to_dict_types():
input = {
'1.1': 2,
'3.3': 4,
'5.5': 6,
'7.7': 8
}
ob = OrderBook()
ob.bids = input
ob.asks = input
data = ob.to_dict(from_type=str, to_type=float)
assert data['bid'] == {1.1: 2, 3.3: 4, 5.5: 6, 7.7: 8}
assert data['ask'] == {1.1: 2, 3.3: 4, 5.5: 6, 7.7: 8}
'''
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com
Please see the LICENSE file for the terms and conditions
associated with this software.
'''
from decimal import Decimal
import random
import pytest
from order_book import SortedDict
def test_ascending():
s = SortedDict(ordering='ASC')
s[3] = "a"
s[2] = "b"
s[1] = "c"
assert s.keys() == (1, 2, 3)
def test_descending():
s = SortedDict(ordering='DESC')
s[1] = "a"
s[3] = "b"
s[2] = "c"
assert s.keys() == (3, 2, 1)
def test_iteration():
expected = (3, 2, 1)
index = None
s = SortedDict(ordering='DESC')
s[1] = "a"
s[3] = "b"
s[2] = "c"
for index, key in enumerate(s):
assert key == expected[index]
assert index == 2
def test_index():
s = SortedDict(ordering='DESC')
s[1] = "a"
s[3] = "b"
s[2] = "c"
assert s.index(0) == (3, "b")
assert s.index(1) == (2, "c")
assert s.index(2) == (1, "a")
assert s.index(-1) == (1, 'a')
assert s.index(-2) == (2, 'c')
assert s.index(-3) == (3, 'b')
with pytest.raises(IndexError):
assert s.index(3) == (2, "c")
with pytest.raises(IndexError):
assert s.index(4) == (2, "c")
def test_decimal():
s = SortedDict(ordering='DESC')
s[Decimal('1.2')] = "a"
s[Decimal('1.5')] = "b"
s[Decimal('1.6')] = "c"
s[Decimal('1.7')] = "d"
assert len(s) == 4
assert s.keys() == (Decimal('1.7'), Decimal('1.6'), Decimal('1.5'), Decimal('1.2'))
def test_random_data():
random.seed()
values = []
asc = SortedDict(ordering='ASC')
desc = SortedDict(ordering='DESC')
for _ in range(2000):
values.append(random.uniform(0.0, 100000.0))
values = set(values)
for v in values:
asc[v] = str(v)
desc[v] = str(v)
previous = None
for key in asc:
assert key in values
assert str(key) == asc[key]
if previous:
assert previous < key
previous = key
previous = None
for key in desc:
assert key in values
assert str(key) == desc[key]
if previous:
assert previous > key
previous = key
def test_to_dict():
random.seed()
values = []
asc = SortedDict(ordering='ASC')
desc = SortedDict(ordering='DESC')
for _ in range(2000):
values.append(random.uniform(0.0, 100000.0))
values = set(values)
for v in values:
asc[v] = str(v)
desc[v] = str(v)
d = asc.to_dict()
assert list(d.keys()) == list(asc.keys())
assert sorted(d.keys()) == list(d.keys())
previous = None
for key, _ in d.items():
assert d[key] == asc[key]
if previous:
d[key] > previous
previous = d[key]
d = desc.to_dict()
assert list(d.keys()) == list(desc.keys())
assert list(reversed(sorted(d.keys()))) == list(d.keys())
previous = None
for key, val in d.items():
assert d[key] == desc[key]
if previous:
d[key] < previous
previous = d[key]
def test_to_list():
random.seed()
values = []
asc = SortedDict(ordering='ASC')
desc = SortedDict(ordering='DESC')
for _ in range(2000):
values.append(random.uniform(0.0, 100000.0))
values = set(values)
for v in values:
asc[v] = str(v)
desc[v] = str(v)
lst = asc.to_list()
_keys = list(list(zip(*lst))[0])
assert _keys == list(asc.keys())
assert sorted(_keys) == _keys
previous = None
for key, val in lst:
assert val == asc[key]
val = float(val)
if previous:
assert val > previous
previous = val
lst = desc.to_list()
_keys = list(list(zip(*lst))[0])
assert _keys == list(desc.keys())
assert list(reversed(sorted(_keys))) == _keys
previous = None
for key, val in lst:
assert val == desc[key]
val = float(val)
if previous:
assert val < previous
previous = val
def test_init_from_dict():
with pytest.raises(TypeError):
asc = SortedDict("a", ordering='ASC')
with pytest.raises(TypeError):
asc = SortedDict({}, {}, ordering='ASC')
asc = SortedDict({4: 'a', 1: 'c', 3: 'f', 6: 'j', 9: 'z', 2: 'p'}, ordering='ASC')
assert asc.to_dict() == {1: 'c', 2: 'p', 3: 'f', 4: 'a', 6: 'j', 9: 'z'}
assert list(asc.keys()) == [1, 2, 3, 4, 6, 9]
desc = SortedDict({4: 'a', 1: 'c', 3: 'f', 6: 'j', 9: 'z', 2: 'p'}, ordering='DESC')
assert desc.to_dict() == {1: 'c', 2: 'p', 3: 'f', 4: 'a', 6: 'j', 9: 'z'}
assert list(desc.keys()) == [9, 6, 4, 3, 2, 1]
def test_invalid_ordering():
with pytest.raises(ValueError):
SortedDict(ordering='D')
with pytest.raises(ValueError):
SortedDict(ordering=1)
def test_default_ordering():
# default ordering is ascending
d = SortedDict()
d[3] = 'a'
d[2] = 'b'
d[1] = 'c'
assert list(d.keys()) == [1, 2, 3]
def test_illegal_index():
d = SortedDict()
with pytest.raises(IndexError):
d.index(0)
with pytest.raises(TypeError):
d.index('a')
with pytest.raises(TypeError):
d.index()
def test_empty_keys():
d = SortedDict()
assert d.keys() == ()
def test_keys_reference_counting():
d = SortedDict()
d[1] = 'a'
assert d.keys() == (1,)
d[2] = 'b'
assert d.keys() == (1, 2)
def test_invalid_key():
d = SortedDict()
with pytest.raises(KeyError):
d[1]
with pytest.raises(KeyError):
del d[1]
def test_del():
d = SortedDict()
d[3] = 'a'
d[2] = 'b'
d[1] = 'c'
del d[2]
assert d.keys() == (1, 3)
def test_iteration_noop():
d = SortedDict()
counter = 0
for _ in d:
counter += 1
assert counter == 0
def test_invalid_depth():
with pytest.raises(ValueError):
SortedDict(max_depth=-1)
with pytest.raises(ValueError):
SortedDict(max_depth='A')
def test_invalid_truncate():
with pytest.raises(ValueError):
SortedDict(truncate=10)
def test_depth_members():
d = SortedDict(max_depth=10, truncate=True)
assert d.__max_depth == 10
assert d.__truncate == 1
e = SortedDict(max_depth=100, truncate=False)
assert e.__max_depth == 100
assert e.__truncate == 0
f = SortedDict()
assert f.__max_depth == 0
assert f.__truncate == 0
def test_depth():
d = SortedDict({i: i for i in range(100)}, max_depth=10)
assert d.keys() == (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
assert len(d) == 10
assert len(d.to_dict()) == 10
assert len(d.to_list()) == 10
def test_depth_nontruncated():
d = SortedDict({i: i for i in range(100)}, max_depth=10)
del d[5]
assert d.keys() == (0, 1, 2, 3, 4, 6, 7, 8, 9, 10)
def test_depth_truncated():
d = SortedDict({i: i for i in range(100)}, max_depth=10)
d.truncate()
del d[5]
assert d.keys() == (0, 1, 2, 3, 4, 6, 7, 8, 9)
def test_depth_auto_truncate():
d = SortedDict({i: i for i in range(100)}, truncate=True, max_depth=10)
del d[5]
assert d.keys() == (0, 1, 2, 3, 4, 6, 7, 8, 9)
d[1.1] = 0
d[1.2] = 0
d[1.3] = 0
assert d.keys() == (0, 1, 1.1, 1.2, 1.3, 2, 3, 4, 6, 7)
def test_to_dict_types():
input = {
1: 2,
3.3: 4,
Decimal('5.6'): 7.8,
9: 11.11,
Decimal('1.3'): Decimal('3.3'),
77.8: Decimal('19.9')
}
d = SortedDict(input)
assert d.to_dict() == input
assert d.to_dict(to_type=str) == {'1': '2', '3.3': '4', '5.6': '7.8', '9': '11.11', '1.3': '3.3', '77.8': '19.9'}
assert d.to_dict(from_type=str, to_type=float) == input
assert d.to_dict(to_type=float, from_type=Decimal) == {1: 2, 3.3: 4, 5.6: 7.8, 9: 11.11, 1.3: 3.3, 77.8: 19.9}
+4
-0
## Changelog
### 0.6.1 (2024-04-22)
* Update: to_list's behavior matches that of to_dict (respects max_depth, if set).
* Update: resolve build warnings on some compilers.
### 0.6.0 (2022-10-19)

@@ -4,0 +8,0 @@ * Update: Drop support for python 3.7

+16
-6
Metadata-Version: 2.1
Name: order-book
Version: 0.6.0
Version: 0.6.1
Summary: A fast orderbook implementation, in C, for Python

@@ -10,3 +10,2 @@ Home-page: https://github.com/bmoscon/orderbook

Keywords: market data,trading
Platform: UNKNOWN
Classifier: Intended Audience :: Developers

@@ -16,5 +15,6 @@ Classifier: Development Status :: 3 - Alpha

Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only

@@ -92,2 +92,10 @@ Classifier: Programming Language :: Python :: Implementation :: CPython

# Data can also be exported as an ordered list
# .to_list() returns a list of (price, size) tuples
print("Top 5 Asks")
print(ob.asks.to_list()[:5])
print("\nTop 5 Bids")
print(ob.bids.to_list()[:5])
```

@@ -150,2 +158,6 @@

### 0.6.1 (2024-04-22)
* Update: to_list's behavior matches that of to_dict (respects max_depth, if set).
* Update: resolve build warnings on some compilers.
### 0.6.0 (2022-10-19)

@@ -224,3 +236,1 @@ * Update: Drop support for python 3.7

* Initial Release

@@ -15,2 +15,5 @@ CHANGES.md

orderbook/utils.c
orderbook/utils.h
orderbook/utils.h
tests/test_checksums.py
tests/test_orderbook.py
tests/test_sorteddict.py
/*
Copyright (C) 2020-2022 Bryant Moscon - bmoscon@gmail.com
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com

@@ -543,3 +543,3 @@ Please see the LICENSE file for the terms and conditions

// default 'str' formatting is wrong when the value is in scientific notation
if (EXPECT(memchr(&data[startpos], (char) 'E', *pos - startpos), 0)) {
if (EXPECT((long)memchr(&data[startpos], (char) 'E', *pos - startpos), (long)0)) {
*pos = startpos;

@@ -563,3 +563,3 @@ if (EXPECT(formatf_string_builder(pydata, data, pos), 0)) {

// default 'str' formatting is wrong when the value is less than 0.0001 or in scientific notation
if (EXPECT(!strncmp(&data[startpos], "0.0000", 6) || memchr(&data[startpos], (char) 'E', *pos - startpos), 0)) {
if (EXPECT(!strncmp((const char *)&data[startpos], "0.0000", 6) || memchr(&data[startpos], (char) 'E', *pos - startpos), 0)) {
*pos = startpos;

@@ -566,0 +566,0 @@ if (EXPECT(floatstr_string_builder(pydata, data, pos), 0)) {

/*
Copyright (C) 2020-2022 Bryant Moscon - bmoscon@gmail.com
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com

@@ -124,3 +124,3 @@ Please see the LICENSE file for the terms and conditions

.m_clear = order_book_clear,
.m_free = order_book_free,
.m_free = (void *)order_book_free,
};

@@ -127,0 +127,0 @@

/*
Copyright (C) 2020-2022 Bryant Moscon - bmoscon@gmail.com
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com

@@ -37,3 +37,3 @@ Please see the LICENSE file for the terms and conditions

self->ordering = INVALID_ORDERING;
self->ordering = INVALID_ORDERING;
// -1 means uninitalized

@@ -343,15 +343,8 @@ self->iterator_index = -1;

PyObject* SortedDict_tolist(SortedDict *self, PyObject *args, PyObject *kwargs)
PyObject* SortedDict_tolist(SortedDict *self, PyObject *Py_UNUSED(ignored))
{
static char* keywords[] = {"n_levels", NULL};
int n_levels = -1;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", keywords, &n_levels)) {
return NULL;
}
int data_len = PyDict_Size(self->data);
// Set n_levels to data_len if n_levels is the default value
if (n_levels == -1 | n_levels > data_len) {
n_levels = data_len;
int len = PyDict_Size(self->data);
if ((self->depth > 0) && (self->depth < len)) {
len = self->depth;
}

@@ -367,3 +360,3 @@

PyObject *ret = PyList_New(n_levels);
PyObject *ret = PyList_New(len);
if (EXPECT(!ret, 0)) {

@@ -373,3 +366,3 @@ return NULL;

for (int i = 0; i < n_levels; ++i) {
for (int i = 0; i < len; ++i) {
// new reference

@@ -505,3 +498,3 @@ PyObject *key = PySequence_GetItem(self->keys, i);

self->iterator_index = -1;
return self;
return (PyObject *)self;
}

@@ -508,0 +501,0 @@

/*
Copyright (C) 2020-2022 Bryant Moscon - bmoscon@gmail.com
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com

@@ -45,3 +45,3 @@ Please see the LICENSE file for the terms and conditions

PyObject* SortedDict_todict(SortedDict *self, PyObject *unused, PyObject *kwargs);
PyObject* SortedDict_tolist(SortedDict *self, PyObject *args, PyObject *kwargs);
PyObject* SortedDict_tolist(SortedDict *self, PyObject *Py_UNUSED(ignored));
PyObject* SortedDict_truncate(SortedDict *self, PyObject *Py_UNUSED(ignored));

@@ -74,3 +74,3 @@

{"to_dict", (PyCFunction) SortedDict_todict, METH_VARARGS | METH_KEYWORDS, "return a python dictionary, sorted by keys"},
{"to_list", (PyCFunction) SortedDict_tolist, METH_VARARGS | METH_KEYWORDS, "return a list of key, value tuple with length specified in input argument. if no argument is passed or the argument is larger than length of self->data, return items with length of self->data."},
{"to_list", (PyCFunction) SortedDict_tolist, METH_NOARGS, "return a list of key, value tuples."},
{NULL}

@@ -77,0 +77,0 @@ };

/*
Copyright (C) 2020-2022 Bryant Moscon - bmoscon@gmail.com
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com

@@ -4,0 +4,0 @@ Please see the LICENSE file for the terms and conditions

/*
Copyright (C) 2020-2022 Bryant Moscon - bmoscon@gmail.com
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com

@@ -4,0 +4,0 @@ Please see the LICENSE file for the terms and conditions

Metadata-Version: 2.1
Name: order_book
Version: 0.6.0
Version: 0.6.1
Summary: A fast orderbook implementation, in C, for Python

@@ -10,3 +10,2 @@ Home-page: https://github.com/bmoscon/orderbook

Keywords: market data,trading
Platform: UNKNOWN
Classifier: Intended Audience :: Developers

@@ -16,5 +15,6 @@ Classifier: Development Status :: 3 - Alpha

Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only

@@ -92,2 +92,10 @@ Classifier: Programming Language :: Python :: Implementation :: CPython

# Data can also be exported as an ordered list
# .to_list() returns a list of (price, size) tuples
print("Top 5 Asks")
print(ob.asks.to_list()[:5])
print("\nTop 5 Bids")
print(ob.bids.to_list()[:5])
```

@@ -150,2 +158,6 @@

### 0.6.1 (2024-04-22)
* Update: to_list's behavior matches that of to_dict (respects max_depth, if set).
* Update: resolve build warnings on some compilers.
### 0.6.0 (2022-10-19)

@@ -224,3 +236,1 @@ * Update: Drop support for python 3.7

* Initial Release

@@ -65,2 +65,10 @@ # Orderbook

# Data can also be exported as an ordered list
# .to_list() returns a list of (price, size) tuples
print("Top 5 Asks")
print(ob.asks.to_list()[:5])
print("\nTop 5 Bids")
print(ob.bids.to_list()[:5])
```

@@ -67,0 +75,0 @@

'''
Copyright (C) 2020-2022 Bryant Moscon - bmoscon@gmail.com
Copyright (C) 2020-2024 Bryant Moscon - bmoscon@gmail.com

@@ -38,3 +38,3 @@ Please see the LICENSE file for the terms and conditions

name='order_book',
version='0.6.0',
version='0.6.1',
author="Bryant Moscon",

@@ -57,5 +57,6 @@ author_email="bmoscon@gmail.com",

"Programming Language :: Python",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: Only",

@@ -62,0 +63,0 @@ "Programming Language :: Python :: Implementation :: CPython",