Strategy API¶
Event-driven strategy classes for Python. Mirrors C++ flox::Strategy.
Class: flox_py.Symbol¶
Returned by SymbolRegistry.add_symbol. Works transparently as an int wherever a symbol ID is expected.
| Property | Type | Description |
|---|---|---|
id |
int |
Numeric symbol ID |
name |
str |
Symbol name, e.g. "BTCUSDT" |
exchange |
str |
Exchange name, e.g. "binance" |
tick_size |
float |
Minimum price increment |
Class: flox_py.SymbolRegistry¶
| Method | Returns | Description |
|---|---|---|
add_symbol(exchange, name, tick_size) |
Symbol |
Register a symbol and return its Symbol object |
Class: flox_py.Strategy¶
Constructor¶
symbols — list of Symbol objects or raw integer IDs to subscribe to.
Overridable Callbacks¶
on_trade(ctx: SymbolContext, trade: TradeData)¶
Called on each trade event for subscribed symbols.
on_book_update(ctx: SymbolContext)¶
Called on each order book update.
on_bar(ctx: SymbolContext, bar: BarData)¶
Called on each closed OHLC bar. bar exposes open, high, low, close,
volume, buy_volume, start_time_ns, end_time_ns, bar_type,
bar_type_param, close_reason. Use BacktestRunner.run_bars(...) to replay
historical bars or Runner.on_bar(...) to push live bars.
def on_bar(self, ctx, bar):
# detect breakout on bar close
if bar.close > self.prev_high and ctx.is_flat():
self.market_buy(0.01)
self.prev_high = max(getattr(self, "prev_high", 0.0), bar.high)
on_start() / on_stop()¶
Lifecycle callbacks.
Order Emission — Shorthand¶
These methods use the first registered symbol when symbol is omitted.
| Method | Description |
|---|---|
market_buy(qty, symbol=None) |
Market buy |
market_sell(qty, symbol=None) |
Market sell |
limit_buy(price, qty, symbol=None) |
Limit buy |
limit_sell(price, qty, symbol=None) |
Limit sell |
stop_market(side, trigger, qty, symbol=None) |
Stop market |
close_position(symbol=None) |
Close position (reduce-only) |
Order Emission — Explicit (emit_* variants)¶
| Method | Returns | Description |
|---|---|---|
emit_market_buy(symbol, qty) |
int |
Market buy, returns order ID |
emit_market_sell(symbol, qty) |
int |
Market sell |
emit_limit_buy(symbol, price, qty) |
int |
Limit buy |
emit_limit_sell(symbol, price, qty) |
int |
Limit sell |
emit_cancel(order_id) |
None |
Cancel order |
emit_cancel_all(symbol) |
None |
Cancel all orders for symbol |
emit_modify(order_id, price, qty) |
None |
Modify existing order |
emit_stop_market(symbol, side, trigger, qty) |
int |
Stop market order |
emit_stop_limit(symbol, side, trigger, limit, qty) |
int |
Stop limit order |
emit_take_profit_market(symbol, side, trigger, qty) |
int |
Take profit market |
emit_take_profit_limit(symbol, side, trigger, limit, qty) |
int |
Take profit limit |
emit_trailing_stop(symbol, side, offset, qty) |
int |
Trailing stop |
emit_trailing_stop_percent(symbol, side, bps, qty) |
int |
Trailing stop (%) |
emit_close_position(symbol) |
int |
Close position (reduce-only) |
Context Queries¶
| Method | Returns | Description |
|---|---|---|
position(symbol) |
float |
Current position quantity |
ctx(symbol) |
SymbolContext |
Per-symbol context snapshot |
get_order_status(order_id) |
int |
Order status (-1 if not found) |
Class: flox_py.SymbolContext¶
| Property | Type | Description |
|---|---|---|
symbol_id |
int |
Symbol identifier |
position |
float |
Current position |
last_trade_price |
float |
Last trade price |
best_bid |
float |
Best bid price |
best_ask |
float |
Best ask price |
mid_price |
float |
Mid price |
unrealized_pnl |
float |
Unrealized P&L |
book_spread() |
float |
Bid-ask spread |
is_long() |
bool |
True if long |
is_short() |
bool |
True if short |
is_flat() |
bool |
True if no position |
Class: flox_py.TradeData¶
| Property | Type | Description |
|---|---|---|
symbol |
int |
Symbol ID |
price |
float |
Trade price |
quantity |
float |
Trade quantity |
is_buy |
bool |
Buy-side aggressor |
timestamp_ns |
int |
Timestamp (nanoseconds) |
Class: flox_py.Runner¶
Feeds market data into strategies and routes emitted signals to a callback.
runner = flox.Runner(registry, on_signal) # synchronous
runner = flox.Runner(registry, on_signal, threaded=True) # Disruptor background thread
runner.add_strategy(strategy)
runner.start()
runner.on_trade(symbol, price, qty, is_buy, ts_ns)
runner.on_book_snapshot(symbol, bid_prices, bid_qtys, ask_prices, ask_qtys, ts_ns)
runner.on_bar(symbol, open, high, low, close, volume, ...)
runner.stop()
| Method | Description |
|---|---|
add_strategy(strategy) |
Register a strategy instance |
start() |
Start the runner |
stop() |
Stop the runner |
on_trade(symbol, price, qty, is_buy, ts_ns) |
Inject a trade event |
on_book_snapshot(symbol, bid_px, bid_qty, ask_px, ask_qty, ts_ns) |
Inject an order book snapshot |
on_bar(symbol, open, high, low, close, volume=0, buy_volume=0, start_time_ns=0, end_time_ns=0, bar_type=0, bar_type_param=0, close_reason=0) |
Inject a closed OHLC bar |
symbol accepts a Symbol object or a raw int.
Signal object¶
Passed to the on_signal callback.
| Property | Type | Description |
|---|---|---|
side |
str |
"buy" or "sell" |
quantity |
float |
Order quantity |
price |
float |
Limit price (0 for market) |
order_type |
str |
"market", "limit", etc. |
order_id |
int |
Internal order ID |
Class: flox_py.BacktestRunner¶
Runs a strategy against historical CSV data.
bt = flox.BacktestRunner(registry, fee_rate=0.0004, initial_capital=10_000)
bt.set_strategy(strategy)
stats = bt.run_csv("data.csv") # auto-detects symbol from registry
stats = bt.run_csv("data.csv", "BTCUSDT") # explicit symbol name
| Method | Description |
|---|---|
set_strategy(strategy) |
Set the strategy to backtest |
run_csv(path, symbol=None) |
Run backtest against a CSV file, returns stats dict |
run_ohlcv(ts, close, symbol=None) |
Replay close-only bars as synthetic trades (Strategy.on_trade fires) |
run_bars(start_time_ns, end_time_ns, open, high, low, close, volume, symbol=None, bar_type=0, bar_type_param=0) |
Replay full OHLCV bars (Strategy.on_bar fires) |
Stats dict keys¶
| Key | Description |
|---|---|
return_pct |
Net return percentage |
net_pnl |
Net P&L after fees |
total_trades |
Round-trip trade count |
win_rate |
Winning trade fraction |
sharpe |
Annualized Sharpe ratio |
max_drawdown_pct |
Peak-to-trough drawdown (%) |
Example¶
import flox_py as flox
registry = flox.SymbolRegistry()
btc = registry.add_symbol("binance", "BTCUSDT", tick_size=0.01)
class SMAcross(flox.Strategy):
def __init__(self, symbols):
super().__init__(symbols)
self.fast = flox.SMA(10)
self.slow = flox.SMA(30)
def on_trade(self, ctx, trade):
f = self.fast.update(trade.price)
s = self.slow.update(trade.price)
if f is None or s is None:
return
if f > s and ctx.is_flat():
self.market_buy(0.01)
elif f < s and ctx.is_long():
self.close_position()
# Live
def on_signal(sig):
print(sig.side, sig.order_type, sig.quantity)
runner = flox.Runner(registry, on_signal)
runner.add_strategy(SMAcross([btc]))
runner.start()
# Backtest
bt = flox.BacktestRunner(registry, fee_rate=0.0004, initial_capital=10_000)
bt.set_strategy(SMAcross([btc]))
stats = bt.run_csv("btcusdt_trades.csv", "BTCUSDT")
print(stats)