Python SDKv1.0.2.1

HiveQ Flow
API Reference

Institutional-grade quantitative trading and backtesting — in a few lines of Python. You author the strategy on your machine; HiveQ runs it on the engine that has executed billions of dollars in live trades — sourcing the data, simulating real market microstructure, and handing back a full performance report.

Install the HiveQ SDK
bash
python -m pip install --upgrade "hiveq-sdk==1.0.2.1"
python -c "import importlib.metadata as m; print(m.version('hiveq-sdk'))"
HiveQ Start · Docs & Learning

HiveQ Video Setup Guide.

Learn HiveQ without leaving Docs. Move from a clean Python environment and secure sign-in to a reviewed strategy, a completed backtest, and evidence you can explain.

Python 3.11+Six guided videosNo API-key copying
Guided walkthrough · 0/6 watched01 · 01:06
Preview · Illustrative UI
Guide 01 of 06

Install and Sign In to HiveQ

Create an isolated Python environment, install the public HiveQ SDK, complete secure browser authentication, and verify the session.

python -m venv .venvpython -m pip install hiveq-sdkhiveq login
Completion is saved when the video ends
Selected outcomeAn isolated environment and an authenticated HiveQ session.
Human pathVideo, commands, checkpoints
AI pathInstalled Flow reference
Written onboarding

Nine steps. One evidence trail.

Follow the same supported workflow in text. The core journey ends with a completed, understood backtest; competition is an optional next step.

Your position1 / 9
Step 01

Install and sign in

Create an isolated Python environment, install the public HiveQ SDK, and complete the secure browser sign-in.

workflow
python -m venv .venv
source .venv/bin/activate
python -m pip install hiveq-sdk
hiveq login
Review before continuing
  • Use Python 3.11 or newer.
  • Let the CLI complete the secure browser handoff.
  • Never copy API keys or token files into strategy code.
OutcomeAuthenticated local research environment

Overview

Install hiveq-sdk on your machine and author strategies through its hiveq.flow namespace. The local client captures and submits your strategy; HiveQ then runs its callbacks in the managed engine, sources the declared data, and returns a Run handle for results. Direct local data discovery is a separate hiveq_data workflow—not something to call from a strategy callback.

Why HiveQ

  • A simulator built to mirror production. Orders clear against real market microstructure — tick-level data, exchange session windows, primary-exchange auctions, per-asset fee models, slippage, and tick-size rounding.
  • Research → live, same code. The strategy you backtest runs unchanged in paper and live trading — promote a validated backtest from the platform in one click.
  • Every run tracked & versioned. Strategy code and config are captured per run, so any backtest is reproducible — inspect the exact code behind a result and build from it.
  • Production execution algos built in. POV, TWAP, and open/close auctions — the same algorithms used in live trading, not approximations.
  • Multi-asset, multi-strategy. Equities, futures (continuous contracts with automatic rollover), and options down to 0DTE — together in one backtest, with TCA and PDF tearsheets out of the box.

The model in one minute

  • One class, callback methods. Implement only the events you care about — on_start, on_bar, on_order, and more.
  • Subscribe in on_start. The callback runs once per calendar day; repeated identical subscriptions are deduplicated. Guard any truly one-time setup with instance state.
  • Fills arrive in on_order — there is no separate fill callback; check order.is_filled.
  • Time is U.S. Eastern. ctx.now() and every timestamp are already in market time (ET) — no timezone math.
