Documentation
Everything you need to write, run, and interpret HFTS strategies.
Quickstart
A strategy is a Python class with a single required method on_calc that is called for every market tick. The simplest possible strategy:
Open the app, paste this into the editor, select a symbol and date range, and click Run.
Strategy lifecycle
| Method | Called when |
|---|---|
| __init__(self) | Once before the first tick. Use to initialise state variables. |
| on_calc(self, tick) | Every market tick (bid/ask update). Main strategy logic goes here. |
| on_ack(self, order) | Called when the exchange acknowledges an order (open or instant fill). Optional. |
| on_complete(self, order) | Called when a passive limit order is fully filled. Optional. |
| on_partial(self, order) | Called when a passive limit order is partially filled. order.size is the amount filled in this event; the order remains open for the rest. Optional. |
| on_error(self, order, error) | Called when an order is rejected. error is a string describing the reason (e.g. "not_enough", "postonly"). Optional. |
Tick object
Each call to on_calc receives a tick with the latest market state:
| Field | Type | Description |
|---|---|---|
| tick.bid | float | Best bid price |
| tick.ask | float | Best ask price |
| tick.bid_size | float | Size available at best bid |
| tick.ask_size | float | Size available at best ask |
| tick.timestamp | datetime | Exchange timestamp of this quote |
| tick.sequence | int | Exchange sequence number (virtual) |
Indicators
The sandbox includes talipp, a streaming technical indicator library designed for event-driven systems. Indicators update incrementally on each new value — no need to recompute over the full history on every tick.
Popular indicators available:
| Indicator | Description |
|---|---|
| EMA(period) | Exponential moving average |
| SMA(period) | Simple moving average |
| RSI(period) | Relative strength index (0–100) |
| MACD(fast, slow, signal) | MACD line, signal line, and histogram |
| BB(period, std_dev) | Bollinger Bands — upper, middle, lower |
| STOCH(period) | Stochastic oscillator (%K and %D) |
| ATR(period) | Average true range |
| VWAP() | Volume-weighted average price |
Full list of supported indicators →
Need a different library? Additional packages can be added to the sandbox on request — reach out via the contact form.
Placing orders
All order methods are available on self inside on_calc.
Limit orders
| Method | Parameters | Description |
|---|---|---|
| buy_limit(price, size, post_only=False) | price: float, size: float|None, post_only: bool | Post a passive buy limit order. If size is None, uses all available USD. If post_only is True, the order is cancelled instead of filled when it would immediately cross the spread. |
| sell_limit(price, size, post_only=False) | price: float, size: float|None, post_only: bool | Post a passive sell limit order. If size is None, sells all BTC. If post_only is True, the order is cancelled instead of filled when it would immediately cross the spread. |
Market orders
| Method | Parameters | Description |
|---|---|---|
| buy_market(size) | size: float|None | Buy at best ask immediately. If size is None, uses all available USD. |
| sell_market(size) | size: float|None | Sell at best bid immediately. If size is None, sells all BTC. |
Order management
| Method | Description |
|---|---|
| has_active_orders() | Returns True if any order is currently open or pending fill. |
| last_order() | Returns the most recently submitted order as an Order object, or None if no order has been placed yet. |
| cancel_all() | Cancel all open orders. |
Wallet
The simulation starts with a fixed initial balance. Access current balances via:
| Field | Type | Description |
|---|---|---|
| self.wallet_quote | float | Available quote currency balance (e.g. USD) |
| self.wallet_base | float | Available base currency balance (e.g. BTC) |
Setup methods
Call these inside __init__ to configure the run before it starts.
| Method | Parameters | Description |
|---|---|---|
| fund(amount) | amount: float | Set the starting USD balance. Defaults to 10 000.0 if not called. |
| seed(value) | value: int | Seed the engine's random number generator for fully reproducible runs. Two runs with the same seed and parameters produce identical fill sequences and P&L. Defaults to a fixed seed if not called. |
| set_taker_fee(rate) | rate: float | Set the taker fee multiplier applied to market orders and limit orders that cross the spread. Defaults to 0.997 (0.3% fee). |
| set_maker_fee(rate) | rate: float | Set the maker fee multiplier applied to passive limit orders that rest in the book. Defaults to 1.000 (no fee). |
Parameters
Any instance variables set in __init__ are treated as configurable parameters. They appear in the UI and can be overridden per run without editing code.
Prefix a variable with _ to mark it as internal state (not a parameter):
Report
After each run the report tab shows:
- Accumulated P&L curve (Highstock interactive chart)
- Per-trade table — side, price, size, fee, latency, status
- Summary: total P&L, win rate, average latency, max drawdown, total fees
- Cancelled and failed orders are flagged inline
Latency model
HFTS models the full round-trip delay for every order:
- Sync delay — time from tick reception to order submission (your processing time)
- Ack delay — exchange processing and acknowledgement time
Both delays are sampled from a configurable distribution (normal or uniform) with configurable min/max bounds. The order book state at the simulated arrival time is used for fill logic — so a late order can miss the price level entirely and get cancelled.
Fill rules
| Order type | Fee | Fill condition |
|---|---|---|
| buy_limit | Maker (0%) | Ask drops to or below your price after ack delay. May fill as taker if the price crosses the spread at ack time. If the available size at that price level is smaller than the order, the order is partially filled and remains open for the remainder — on_partial is called for each partial fill, on_complete when fully filled. |
| sell_limit | Maker (0%) | Bid rises to or above your price after ack delay. May fill as taker if the price crosses the spread at ack time. Same partial fill behaviour as buy_limit. |
| buy_market | Taker (0.3%) | Fills immediately at ask at ack time |
| sell_market | Taker (0.3%) | Fills immediately at bid at ack time |