order-book
Advanced tools
+4
-0
| ## Changelog | ||
| ### 0.4.0 (2021-09-16) | ||
| * Feature: changes to code and setup.py to enable compiling on windows | ||
| * Feature: add from_type/to_type kwargs to the to_dict methods, allowing for type conversion when creating the dictionary | ||
| ### 0.3.2 (2021-09-04) | ||
@@ -4,0 +8,0 @@ * Bugfix: depth was incorrectly ignored when converting sorteddict to python dict |
| Metadata-Version: 2.1 | ||
| Name: order-book | ||
| Version: 0.3.2 | ||
| Version: 0.4.0 | ||
| Summary: A fast orderbook implementation, in C, for Python | ||
@@ -130,2 +130,6 @@ Home-page: https://github.com/bmoscon/orderbook | ||
| ### 0.4.0 (2021-09-16) | ||
| * Feature: changes to code and setup.py to enable compiling on windows | ||
| * Feature: add from_type/to_type kwargs to the to_dict methods, allowing for type conversion when creating the dictionary | ||
| ### 0.3.2 (2021-09-04) | ||
@@ -132,0 +136,0 @@ * Bugfix: depth was incorrectly ignored when converting sorteddict to python dict |
+36
-36
@@ -110,11 +110,11 @@ /* | ||
| /* Orderbook methods */ | ||
| PyObject* Orderbook_todict(const Orderbook *self, PyObject *Py_UNUSED(ignored)) | ||
| PyObject* Orderbook_todict(const Orderbook *self, PyObject *unused, PyObject *kwargs) | ||
| { | ||
| PyObject *ret = PyDict_New(); | ||
| if (__builtin_expect(!ret, 0)) { | ||
| if (EXPECT(!ret, 0)) { | ||
| return NULL; | ||
| } | ||
| PyObject *bids = SortedDict_todict(self->bids, NULL); | ||
| if (__builtin_expect(!bids, 0)) { | ||
| PyObject *bids = SortedDict_todict(self->bids, unused, kwargs); | ||
| if (EXPECT(!bids, 0)) { | ||
| Py_DECREF(ret); | ||
@@ -124,4 +124,4 @@ return NULL; | ||
| PyObject *asks = SortedDict_todict(self->asks, NULL); | ||
| if (__builtin_expect(!asks, 0)) { | ||
| PyObject *asks = SortedDict_todict(self->asks, unused, kwargs); | ||
| if (EXPECT(!asks, 0)) { | ||
| Py_DECREF(bids); | ||
@@ -132,3 +132,3 @@ Py_DECREF(ret); | ||
| if (__builtin_expect(PyDict_SetItemString(ret, "bid", bids) < 0, 0)) { | ||
| if (EXPECT(PyDict_SetItemString(ret, "bid", bids) < 0, 0)) { | ||
| Py_DECREF(asks); | ||
@@ -140,3 +140,3 @@ Py_DECREF(bids); | ||
| if (__builtin_expect(PyDict_SetItemString(ret, "ask", asks) < 0, 0)) { | ||
| if (EXPECT(PyDict_SetItemString(ret, "ask", asks) < 0, 0)) { | ||
| Py_DECREF(asks); | ||
@@ -156,3 +156,3 @@ Py_DECREF(bids); | ||
| { | ||
| if (__builtin_expect(self->checksum == INVALID_CHECKSUM_FORMAT, 0)) { | ||
| if (EXPECT(self->checksum == INVALID_CHECKSUM_FORMAT, 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "no checksum format specified"); | ||
@@ -162,7 +162,7 @@ return NULL; | ||
| if (__builtin_expect(update_keys(self->bids), 0)) { | ||
| if (EXPECT(update_keys(self->bids), 0)) { | ||
| return NULL; | ||
| } | ||
| if (__builtin_expect(update_keys(self->asks), 0)) { | ||
| if (EXPECT(update_keys(self->asks), 0)) { | ||
| return NULL; | ||
@@ -186,3 +186,3 @@ } | ||
| { | ||
| if (__builtin_expect(!PyUnicode_Check(key), 0)) { | ||
| if (EXPECT(!PyUnicode_Check(key), 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "key must one of bid/ask"); | ||
@@ -193,3 +193,3 @@ return NULL; | ||
| PyObject *str = PyUnicode_AsEncodedString(key, "UTF-8", "strict"); | ||
| if (__builtin_expect(!str, 0)) { | ||
| if (EXPECT(!str, 0)) { | ||
| return NULL; | ||
@@ -217,3 +217,3 @@ } | ||
| { | ||
| if (__builtin_expect(!PyUnicode_Check(key), 0)) { | ||
| if (EXPECT(!PyUnicode_Check(key), 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "key must one of bid/ask"); | ||
@@ -224,3 +224,3 @@ return -1; | ||
| PyObject *str = PyUnicode_AsEncodedString(key, "UTF-8", "strict"); | ||
| if (__builtin_expect(!str, 0)) { | ||
| if (EXPECT(!str, 0)) { | ||
| return -1; | ||
@@ -232,3 +232,3 @@ } | ||
| if (__builtin_expect(key_int == INVALID_SIDE, 0)) { | ||
| if (EXPECT(key_int == INVALID_SIDE, 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "key must one of bid/ask"); | ||
@@ -238,3 +238,3 @@ return -1; | ||
| if (__builtin_expect(!value, 0)) { | ||
| if (EXPECT(!value, 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "cannot delete"); | ||
@@ -244,3 +244,3 @@ return -1; | ||
| if (__builtin_expect(!PyDict_Check(value), 0)) { | ||
| if (EXPECT(!PyDict_Check(value), 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "value must be a dict"); | ||
@@ -251,3 +251,3 @@ return -1; | ||
| PyObject *copy = PyDict_Copy(value); | ||
| if (__builtin_expect(!copy, 0)) { | ||
| if (EXPECT(!copy, 0)) { | ||
| return -1; | ||
@@ -308,3 +308,3 @@ } | ||
| PyObject *repr = PyObject_Str(pydata); | ||
| if (__builtin_expect(!repr, 0)) { | ||
| if (EXPECT(!repr, 0)) { | ||
| return -1; | ||
@@ -315,3 +315,3 @@ } | ||
| Py_DECREF(repr); | ||
| if (__builtin_expect(!str, 0)) { | ||
| if (EXPECT(!str, 0)) { | ||
| return -1; | ||
@@ -321,3 +321,3 @@ } | ||
| const char *string = PyBytes_AS_STRING(str); | ||
| if (__builtin_expect(!string, 0)) { | ||
| if (EXPECT(!string, 0)) { | ||
| Py_DECREF(str); | ||
@@ -354,7 +354,7 @@ return -1; | ||
| if (__builtin_expect(kraken_string_builder(price, data, pos), 0)) { | ||
| if (EXPECT(kraken_string_builder(price, data, pos), 0)) { | ||
| return -1; | ||
| } | ||
| if (__builtin_expect(kraken_string_builder(size, data, pos), 0)) { | ||
| if (EXPECT(kraken_string_builder(size, data, pos), 0)) { | ||
| return -1; | ||
@@ -370,3 +370,3 @@ } | ||
| { | ||
| if (__builtin_expect(ob->max_depth && ob->max_depth < 10, 0)) { | ||
| if (EXPECT(ob->max_depth && ob->max_depth < 10, 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "Max depth is less than minimum number of levels for Kraken checksum"); | ||
@@ -379,3 +379,3 @@ return NULL; | ||
| if (__builtin_expect(bids_size < 10 || asks_size < 10, 0)) { | ||
| if (EXPECT(bids_size < 10 || asks_size < 10, 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "Depth is less than minimum number of levels for Kraken checksum"); | ||
@@ -386,7 +386,7 @@ return NULL; | ||
| int pos = 0; | ||
| if (__builtin_expect(kraken_populate_side(ob->asks, ob->checksum_buffer, &pos), 0)) { | ||
| if (EXPECT(kraken_populate_side(ob->asks, ob->checksum_buffer, &pos), 0)) { | ||
| return NULL; | ||
| } | ||
| if (__builtin_expect(kraken_populate_side(ob->bids, ob->checksum_buffer, &pos), 0)) { | ||
| if (EXPECT(kraken_populate_side(ob->bids, ob->checksum_buffer, &pos), 0)) { | ||
| return NULL; | ||
@@ -403,3 +403,3 @@ } | ||
| PyObject *repr = PyObject_Str(pydata); | ||
| if (__builtin_expect(!repr, 0)) { | ||
| if (EXPECT(!repr, 0)) { | ||
| return -1; | ||
@@ -410,3 +410,3 @@ } | ||
| Py_DECREF(repr); | ||
| if (__builtin_expect(!str, 0)) { | ||
| if (EXPECT(!str, 0)) { | ||
| return -1; | ||
@@ -416,3 +416,3 @@ } | ||
| const char *string = PyBytes_AS_STRING(str); | ||
| if (__builtin_expect(!string, 0)) { | ||
| if (EXPECT(!string, 0)) { | ||
| Py_DECREF(str); | ||
@@ -434,3 +434,3 @@ return -1; | ||
| { | ||
| if (__builtin_expect(ob->max_depth && ob->max_depth < depth, 0)) { | ||
| if (EXPECT(ob->max_depth && ob->max_depth < depth, 0)) { | ||
| PyErr_SetString(PyExc_ValueError, "Max depth is less than minimum number of levels for checksum"); | ||
@@ -451,7 +451,7 @@ return NULL; | ||
| if (__builtin_expect(ftx_string_builder(price, ob->checksum_buffer, &pos), 0)) { | ||
| if (EXPECT(ftx_string_builder(price, ob->checksum_buffer, &pos), 0)) { | ||
| return NULL; | ||
| } | ||
| if (__builtin_expect(ftx_string_builder(size, ob->checksum_buffer, &pos), 0)) { | ||
| if (EXPECT(ftx_string_builder(size, ob->checksum_buffer, &pos), 0)) { | ||
| return NULL; | ||
@@ -465,7 +465,7 @@ } | ||
| if (__builtin_expect(ftx_string_builder(price, ob->checksum_buffer, &pos), 0)) { | ||
| if (EXPECT(ftx_string_builder(price, ob->checksum_buffer, &pos), 0)) { | ||
| return NULL; | ||
| } | ||
| if (__builtin_expect(ftx_string_builder(size, ob->checksum_buffer, &pos), 0)) { | ||
| if (EXPECT(ftx_string_builder(size, ob->checksum_buffer, &pos), 0)) { | ||
| return NULL; | ||
@@ -472,0 +472,0 @@ } |
@@ -44,3 +44,3 @@ /* | ||
| PyObject* Orderbook_todict(const Orderbook *self, PyObject *Py_UNUSED(ignored)); | ||
| PyObject* Orderbook_todict(const Orderbook *self, PyObject *unused, PyObject *kwargs); | ||
| PyObject* Orderbook_checksum(const Orderbook *self, PyObject *Py_UNUSED(ignored)); | ||
@@ -62,3 +62,3 @@ | ||
| {"ask", T_OBJECT_EX, offsetof(Orderbook, asks), 0, "asks"}, | ||
| {"max_depth", T_INT, offsetof(Orderbook, max_depth), READONLY, "Maximum book depth"}, | ||
| {"max_depth", T_INT, offsetof(Orderbook, max_depth), READONLY, "maximum book depth"}, | ||
| {NULL} | ||
@@ -70,4 +70,4 @@ }; | ||
| static PyMethodDef Orderbook_methods[] = { | ||
| {"to_dict", (PyCFunction) Orderbook_todict, METH_NOARGS, "Return a python dictionary with bids and asks"}, | ||
| {"checksum", (PyCFunction) Orderbook_checksum, METH_NOARGS, "Calculate checksum using top N levels"}, | ||
| {"to_dict", (PyCFunction) Orderbook_todict, METH_VARARGS | METH_KEYWORDS, "return a python dictionary with bids and asks"}, | ||
| {"checksum", (PyCFunction) Orderbook_checksum, METH_NOARGS, "calculate checksum using top N levels"}, | ||
| {NULL} | ||
@@ -74,0 +74,0 @@ }; |
+87
-23
@@ -45,2 +45,3 @@ /* | ||
| } | ||
| return (PyObject *) self; | ||
@@ -164,7 +165,7 @@ } | ||
| if (__builtin_expect(!keys, 0)) { | ||
| if (EXPECT(!keys, 0)) { | ||
| return 1; | ||
| } | ||
| if (__builtin_expect(PyList_Sort(keys) < 0, 0)) { | ||
| if (EXPECT(PyList_Sort(keys) < 0, 0)) { | ||
| Py_DECREF(keys); | ||
@@ -175,3 +176,3 @@ return 1; | ||
| if (self->ordering == DESCENDING) { | ||
| if (__builtin_expect(PyList_Reverse(keys) < 0, 0)) { | ||
| if (EXPECT(PyList_Reverse(keys) < 0, 0)) { | ||
| Py_DECREF(keys); | ||
@@ -184,3 +185,3 @@ return 1; | ||
| Py_DECREF(keys); | ||
| if (__builtin_expect(!ret, 0)) { | ||
| if (EXPECT(!ret, 0)) { | ||
| return 1; | ||
@@ -202,3 +203,3 @@ } | ||
| { | ||
| if (__builtin_expect(update_keys(self), 0)) { | ||
| if (EXPECT(update_keys(self), 0)) { | ||
| return NULL; | ||
@@ -222,7 +223,7 @@ } | ||
| long i = PyLong_AsLong(index); | ||
| if (__builtin_expect(PyErr_Occurred() != NULL, 0)) { | ||
| if (EXPECT(PyErr_Occurred() != NULL, 0)) { | ||
| return NULL; | ||
| } | ||
| if (__builtin_expect(update_keys(self), 0)) { | ||
| if (EXPECT(update_keys(self), 0)) { | ||
| return NULL; | ||
@@ -233,3 +234,3 @@ } | ||
| PyObject *key = PySequence_GetItem(self->keys, i); | ||
| if (__builtin_expect(!key, 0)) { | ||
| if (EXPECT(!key, 0)) { | ||
| return NULL; | ||
@@ -240,3 +241,3 @@ } | ||
| PyObject *value = PyDict_GetItem(self->data, key); | ||
| if (__builtin_expect(!value, 0)) { | ||
| if (EXPECT(!value, 0)) { | ||
| Py_DECREF(key); | ||
@@ -247,3 +248,3 @@ return value; | ||
| PyObject *ret = PyTuple_New(2); | ||
| if (__builtin_expect(!ret, 0)) { | ||
| if (EXPECT(!ret, 0)) { | ||
| Py_DECREF(key); | ||
@@ -261,10 +262,19 @@ return NULL; | ||
| PyObject* SortedDict_todict(SortedDict *self, PyObject *Py_UNUSED(ignored)) | ||
| PyObject* SortedDict_todict(SortedDict *self, PyObject *unused, PyObject *kwargs) | ||
| { | ||
| static char *kwlist[] = {"from_type", "to_type", NULL}; | ||
| PyObject *from = NULL; | ||
| PyObject *to = NULL; | ||
| if (!PyArg_ParseTupleAndKeywords(unused, kwargs, "|$OO", kwlist, &from, &to)) { | ||
| return NULL; | ||
| } | ||
| PyObject *ret = PyDict_New(); | ||
| if (__builtin_expect(!ret, 0)) { | ||
| if (EXPECT(!ret, 0)) { | ||
| return NULL; | ||
| } | ||
| if (__builtin_expect(update_keys(self), 0)) { | ||
| if (EXPECT(update_keys(self), 0)) { | ||
| Py_DECREF(ret); | ||
| return NULL; | ||
@@ -278,6 +288,60 @@ } | ||
| bool free_key, free_value; | ||
| for(int i = 0; i < len; ++i) { | ||
| free_key = false; | ||
| free_value = false; | ||
| PyObject *key = PyTuple_GET_ITEM(self->keys, i); | ||
| PyObject *value = PyDict_GetItem(self->data, key); | ||
| if (to) { | ||
| if (!from || (from && (PyObject_IsInstance(key, from)))) { | ||
| PyObject *args = PyTuple_Pack(1, key); | ||
| if (EXPECT(!args, 0)) { | ||
| Py_DECREF(ret); | ||
| return NULL; | ||
| } | ||
| key = PyObject_CallObject(to, args); | ||
| Py_DECREF(args); | ||
| if (EXPECT(!key, 0)) { | ||
| Py_DECREF(ret); | ||
| return NULL; | ||
| } | ||
| free_key = true; | ||
| } | ||
| if (!from || (from && (PyObject_IsInstance(value, from)))) { | ||
| PyObject *args = PyTuple_Pack(1, value); | ||
| if (EXPECT(!args, 0)) { | ||
| Py_DECREF(ret); | ||
| if (free_key) { | ||
| Py_DECREF(key); | ||
| } | ||
| return NULL; | ||
| } | ||
| value = PyObject_CallObject(to, args); | ||
| Py_DECREF(args); | ||
| if (EXPECT(!value, 0)) { | ||
| Py_DECREF(ret); | ||
| if (free_key) { | ||
| Py_DECREF(key); | ||
| } | ||
| return NULL; | ||
| } | ||
| free_value = true; | ||
| } | ||
| } | ||
| PyDict_SetItem(ret, key, value); | ||
| if (free_key) { | ||
| Py_DECREF(key); | ||
| } | ||
| if (free_value) { | ||
| Py_DECREF(value); | ||
| } | ||
| } | ||
@@ -292,3 +356,3 @@ | ||
| if (self->depth) { | ||
| if (__builtin_expect(update_keys(self), 0)) { | ||
| if (EXPECT(update_keys(self), 0)) { | ||
| return NULL; | ||
@@ -298,3 +362,3 @@ } | ||
| PyObject *delete = PySequence_GetSlice(self->keys, self->depth, PyDict_Size(self->data)); | ||
| if (__builtin_expect(!delete, 0)) { | ||
| if (EXPECT(!delete, 0)) { | ||
| return NULL; | ||
@@ -304,3 +368,3 @@ } | ||
| int len = PySequence_Length(delete); | ||
| if (__builtin_expect(len == -1, 0)) { | ||
| if (EXPECT(len == -1, 0)) { | ||
| Py_DECREF(delete); | ||
@@ -311,3 +375,3 @@ return NULL; | ||
| for (int i = 0; i < len; ++i) { | ||
| if (__builtin_expect(PyDict_DelItem(self->data, PySequence_Fast_GET_ITEM(delete, i)) == -1, 0)) { | ||
| if (EXPECT(PyDict_DelItem(self->data, PySequence_Fast_GET_ITEM(delete, i)) == -1, 0)) { | ||
| Py_DECREF(delete); | ||
@@ -323,3 +387,3 @@ return NULL; | ||
| if (__builtin_expect(update_keys(self), 0)) { | ||
| if (EXPECT(update_keys(self), 0)) { | ||
| return NULL; | ||
@@ -352,3 +416,3 @@ } | ||
| if (__builtin_expect(!PyErr_Occurred(), 0)) { | ||
| if (EXPECT(!PyErr_Occurred(), 0)) { | ||
| PyErr_SetString(PyExc_KeyError, "key does not exist"); | ||
@@ -367,5 +431,5 @@ } | ||
| if (__builtin_expect(ret == -1, 0)) { | ||
| if (EXPECT(ret == -1, 0)) { | ||
| return ret; | ||
| } else if (__builtin_expect(self->truncate && !SortedDict_truncate(self, NULL), 0)) { | ||
| } else if (EXPECT(self->truncate && !SortedDict_truncate(self, NULL), 0)) { | ||
| return -1; | ||
@@ -393,3 +457,3 @@ } | ||
| if (__builtin_expect(update_keys(self), 0)) { | ||
| if (EXPECT(update_keys(self), 0)) { | ||
| return NULL; | ||
@@ -399,3 +463,3 @@ } | ||
| Py_ssize_t size = PySequence_Fast_GET_SIZE(self->keys); | ||
| if (__builtin_expect(size == 0, 0)){ | ||
| if (EXPECT(size == 0, 0)){ | ||
| return NULL; | ||
@@ -402,0 +466,0 @@ } |
@@ -44,3 +44,3 @@ /* | ||
| PyObject* SortedDict_index(SortedDict *self, PyObject *index); | ||
| PyObject* SortedDict_todict(SortedDict *self, PyObject *Py_UNUSED(ignored)); | ||
| PyObject* SortedDict_todict(SortedDict *self, PyObject *unused, PyObject *kwargs); | ||
| PyObject* SortedDict_truncate(SortedDict *self, PyObject *Py_UNUSED(ignored)); | ||
@@ -69,5 +69,5 @@ | ||
| {"keys", (PyCFunction) SortedDict_keys, METH_NOARGS, "return a list of keys in the sorted dictionary"}, | ||
| {"index", (PyCFunction) SortedDict_index, METH_O, "Return a key, value tuple at index N"}, | ||
| {"truncate", (PyCFunction) SortedDict_truncate, METH_NOARGS, "Truncate to length max_depth"}, | ||
| {"to_dict", (PyCFunction) SortedDict_todict, METH_NOARGS, "return a python dictionary, sorted by keys"}, | ||
| {"index", (PyCFunction) SortedDict_index, METH_O, "return a key, value tuple at index N"}, | ||
| {"truncate", (PyCFunction) SortedDict_truncate, METH_NOARGS, "truncate to length max_depth"}, | ||
| {"to_dict", (PyCFunction) SortedDict_todict, METH_VARARGS | METH_KEYWORDS, "return a python dictionary, sorted by keys"}, | ||
| {NULL} | ||
@@ -74,0 +74,0 @@ }; |
@@ -10,2 +10,8 @@ /* | ||
| #if defined(_WIN32) | ||
| #define EXPECT(EXPR, VAL) (EXPR) | ||
| #else | ||
| #define EXPECT(EXPR, VAL) __builtin_expect((EXPR), (VAL)) | ||
| #endif | ||
| enum side_e { | ||
@@ -12,0 +18,0 @@ BID, |
+5
-1
| Metadata-Version: 2.1 | ||
| Name: order_book | ||
| Version: 0.3.2 | ||
| Version: 0.4.0 | ||
| Summary: A fast orderbook implementation, in C, for Python | ||
@@ -130,2 +130,6 @@ Home-page: https://github.com/bmoscon/orderbook | ||
| ### 0.4.0 (2021-09-16) | ||
| * Feature: changes to code and setup.py to enable compiling on windows | ||
| * Feature: add from_type/to_type kwargs to the to_dict methods, allowing for type conversion when creating the dictionary | ||
| ### 0.3.2 (2021-09-04) | ||
@@ -132,0 +136,0 @@ * Bugfix: depth was incorrectly ignored when converting sorteddict to python dict |
+4
-2
@@ -8,2 +8,3 @@ ''' | ||
| import glob | ||
| import os | ||
| from os import path | ||
@@ -16,3 +17,4 @@ from setuptools import setup, Extension, find_packages | ||
| sources = glob.glob('orderbook/*.c') | ||
| orderbook = Extension('order_book', sources=sources, extra_compile_args=["-O3"]) | ||
| optimize_arg = "/O2" if os.name == "nt" else "-O3" | ||
| orderbook = Extension('order_book', sources=sources, extra_compile_args=[optimize_arg]) | ||
@@ -38,3 +40,3 @@ | ||
| name='order_book', | ||
| version='0.3.2', | ||
| version='0.4.0', | ||
| author="Bryant Moscon", | ||
@@ -41,0 +43,0 @@ author_email="bmoscon@gmail.com", |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
60942
3.7%56
3.7%