Install hiveq-sdk 1.0.2.1 · import root hiveq.flow · Python >=3.11. (Don't install the separate hiveq-flow engine package alongside it — same namespace.) This reference covers backtest authoring, reading results, and remote deploy. Live trading is out of scope here.

Choose your HiveQ surface

HiveQ uses related package and namespace names for different jobs. Pick one path before copying an example. For a normal strategy and backtest, stay in the first two columns: install hiveq-sdk, import hiveq.flow, and let the managed runtime execute the callbacks.

RUNS ON YOUR MACHINE

hiveq-sdk

The package researchers install. It provides login, dataset discovery, the Flow authoring API, submission, and result handles.

python -m pip install hiveq-sdk
AUTHOR LOCALLY

hiveq.flow

The strategy namespace inside hiveq-sdk. Define the class locally; run_backtest packages and submits it to HiveQ.

import hiveq.flow as hf
RUNS IN HIVEQ

Managed engine

ctx, event callbacks, entitled market data, fills, and analytics exist here. The full hiveq-flow engine package is platform-only.

def on_bar(self, ctx, event):
DIRECT DATA WORK

hiveq_data

A standalone client for discovery, historical queries, streams, and publishing outside a Flow strategy callback.

import hiveq_data as hd
Do not install hiveq-flow locally. That is the full engine distribution used by the managed executor and it shares the hiveq.flow namespace. A researcher installs only hiveq-sdk, unless an approved direct-data workflow explicitly calls for the separate hiveq_data package.

Data Access

Inside a HiveQ Flow strategy, declare data in data_configsand subscribe through ctx; the managed HiveQ engine retrieves it under the authenticated account's entitlements. Outside a strategy, use the dataset CLI for bounded discovery or the standalone hiveq_data client for an explicitly approved direct-data workflow. Never call hiveq_data from a strategy callback.

PUBLIC WORKFLOW

Declare the dataset.

Use the installed Flow reference and one confirmed dataset/schema in data_configs.

ACCOUNT BOUNDARY

Entitlements decide access.

Available datasets and schemas follow the authenticated HiveQ account—not a public catalog promise.

MANAGED EXECUTION

HiveQ retrieves the data.

The remote engine sources the declared data and preserves one reproducible run contract.

Auction imbalance feeds

Auction imbalances describe the buy/sell pressure around an exchange opening or closing auction. Use them for auction research and execution logic—not as ordinary intraday bars. When the authenticated account is entitled to this data, HiveQ exposes four logical HIVEQ_US_EQ schemas. Each delivers an ImbalanceData event to on_imbalance during the venue's available publication window:

python
{'type':'hiveq_historical','dataset':'HIVEQ_US_EQ','schema':['early_imbalance']}
{'type':'hiveq_historical','dataset':'HIVEQ_US_EQ','schema':['arca_imbalance']}
{'type':'hiveq_historical','dataset':'HIVEQ_US_EQ','schema':['nasd_imbalance']}
{'type':'hiveq_historical','dataset':'HIVEQ_US_EQ','schema':['nyse_imbalance']}
Use these logical schema names—never a physical database or table name. Confirm the account's fields, symbols, and date coverage with the installed metadata/catalog commands before choosing a research window. Venue-specific optional fields may be None. If the strategy places MOO, MOC, LOO, or LOC orders, also subscribe to the appropriate tick-by-tick trade schema: aggregated bars_* contain no auction print and cannot fill an auction order.

Standalone data client

SDK 1.0.2.1 also ships authoring stubs for the public standalone hiveq_data 0.2.9 surface. Use this only when your workflow explicitly needs direct historical discovery, publishing, or a live/replay stream outside a Flow strategy.

python
from hiveq_data import Historical, InstrumentReference, Publisher, LiveStream

ref = InstrumentReference()
futures = ref.get_futures(
    symbols=['ES.v.0', 'NQ.v.0'],
    start_date='2025-06-01', end_date='2025-06-30',
)

publisher = Publisher()   # async_mode=True by default
# publisher.publish(...)
publisher.flush()         # or publisher.close() before process exit

stream = LiveStream()
# stream.subscribe(..., keys=[...], replay=True, from_ts=..., to_ts=...)
stream.close()
The standalone client and HiveQ's managed Flow transport are separate surfaces. Do not substitute one for the other. The canonical signatures, operations, and error types are in the Data Reference appendix of the AI reference.

Install & Sign-in

bash
python -m pip install --upgrade "hiveq-sdk==1.0.2.1"
python -c "import importlib.metadata as m; print(m.version('hiveq-sdk'))"
hiveq login

hiveq login performs the supported one-time browser sign-in before dataset discovery or direct-data work. You may also skip that command and start a backtest; the first run_backtest opens the same browser flow automatically and waits while you sign in. Every later command reuses the saved access—there is no key to copy into code.

A HiveQ API key is the only credential, and the SDK provisions and manages it for you — never hard-code keys or set them by hand. Identity (user, org) is resolved from the key. The first-run wait while the browser is open is expected — sign in once and you're set.

Quickstart

This file contains both sides of the boundary. The class is authored on your machine; its callbacks and ctx execute in the managed HiveQ engine. The guarded main() stays on the client, submits the source through run_backtest, waits for the remote run, and reads the report.

python
import hiveq.flow as hf
from hiveq.flow import StrategyConfig
from hiveq.flow.config import AssetType
from hiveq.flow.logger import logger as _get_logger

logger = _get_logger()   # module-level — REQUIRED in every strategy

class BuyAndHold:
    def __init__(self):
        self.bought = False

    def on_start(self, ctx, event):
        ctx.subscribe_bars(ctx.strategy_config.symbols,
                           asset_type=AssetType.EQUITY, interval='1m')
        logger.info(f"[START] subscribed 1m bars for {ctx.strategy_config.symbols}")

    def on_bar(self, ctx, event):
        bar = event.data()                       # -> SigmaBar
        logger.debug(f"[BAR] {bar.symbol} {bar.time} close={bar.close:.2f} bought={self.bought}")
        if not self.bought and ctx.is_flat(bar.symbol):
            logger.info(f"[ENTRY] buying 100 {bar.symbol} at {bar.close:.2f}")
            ctx.buy_order(bar.symbol, quantity=100)
            self.bought = True

    def on_order(self, ctx, event):              # fills come HERE, not on_order_filled
        order = event.data()                     # -> SigmaOrder
        logger.info(f"[ORDER] {order.symbol} status={order.status} filled={order.is_filled}")
        if order.is_filled:
            fill = order.last_fill               # -> SigmaFill

def main():
    # run_backtest submits from the client and returns a Run HANDLE.
    run = hf.run_backtest(
        strategy_configs=[StrategyConfig(name='BuyAndHold', type='BuyAndHold')],
        symbols=['AAPL'],
        start_date='2025-08-01',
        end_date='2025-08-31',
        # the managed HiveQ engine retrieves this declared source
        data_configs=[{'type': 'hiveq_historical', 'dataset': 'HIVEQ_US_EQ',
                       'schema': ['bars_1m']}],
    )

    print(f"run {run.run_id} task {run.task_id}")
    run.wait(progress=False)                      # wait for the remote terminal state
    report = run.report()                         # -> PerformanceReport
    print(report.return_stats.to_string())

if __name__ == '__main__':
    main()
Four non-negotiables: StrategyConfig.type is the class name as a string (must match exactly), and run_backtest(...) returns a Run handle — call run.report() for results. Every strategy must also use the HiveQ logger throughout its callbacks and decision branches. Keep client submission behind the standard if __name__ == '__main__' guard so importing the bundled strategy source cannot submit another run.

Strategy Contract

A strategy is a plain Python class with per-event callback methods, each with the signature (self, ctx, event). Define only the handlers you need.

python
class MyStrategy:
    def __init__(self): ...               # per-strategy state
    def on_start(self, ctx, event): ...   # subscribe here
    def on_bar(self, ctx, event): ...     # event.data() -> SigmaBar
    def on_order(self, ctx, event): ...   # fills/rejects/cancels -> SigmaOrder
    def on_position(self, ctx, event):... # event.data() -> SigmaPosition
    def on_timer(self, ctx, event): ...   # event.data() -> TimerEventData
    def on_rollover(self, ctx, event):... # futures contract roll

Recognized callbacks

on_start, on_stop, on_bar, on_trade, on_quote, on_snap, on_order, on_position, on_timer, on_custom_data, on_index_price (alias on_index), on_rollover, on_imbalance, on_executor, on_security_event. Unknown method names are ignored. on_imbalance delivers an ImbalanceData from the auction imbalance feed. Configure early_imbalance, arca_imbalance, nasd_imbalance, or nyse_imbalance in data_configs; on_executor (EXECUTOR_EVENT) and on_security_event (SECURITY_EVENT) carry opaque payloads.

There is no on_order_filled — fills are delivered to on_order. Don't place orders in on_stop; the engine has already stopped and rejects them.

Required strategy instrumentation

Every strategy must create the HiveQ logger at module level and instrument every callback and decision branch. Use logger.debug(...) for per-bar state and condition checks; use logger.info(...) for signals, orders, and fills. The default engine level is WARNING, so healthy runs remain quiet even though the diagnostic context is already in the strategy.

python
from hiveq.flow.logger import logger as _get_logger
logger = _get_logger()   # module level — never logging.getLogger(__name__)

# Required coverage:
# on_start -> subscriptions/config (INFO)
# on_bar -> time, price, state, every condition outcome (DEBUG)
# order placement + on_order -> side, quantity, reason, status/fill (INFO)
Do not use logging.getLogger(__name__) or logging.basicConfig; the executor silences them. Do not leave a hiveq_log_level override in normal or delivered code — enable it only for a diagnostic re-run.

Order lifecycle contract (FIX-style: status ≠ events)

order.status is the order's state; events are the history. Terminal statuses (FILLED / CANCELED / REJECTED) are sticky — later request-level events never change them. The canonical race: you cancel a resting order but a fill lands while the cancel is in flight → you receive ORDER_FILLED first (the fill is never lost), then ORDER_CANCEL_REJECTED ("too late to cancel"). order.status reads FILLED on both.

  • Act on fills from the ORDER_FILLED event, using the cumulative filled_qty/leaves_qty (idempotent).
  • Treat ORDER_CANCEL_REJECTED / ORDER_MODIFY_REJECTED as informational no-ops when order.is_filled — do not count them toward reject/error limits.
  • ORDER_REJECTED is reserved for the order itself being rejected (entry rejects); it never fires for cancel/replace request rejections.
  • An order canceled after a partial fill ends CANCELED with filled_qty > 0.
  • Never infer disposition from the last event — use order.status / filled_qty.
There is no on_order_filled — fills arrive on on_order; check order.is_filled / order.status / order.last_fill.

Context API — ctx

Everything you do at runtime goes through ctx (a SigmaContext): subscribe to data, place & manage orders, query positions, read the clock, and run managed executors.

Subscriptions (call in on_start)

python
ctx.subscribe_bars(symbols, asset_type=AssetType.EQUITY, interval='1m')
ctx.subscribe_trades(symbols)     # tick prints  (asset_type defaults to EQUITY)
ctx.subscribe_quotes(symbols)     # bid/ask      (asset_type defaults to EQUITY)
ctx.subscribe_option_snaps('SPY', expiration_type='0dte')  # '0dte' | 'YYYY-MM-DD' | datetime

# futures bars: use the canonical symbol + interval API; one call per interval
ctx.subscribe_bars(['ES.v.0', 'NQ.v.0'], asset_type=AssetType.FUTURES, interval='1s')
ctx.subscribe_bars(['ES.v.0', 'NQ.v.0'], asset_type=AssetType.FUTURES, interval='1m')
ctx.subscribe_futures_trades(symbols=['ES.v.0'])

# custom / quant signals — data_id must match the 'id' in data_configs
ctx.subscribe_data(data_id='mysignals')   # -> on_custom_data
subscribe_trades and subscribe_quotes share the same symbol-keyed instrument registration. Repeating either—or calling both for the same symbol—is idempotent and does not duplicate events. Use exactly one overlapping tick schema per symbol in data_configs.
There is no ctx.subscribe_futures_quotes — futures bars use subscribe_bars(..., asset_type=AssetType.FUTURES), while trades retain their dedicated convenience method. For futures NBBO quotes use ctx.subscribe_quotes(['ES.v.0'], asset_type=AssetType.FUTURES). Continuous symbols use ROOT.roll.rank: .c.0 selects a calendar-based front contract and .v.0 selects a volume-based front contract. Both roll rules are supported for every root; choose the rule that matches the strategy rather than inferring it from the product.

Order placement

python
ctx.buy_order(symbol, quantity, order_type=OrderType.LIMIT, limit_price=px)
ctx.sell_order(symbol, quantity)         # exit long
ctx.short_order(symbol, quantity, time_in_force='DAY')  # open short

# sizing helpers (off the current net position)
ctx.close_position(symbol)               # flatten one symbol (no-op if flat)
ctx.order_to_target(symbol, 100)         # signed target: +long / -short / 0
ctx.flatten_all()                        # close every position in this strategy
Only the three sizing helpers (close_position / order_to_target / flatten_all) are idempotent — they skip if a working order already exists for the symbol, so they're safe to call every bar. buy_order/sell_order/short_order have no such guard — calling them every bar stacks orders. Round your own limit_price/stop_price to the instrument tick with adjust_tick_size(symbol, px).

Order management & queries

python
ctx.cancel_order(order_id)
ctx.modify_order(order_id, limit_price=..., stop_price=...)
ctx.cancel_all_orders(symbol)

ctx.net_position(symbol)   # signed float (+long / -short / 0)
ctx.is_flat(symbol)        # bool
ctx.has_open_order(symbol) # bool
ctx.now()                  # ET datetime   (time is ALWAYS U.S. Eastern)
ctx.set_timer('poll', timedelta(minutes=1))   # fires on_timer

Executors — managed order working (POV / TWAP / VWAP / AUCTION)

An executor is a server-side algo that owns a target's full lifecycle — slicing the parent quantity, repricing, replacing/cancelling, and aggregating fills. Reach for one when execution quality matters (large sliced orders, live order-chasing, auction routing); for a simple one-shot market order, a direct buy_order is simpler.

python
params = ctx.build_executor_params(
    symbol='AAPL', quantity=1000, side='BUY',
    executor_type='POV', participate_pct=10,   # work at 10% of volume
)
handle = ctx.add_executor(params)              # start it (None if it failed)
ctx.executor_state(handle)                     # "PARTIALLY_FILLED" | "FILLED" | ...

# re-target IN PLACE — never add a second executor for the same target:
eid = str(handle.executorID)
ctx.replace_executor_params_by_id(eid, new_params)
Executors need a tick stream, not bars — subscribe with ctx.subscribe_trades(...) (schema eq_trades/fut_trades). Keep one handle per target.

Events & Payloads

Each callback receives an event; call event.data() for the payload. The concrete type depends on event.type:

python
event.type        # EventType  (branch on this)
event.data()      # payload object (type depends on event.type)
event.time        # ET datetime
event.ts_event    # int nanoseconds

Payload types

  • SigmaBarsymbol, open, high, low, close, volume, interval, time
  • SigmaOrder — status/fills plus is_sell_short, is_executor_order, strategy_id, router_id
  • SigmaFillorder_id, leaves_qty, order_qty, exec_type, market_center plus execution/price/fee fields (via order.last_fill)
  • SigmaPosition — P&L plus last_price, long_notional, short_notional, max_exposure, total_fill_qty
  • SigmaTradeTick — normalized exchange/market_center/condition plus raw venue/condition fields
  • SigmaQuoteTick — bid/ask plus normalized exchange fields, condition, and is_valid
  • SigmaSnapData — options snapshot (strike, option_type, bid_px, ask_px, is_valid)
  • SigmaCustomData — custom/CSV rows; read columns with column_data(name) (values are strings — cast yourself; quant signals arrive as a signal_json column)
  • Rollovercontinuous_symbol, prev_contract, current_contract
  • ImbalanceData — auction imbalance record from early_imbalance, arca_imbalance, nasd_imbalance, or nyse_imbalance: symbol, side (normalized venue state), imbalance (signed for NYSE), paired_shares, ref_price?, near_price?, far_price?, clearing_price? (NYSE), cont_book_clearing_price?, closing_only_clearing_price?, market_imbalance? (Arca), cross_type? ('O'/'C'/'A', Nasdaq), transaction_type?, exchange?, ts_event, ts_init, time?/time_utc? — venue-specific optional fields are None when not meaningfully published

Results & Reports

run_backtest(...) returns a Run handle — your single accessor for status and results, local or remote.

python
run.report()          # -> PerformanceReport
run.positions()       # positions over time   (DataFrame)
run.orders()          # complete order history (DataFrame)
run.trades()          # executed trades        (DataFrame)
run.fills()           # executed order fills   (DataFrame)
run.daily_returns()   # daily P&L              (DataFrame)
run.tearsheet()                      # -> '<run>.pdf' in the cwd (PDF, the default)
run.tearsheet(output='report.html')  # explicit HTML file (format from the extension)

# silent=True is the DEFAULT — run_backtest deploys and returns the Run immediately:
run = hf.run_backtest(...)
run.wait(progress=False)                          # block quietly until terminal (no tqdm bar)
report = run.report()
# reattach later from any machine (results outlive your Python process):
report = hf.get_run(run.run_id).wait(progress=False).report()
run_backtest(...) always returns a Run handle, never a bare report. silent=True is the default (deploy and return immediately); silent=False blocks with a live progress bar (interactive only). Calling run.wait() without progress=False renders a tqdm bar that pollutes captured/scripted output — always pass progress=False in scripted or agent-driven runs.
On a healthy run, run.report() is the only output you need. Do not poll status, stream progress, or fetch run.logs() / run.event_logs() routinely. If a run misbehaves, re-run once with config={'hiveq_log_level': 'DEBUG'}, inspect run.logs(), then remove the override before delivering the strategy.

PerformanceReport

python
report = run.report()
print(report.return_stats.to_string())   # Sharpe, Sortino, vol, drawdown, win rate
total_trades = report.stats.get('Total Trades', 0)  # one metric by name

stats = report.summary_stats()           # dict: Sharpe, CAGR, Max Drawdown, ...
html = report.create_tearsheet()         # inline HTML string (Jupyter/Marimo) — to save a file use run.tearsheet()
Report DataFrames (fills, orders, …) can be None/empty — always guard before use. Tearsheets and summary_stats() are powered by quantstats, bundled with the SDK. Reader endpoints default to a 100k-row limit (raise hiveq_data_page_size for very dense schemas like options), so long backtests aren't silently truncated.
For remote runs, run.report() backfills positions, orders, and trades from their dedicated endpoints. run.fills() and report.fills are derived from executed orders, so all four report tables match their corresponding run.*() accessors.
A completed backtest with zero trades is not a deliverable. Check report.stats.get('Total Trades', 0); if it is zero or implausibly low, diagnose the entry logic and re-run until the strategy trades realistically within the requested window.

Function Registry

A versioned store of reusable Python callables that live on the platform. Write a function once, push_function it (with its requirements and a semver version), then fetch and run it from any script or machine by name and version.

python
import hiveq.flow as hf

def zscore(series, window=20):
    """Rolling z-score."""
    import numpy as np
    return (series[-1] - np.mean(series[-window:])) / np.std(series[-window:])

# register once with your chosen semantic version...
FUNCTION_VERSION = "MAJOR.MINOR.PATCH"
hf.push_function(zscore, version=FUNCTION_VERSION, requirements=["numpy"])

# ...then load it back and run it on the platform from anywhere:
fn = hf.load_function("zscore", version=FUNCTION_VERSION)
hf.run_function(fn, [1, 2, 3, 4, 5], window=3, requirements=["numpy"])  # -> value
Access control is still TBD. Functions live in your own namespace by default; namespace="default" is the shared/public namespace. Treat cross-namespace permissions as subject to change.

Markets & Examples

Equities, futures (incl. continuous contracts with automatic rollover), options, and your own custom data feeds. Each is demonstrated by a complete, runnable example in the SDK's examples/ directory.

Equities
intraday_momentum_equity.py
SMA crossover, per-symbol state, EOD-flat
Futures
futures_continuous_rollover.py
continuous ES.v.0, enable_auto_rollover, on_rollover
Options (0DTE)
options_0dte_iron_condor.py
subscribe_option_snaps, multi-leg structure
Custom data
custom_data.py
bring-your-own CSV feed → on_custom_data
Orders & brackets
bracket_stop_take_profit.py
stop+target children, OCO emulation
Executors
executor_pov_sliced.py
POV executor, re-target in place
Pairs / stat-arb
pairs_stat_arb.py
rolling z-score spread, short_order
Scheduling
timers_scheduling.py
set_timer + ctx.now() ET checks
python
# Futures: subscribe to a continuous contract; rollover is automatic.
class Breakout:
    def on_start(self, ctx, event):
        ctx.subscribe_bars(['ES.v.0'], asset_type=AssetType.FUTURES, interval='1m')
    def on_rollover(self, ctx, event):
        roll = event.data()   # Rollover: prev_contract -> current_contract

run = hf.run_backtest(
    strategy_configs=[StrategyConfig(name='Breakout', type='Breakout')],
    symbols=['ES.v.0'],
    start_date='2024-01-01', end_date='2024-03-01',
    data_configs=[{'type': 'hiveq_historical', 'dataset': 'HIVEQ_US_FUT',
                   'schema': ['bars_1m']}],
    backtest_config=BacktestConfig(enable_auto_rollover=True),
)

Enums & Config

Imports cheat-sheet

python
import hiveq.flow as hf
from hiveq.flow import StrategyConfig, BacktestConfig, EngineConfig, AssetType, get_run
from hiveq.flow.config import EventType, EventLogType, OMSType
from hiveq.flow.logger import logger as _get_logger
from hiveq.flow.trading_types import OrderType, OrderSide, OrderStatus, MarketCenter
from hiveq.flow.trading.price_utils import adjust_tick_size, get_min_tick

Key enums

  • AssetType: EQUITY · OPTIONS · FUTURES · CRYPTO · INDEX
  • OrderType: MARKET · LIMIT · STOP · STOP_LIMIT · MOO · MOC · LOO · LOC
  • OrderSide: BUY · SELL · OrderStatus: PENDING · SUBMITTED · ACCEPTED · REJECTED · CANCELED · FILLED · PARTIALLY_FILLED

BacktestConfig (key fields)

python
BacktestConfig(
    initial_capital=1_000_000.0,
    commission=0.001,
    equity_fee=0.0011,        # per share
    futures_fee=0.5,          # per contract
    session_start=None,       # asset default: equity 04:00, futures 18:00 ET
    session_end=None,         # asset default: equity 18:30, futures 17:00 ET
    enable_auto_rollover=False,
    auto_flatten_at_close=False,  # force-close non-option positions at session close
    enable_tca=False,
)
The default equity window intentionally runs through 18:30 ET. Keep the session open beyond 16:00 so resting MOC and closing-cross orders can fill against the official close print.

Pitfalls & Patterns

HiveQ Flow is deliberately lean on authoring conveniences — these are the things that are easy to get wrong, and the idioms to handle them.

  • No built-in indicators or history buffer — keep your own collections.deque window and compute with numpy/pandas.
  • Each run has a 2 GB memory limit. Keep per-event state bounded with deque(maxlen=N), running counters, or last-value maps; never accumulate every bar, order, fill, payload, or a growing DataFrame on self. The engine already records those artifacts for post-run access.
  • No native brackets/OCO — place protective child orders on fill in on_order and cancel siblings yourself.
  • No percent-of-equity sizing — size in fixed quantity (or off ctx.portfolio().equity and a price you track).
  • Timestamps ts_event/ts_init are nanoseconds — use .time / ctx.now() for datetimes.
  • Set symbols/start_date/end_date in one place (top-level args OR BacktestConfig), not both.
  • On a continuous-futures subscription, bar.symbol is the resolved outright contract (e.g. ESZ5), never the ES.v.0 string you subscribed with — so if bar.symbol != 'ES.v.0' silently drops every bar. Key off the root, or place orders on bar.symbol directly.
  • End-of-day flatten is opt-in, not automatic — set BacktestConfig(auto_flatten_at_close=True) to force-close non-option positions at session close (options settle automatically regardless).

Bracket / stop-loss + take-profit

python
def on_order(self, ctx, event):
    o = event.data()
    if o.is_filled and o.symbol == self.entry_symbol and not self.protected:
        entry = o.avg_px
        ctx.sell_order(o.symbol, quantity=o.filled_qty,
                       order_type=OrderType.STOP,  stop_price=entry * 0.98)
        ctx.sell_order(o.symbol, quantity=o.filled_qty,
                       order_type=OrderType.LIMIT, limit_price=entry * 1.04)
        self.protected = True
    # when one leg fills, cancel_all_orders(symbol) to emulate OCO

Ready to prove your alpha?

Build, optimize, deploy, and scale — on institutional-grade infrastructure.

Get Early Access