SORY
A smart order router for crypto, in a Python package.
One order book across every venue you trade, then execute against the best all-in price — fees included. One config to get a consolidated book, one call to route an order.
pip install sory
The whole integration
import asyncio
from decimal import Decimal
from sory import SoryClient
config = {
"licence_key": "...",
"symbol": "BTC/USDT",
"exchanges": ["binance", "okx", "bybit"],
"fees_bps": "fees.toml",
"fee_offset": {"binance": "taker", "okx": "taker", "bybit": "taker"},
}
async def main() -> None:
async with SoryClient(config) as sory:
# A consolidated, fee-adjusted book across all three venues.
book = sory.book()
print(book.best_bid.price, book.best_ask.price, book.best_ask.venue)
# One call. SORY splits it across venues on best all-in price.
order = await sory.place(side="buy", qty=Decimal("2"), order_type="market")
print(order.filled, order.achieved_price, order.plan.allocations)
asyncio.run(main())
That is the integration. No adapters to write, no per-venue special cases, no WebSocket plumbing.
What it does
Two things, well:
Market data — one consolidated order book across any number of venues, with your fees applied where you ask, venue health tracked, VWAP depth and the trade tape on request.
Execution — a market order split across venues on best all-in price, or a limit order resting on the venue you name, with a full account of what filled where and why.
It is deliberately not an OEMS. No balances, no positions, no PnL, no trade database. SORY plugs into whatever already owns those — which is why it takes about ten minutes to drop into an existing system.
What people use it for
| Best execution | Sweep the consolidated book instead of guessing which venue is cheapest. |
| Arbitrage and basis | One book, every venue, fee-adjusted — the spread you see is the spread you get. |
| Hedging | Fill on one venue, hedge across the others in a single call. |
| Market making | Quote passively, hedge the fill in one call. |
| Unwinds and liquidations | Size against real aggregate depth, and see the remainder you could not fill. |
| Execution algos | TWAP, VWAP, POV — build the schedule, let SORY handle each slice. |
| Research and monitoring | Consolidated depth and the trade tape, as a normal Python stream. |
Why fee-aware routing matters
Routing on quoted prices is routing on the wrong number:
venue A: ask 100.00, your taker fee 20 bps -> all-in 100.200
venue B: ask 100.05, your taker fee 1 bp -> all-in 100.060 <- actually cheaper
Venue A looks better and costs more. SORY compares all-in prices everywhere, using your own fee tier from a config file — never a venue's published rate, which is usually the entry-level tier and not what you pay.
What you get
| Consolidated book | Any number of venues merged into one, fee-adjusted, best price first. |
| Venue health | Online / Stale / Offline. A silent feed is treated as a fault, not a quiet market — and never routed to. |
| Fee-inclusive routing | Splits on best all-in price, and reports every venue it considered, excluded, and why. |
| One config | One symbol per client. Two symbols is two clients, one line each. |
| Full reconciliation | requested == filled + unfilled, always, with a reason. Remainders are never silently dropped. |
| VWAP depth | The price to fill 1, 5, 10 units — and the limit price needed to achieve it. |
| Unified liquidity | Pool BTC/USD, BTC/USDC and BTC/USDT into one book. |
| Trade tape | Every venue, streamed in parallel with the book. |
Decimal throughout |
No float rounding anywhere near a price, size or fee. |
| Dry run | One flag on place(): routes, validates and builds every venue order, then stops before sending. |
| Open orders | Ask the venues what is still resting — the whole recovery story after a restart. |
Venues
Binance, OKX, Bybit, Coinbase, Kraken, Bitget, KuCoin, Gate, HTX, Crypto.com, Bullish, Bitstamp, Bitfinex, Hyperliquid and Deribit, among others — the full list is generated from the registry, so it is never out of date.
Venue ids and symbols follow a single convention across all of them — BTC/USDT for spot,
BTC/USDT:USDT for a perp — so putting a venue on a card is one string. Omit exchanges entirely
and SORY uses every venue that lists the symbol. Need one that is not listed?
Ask for it — new venues land on almost every release.
Try it
playground.sory.pro — build a config and run it against live order books, in your browser.
Documentation
introduction · configuration · market data · routing · execution · fees · market making · errors · venues · licensing
Licence
SORY is commercial software, unlocked with a licence key — see licensing. The source is MIT; see LICENSE.
Metadata
Release files for sory 5.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sory-5.6.1.tar.gz | 97.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sory-5.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 166.6 kB
Release files / sory-5.6.1.tar.gz
| Download URL | sory-5.6.1.tar.gz |
|---|---|
| Size | 97.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
67b1d0ee872d1c493e45df062a6f5aded8eaaba089b24343cd2ad7b29a2468ca
|
|
BLAKE2b-256 checksum How to use checksums |
8c2f188a42960dd7b6800d3fde09147c6b87567850b650934fdbd37d55ab4189
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / sory-5.6.1-py3-none-any.whl
| Download URL | sory-5.6.1-py3-none-any.whl |
|---|---|
| Size | 69.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0a9e078c6144a8147a5d44ee748052a5869a3327217b77a0e818b7f1bf376416
|
|
BLAKE2b-256 checksum How to use checksums |
df4da2516dad65621142bb1be778030430b23b733497f38b8ff6b404dab2bef4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|