Execution & Algorithms
Smart order routing, execution algorithms (TWAP, VWAP, Iceberg, Sniper, POV, DCA), pre-trade risk checks, and post-trade reporting
Liquidity.io provides institutional-grade execution capabilities through the @liquidityio/trading SDK. This includes smart order routing across multiple venues, algorithmic execution for minimizing market impact, pre-trade risk management, and post-trade analytics.
Smart Order Routing
When smart routing is enabled, the client aggregates order books from all connected venues and routes orders to the venue offering the best price for the requested size.
How It Works
- The client fetches the order book from every connected venue
- Order books are aggregated into a unified view with per-venue attribution
- For a buy order, the client finds the venue with the lowest ask that can fill the quantity
- For a sell order, it finds the venue with the highest bid
- The order is placed on the selected venue
Configuration
import { Client, Config, NativeVenueConfig, CcxtConfig } from '@liquidityio/trading';
const config = new Config()
.withNative('liquidity', NativeVenueConfig.liquidDex('https://api.liquidity.io/v1'))
.withCcxt('binance', CcxtConfig.create('binance').withCredentials('key', 'secret'))
.withCcxt('okx', CcxtConfig.create('okx').withCredentials('key', 'secret'))
.withVenuePriority(['liquidity', 'binance', 'okx'])
.withSmartRouting(true);
const client = new Client(config);
await client.connect();
// This order is automatically routed to the best venue
const order = await client.buy('BTC-USDC', '1.0');
console.log(`Routed to ${order.venue} at ${order.averagePrice}`);Manual Venue Selection
Override smart routing by specifying a venue:
// Force execution on Binance
const order = await client.buy('BTC-USDC', '1.0', 'binance');
// Force execution on the Liquidity.io ATS
const atsOrder = await client.limitBuy('AAPL', 100, '248.50', 'liquidity');Python
from lx_trading import Client, Config
from lx_trading.config import NativeVenueConfig, CcxtConfig
config = Config()
config.with_native("liquidity", NativeVenueConfig.liquid_dex("https://api.liquidity.io/v1"))
config.with_ccxt("binance", CcxtConfig.create("binance").with_credentials("key", "secret"))
config.smart_routing = True
client = Client(config)
await client.connect()
# Smart-routed order
order = await client.buy("BTC-USDC", Decimal("1.0"))
print(f"Routed to {order.venue} at {order.average_price}")
# Explicit venue
order = await client.buy("BTC-USDC", Decimal("1.0"), venue="binance")Execution Algorithms
The SDK provides six built-in execution algorithms for managing large orders with minimal market impact. All algorithms are implemented client-side and use the Client to place orders.
TWAP (Time-Weighted Average Price)
Splits a large order into equal slices executed at regular time intervals. Suitable when the goal is to match the time-weighted average price over a period.
Parameters:
| Parameter | Type | Description |
|---|---|---|
symbol | string | Trading pair |
side | Side | BUY or SELL |
totalQuantity | Decimal | Total quantity to execute |
durationSeconds | number | Time window for execution |
numSlices | number | Number of child orders |
TypeScript:
import { TwapExecutor, Side } from '@liquidityio/trading';
import { Decimal } from 'decimal.js';
const twap = new TwapExecutor(
client,
'BTC-USDC', // symbol
Side.BUY, // side
new Decimal('10'), // total quantity: 10 BTC
3600, // duration: 1 hour
12, // 12 slices (one every 5 minutes)
);
const orders = await twap.execute();
console.log(`Executed ${orders.length} slices`);
for (const o of orders) {
console.log(` ${o.orderId}: ${o.filledQuantity} @ ${o.averagePrice}`);
}Python:
from lx_trading.execution import TwapExecutor
from lx_trading.types import Side
from decimal import Decimal
twap = TwapExecutor(
client=client,
symbol="BTC-USDC",
side=Side.BUY,
total_quantity=Decimal("10"),
duration_seconds=3600,
num_slices=12,
)
orders = await twap.execute()
for o in orders:
print(f" {o.order_id}: {o.filled_quantity} @ {o.average_price}")VWAP (Volume-Weighted Average Price)
Executes based on a participation rate of market volume. The algorithm monitors real-time volume and sizes each slice to represent a fixed percentage of observed volume. Suitable when the goal is to match the volume-weighted average price.
Parameters:
| Parameter | Type | Description |
|---|---|---|
symbol | string | Trading pair |
side | Side | BUY or SELL |
totalQuantity | Decimal | Total quantity to execute |
participationRate | Decimal | Fraction of market volume (e.g., 0.10 = 10%) |
maxDurationSeconds | number | Maximum execution window |
TypeScript:
import { VwapExecutor, Side } from '@liquidityio/trading';
const vwap = new VwapExecutor(
client,
'BTC-USDC', // symbol
Side.BUY, // side
new Decimal('10'), // total: 10 BTC
new Decimal('0.10'), // 10% participation rate
7200, // max 2 hours
);
const orders = await vwap.execute();
const totalFilled = orders.reduce(
(sum, o) => sum.plus(o.filledQuantity),
new Decimal(0),
);
console.log(`VWAP filled ${totalFilled} BTC across ${orders.length} slices`);Python:
from lx_trading.execution import VwapExecutor
vwap = VwapExecutor(
client=client,
symbol="BTC-USDC",
side=Side.BUY,
total_quantity=Decimal("10"),
participation_rate=Decimal("0.10"),
max_duration_seconds=7200,
)
orders = await vwap.execute()
total_filled = sum(o.filled_quantity for o in orders)
print(f"VWAP filled {total_filled} BTC in {len(orders)} slices")Iceberg
Hides a large order by placing only a small visible quantity at a time. Each visible slice is a limit order; when it fills, the next slice is placed. The full order size is never revealed to the market.
Parameters:
| Parameter | Type | Description |
|---|---|---|
symbol | string | Trading pair |
side | Side | BUY or SELL |
totalQuantity | Decimal | Total hidden quantity |
visibleQuantity | Decimal | Size of each visible slice |
price | Decimal | Limit price for all slices |
venue | string | (Optional) Target venue |
TypeScript:
import { IcebergExecutor, Side } from '@liquidityio/trading';
const iceberg = new IcebergExecutor(
client,
'BTC-USDC', // symbol
Side.BUY, // side
new Decimal('100'), // total: 100 BTC (hidden)
new Decimal('5'), // visible: 5 BTC at a time
new Decimal('83000'), // limit price
'liquidity', // venue (optional)
);
const orders = await iceberg.execute();
console.log(`Iceberg completed: ${orders.length} slices filled`);Python:
from lx_trading.execution import IcebergExecutor
iceberg = IcebergExecutor(
client=client,
symbol="BTC-USDC",
side=Side.BUY,
total_quantity=Decimal("100"),
visible_quantity=Decimal("5"),
price=Decimal("83000"),
venue="liquidity",
)
orders = await iceberg.execute()Sniper
Monitors the market and executes immediately when the price reaches a target. Useful for capturing specific price levels with minimal latency. Polls the ticker every 100ms.
Parameters:
| Parameter | Type | Description |
|---|---|---|
symbol | string | Trading pair |
side | Side | BUY or SELL |
quantity | Decimal | Order quantity |
targetPrice | Decimal | Price trigger (buy: ask ≤ target, sell: bid ≥ target) |
timeoutSeconds | number | Maximum wait time |
TypeScript:
import { SniperExecutor, Side } from '@liquidityio/trading';
const sniper = new SniperExecutor(
client,
'BTC-USDC', // symbol
Side.BUY, // side
new Decimal('1'), // quantity: 1 BTC
new Decimal('80000'), // trigger when ask <= 80000
300, // timeout: 5 minutes
);
const order = await sniper.execute();
if (order) {
console.log(`Sniped at ${order.averagePrice}`);
} else {
console.log('Timeout reached, price target not hit');
}Python:
from lx_trading.execution import SniperExecutor
sniper = SniperExecutor(
client=client,
symbol="BTC-USDC",
side=Side.BUY,
quantity=Decimal("1"),
target_price=Decimal("80000"),
timeout_seconds=300,
)
order = await sniper.execute()
if order:
print(f"Sniped at {order.average_price}")
else:
print("Timeout -- target not reached")POV (Percent of Volume)
Executes as a fixed percentage of observed market volume over time. Similar to VWAP but tracks actual volume deltas rather than estimated hourly rates. Checks volume every 10 seconds.
Parameters:
| Parameter | Type | Description |
|---|---|---|
symbol | string | Trading pair |
side | Side | BUY or SELL |
totalQuantity | Decimal | Total quantity to execute |
targetPercent | Decimal | Target percentage of volume (e.g., 0.05 = 5%) |
maxDurationSeconds | number | Maximum execution window |
TypeScript:
import { PovExecutor, Side } from '@liquidityio/trading';
const pov = new PovExecutor(
client,
'BTC-USDC', // symbol
Side.SELL, // side
new Decimal('50'), // total: 50 BTC
new Decimal('0.05'), // 5% of market volume
14400, // max 4 hours
);
const orders = await pov.execute();
console.log(`POV completed: ${orders.length} slices`);DCA (Dollar Cost Averaging)
Invests a fixed dollar amount at regular intervals regardless of price. Useful for systematic accumulation strategies.
Parameters:
| Parameter | Type | Description |
|---|---|---|
symbol | string | Trading pair |
side | Side | BUY or SELL |
amountPerInterval | Decimal | Dollar amount per interval (quote currency) |
intervalMs | number | Time between orders (milliseconds) |
numIntervals | number | Number of intervals |
venue | string | (Optional) Target venue |
TypeScript:
import { DcaExecutor, Side } from '@liquidityio/trading';
const dca = new DcaExecutor(
client,
'BTC-USDC', // symbol
Side.BUY, // side
new Decimal('1000'), // $1000 per interval
3600000, // every 1 hour
24, // 24 intervals (1 day)
'liquidity', // venue (optional)
);
const orders = await dca.execute();
const totalSpent = orders.reduce(
(sum, o) => sum.plus(o.filledQuantity.mul(o.averagePrice ?? 0)),
new Decimal(0),
);
console.log(`DCA invested $${totalSpent} across ${orders.length} buys`);Python:
from lx_trading.execution import DcaExecutor
dca = DcaExecutor(
client=client,
symbol="BTC-USDC",
side=Side.BUY,
amount_per_interval=Decimal("1000"),
interval_ms=3600000,
num_intervals=24,
venue="liquidity",
)
orders = await dca.execute()Algorithm Comparison
| Algorithm | Use Case | Market Impact | Urgency | Volume Sensitivity |
|---|---|---|---|---|
| TWAP | Benchmark tracking | Low | Low | No |
| VWAP | Volume-matched execution | Low | Low | Yes |
| Iceberg | Hide large orders | Minimal | Medium | No |
| Sniper | Price targeting | None (until trigger) | High | No |
| POV | Participation-limited | Low | Low | Yes |
| DCA | Systematic accumulation | Minimal | None | No |
Pre-Trade Risk Checks
The SDK includes a RiskManager that validates every order against configurable limits before submission.
Risk Parameters
| Parameter | Type | Description |
|---|---|---|
enabled | boolean | Enable/disable risk checks |
maxPositionSize | Decimal | Maximum position in any single asset |
maxOrderSize | Decimal | Maximum size for a single order |
maxDailyLoss | Decimal | Maximum daily loss before kill switch |
maxOpenOrders | number | Maximum concurrent open orders per symbol |
killSwitchEnabled | boolean | Auto-halt trading on daily loss breach |
positionLimits | Map | Per-asset position limits |
TypeScript
import { RiskManager, RiskError } from '@liquidityio/trading';
import { Decimal } from 'decimal.js';
const risk = new RiskManager({
enabled: true,
maxPositionSize: new Decimal('100'), // Max 100 units of any asset
maxOrderSize: new Decimal('10'), // Max 10 units per order
maxDailyLoss: new Decimal('5000'), // $5000 daily loss limit
maxOpenOrders: 50, // Max 50 open orders per symbol
killSwitchEnabled: true, // Auto-halt on loss breach
positionLimits: new Map([
['BTC', new Decimal('10')], // Max 10 BTC position
['ETH', new Decimal('100')], // Max 100 ETH position
]),
});
// Validate before placing an order
try {
risk.validateOrder(orderRequest);
const order = await client.placeOrder(orderRequest);
risk.orderOpened(order.symbol);
} catch (e) {
if (e instanceof RiskError) {
console.error(`Risk check failed: ${e.message}`);
// Do not place the order
}
}
// Update after fills
risk.updatePosition('BTC', new Decimal('1'), Side.BUY);
risk.updatePnl(new Decimal('-250'));
// Check state
console.log(`BTC position: ${risk.position('BTC')}`);
console.log(`Daily PnL: ${risk.getDailyPnl()}`);
console.log(`Kill switch active: ${risk.isKilled}`);
// Reset daily PnL at start of trading day
risk.resetDailyPnl();Python
from lx_trading import RiskManager, RiskError
from lx_trading.config import RiskConfig
from decimal import Decimal
risk = RiskManager(RiskConfig(
enabled=True,
max_position_size=Decimal("100"),
max_order_size=Decimal("10"),
max_daily_loss=Decimal("5000"),
max_open_orders=50,
kill_switch_enabled=True,
position_limits={"BTC": Decimal("10"), "ETH": Decimal("100")},
))
try:
risk.validate_order(order_request)
order = await client.place_order(order_request)
risk.order_opened(order.symbol)
except RiskError as e:
print(f"Risk check failed: {e}")Kill Switch
When killSwitchEnabled is true and the daily PnL exceeds maxDailyLoss, the risk manager activates the kill switch. All subsequent validateOrder calls throw RiskError('Kill switch is active').
To resume trading after a kill switch activation:
risk.reset(); // Deactivate kill switch
risk.resetDailyPnl(); // Reset daily PnL counterPost-Trade Reporting
Order Status Tracking
Track order execution in real-time:
import { orderIsOpen, orderIsDone, orderFillPercent } from '@liquidityio/trading';
const order = await client.limitBuy('BTC-USDC', '1.0', '83000');
// Check status
console.log(`Open: ${orderIsOpen(order)}`);
console.log(`Done: ${orderIsDone(order)}`);
console.log(`Fill %: ${orderFillPercent(order)}%`);
console.log(`Filled: ${order.filledQuantity} / ${order.quantity}`);
console.log(`Remaining: ${order.remainingQuantity}`);
console.log(`Avg price: ${order.averagePrice}`);
console.log(`Fees:`, order.fees);Trade History
Each order may generate multiple fills (trades):
interface Trade {
tradeId: string; // Unique trade ID
orderId: string; // Parent order ID
symbol: string; // Trading pair
venue: string; // Execution venue
side: Side; // Buy or sell
price: Decimal; // Execution price
quantity: Decimal; // Fill quantity
fee: Fee; // Fee charged
timestamp: number; // Unix timestamp
isMaker: boolean; // True if maker, false if taker
}Execution Quality Metrics
Use the orderbook's VWAP functions to measure execution quality:
const book = await client.orderbook('BTC-USDC');
// Expected VWAP for buying 1 BTC based on current book
const expectedVwap = book.vwapBuy(new Decimal('1'));
// Compare against actual execution
const actualPrice = order.averagePrice;
const slippage = actualPrice?.minus(expectedVwap ?? 0);
console.log(`Expected VWAP: ${expectedVwap}`);
console.log(`Actual price: ${actualPrice}`);
console.log(`Slippage: ${slippage}`);Balance and Position Monitoring
// Aggregated balances across all venues
const balances = await client.balances();
for (const b of balances) {
console.log(`${b.asset}: ${b.totalFree} free, ${b.totalLocked} locked`);
for (const v of b.byVenue) {
console.log(` ${v.venue}: ${v.free} free, ${v.locked} locked`);
}
}
// Single asset balance on a specific venue
const btcBalance = await client.balance('BTC', 'liquidity');
console.log(`BTC on Liquidity.io: ${btcBalance.free} free`);FIX Protocol
The on-chain DEX supports FIX 4.4 protocol for institutional connectivity. Contact the Liquidity.io institutional desk for FIX session credentials and CompIDs.
Supported FIX Messages
| Message | Tag | Direction | Description |
|---|---|---|---|
| NewOrderSingle | D | Client -> Server | Submit new order |
| ExecutionReport | 8 | Server -> Client | Order status / fill |
| OrderCancelRequest | F | Client -> Server | Cancel order |
| OrderCancelReject | 9 | Server -> Client | Cancel rejection |
| MarketDataRequest | V | Client -> Server | Subscribe to market data |
| MarketDataSnapshotFullRefresh | W | Server -> Client | Full book snapshot |
| MarketDataIncrementalRefresh | X | Server -> Client | Incremental book update |
FIX Performance
Benchmarked throughput (see On-Chain DEX for engine details):
| Engine | NewOrderSingle/sec | ExecutionReport/sec |
|---|---|---|
| Go | 163K | 124K |
| C++ | 444K | 804K |
| Rust | 484K | 232K |
Configuration Reference
TOML Configuration
All SDK parameters can be set via a TOML configuration file:
[general]
log_level = "info"
timeout_ms = 30000
smart_routing = true
venue_priority = ["liquidity", "binance"]
min_improvement_bps = 5
[risk]
enabled = true
max_position_size = 100
max_order_size = 10
max_daily_loss = 5000
max_open_orders = 50
kill_switch_enabled = true
[risk.position_limits]
BTC = 10
ETH = 100
[native.liquidity]
venue_type = "dex"
api_url = "https://api.liquidity.io/v1"
ws_url = "wss://api.liquidity.io/ws"
api_key = "your-api-key"
api_secret = "your-api-secret"
network = "mainnet"
chain_id = 96369
streaming = true
[ccxt.binance]
exchange_id = "binance"
api_key = "your-binance-key"
api_secret = "your-binance-secret"
sandbox = false
rate_limit = trueLoad in code:
import { Config } from '@liquidityio/trading';
const config = Config.fromObject(require('./config.toml'));
const client = new Client(config);config = Config.from_file("config.toml")
client = Client(config)Supported Venues
All execution algorithms and smart routing work across these venue types:
| Venue Type | Examples | Capabilities |
|---|---|---|
| Native CLOB | Liquidity.io ATS, LX DEX | Full orderbook, limit/market, streaming |
| Native AMM | LX AMM | Swaps, liquidity provision, LP tracking |
| CCXT | Binance, OKX, MEXC, Bybit, KuCoin, 100+ | Orderbook, orders, balances |
| Hummingbot Gateway | Any Gateway-supported DEX | Swaps, LP positions |