HiveQ Docs
HiveQ Flow

AlgoInstructionStrategy

AlgoInstructionStrategy Specification

Version: 1.1.0 Date: 2026-03-27 Status: Draft

Executor-based algorithmic trading strategy that receives per-instruction algo params via userData (CSV signals) and manages entry/exit/risk executors per symbol. Supports position and signal modes, broker locate waterfall for short selling, and futures rollover.


Strategy Configuration

Passed via StrategyConfig.params:

StrategyConfig(
    name='my_algo',
    type='AlgoInstructionStrategy',
    params={
        # --- Data Sources ---
        'userDataList': 'signal_file_1,signal_file_2',  # Comma-separated userData source IDs
        'algoConfigEventID': '',                          # Algo config file source ID (see Algo Config File section)
        'universeFile': '',                               # Path to universe file for symbol loading
        'assetType': 'FUTURES',                           # Asset class: "FUTURES" or "EQUITY"

        # --- Entry Time Window ---
        'entryBeginTime': '09:30:00',   # Entry window start (HH:MM:SS)
        'entryEndTime': '15:45:00',     # Entry window end

        # --- Exit Time Window ---
        'exitBeginTime': '15:45:30',    # Exit window start
        'exitEndTime': '16:00:00',      # Exit window end

        # --- Risk Time Window ---
        'riskBeginTime': '09:30:00',    # Risk window start
        'riskEndTime': '15:45:25',      # Risk window end (typically before exit window)

        # --- Market Hours ---
        'marketOpenTime': '09:30:00',   # Market open
        'marketCloseTime': '16:00:00',  # Market close

        # --- Global Executor Defaults ---
        'entryExecutorType': 'POV',               # Default entry executor type
        'exitAlgo': 'POV',                         # Default exit executor type
        'participatePercentage': 10.0,             # Default entry POV participation (%)
        'exitParticipatePercentage': 10.0,         # Default exit POV participation (%)
        'aggressivePriceMultiplier': 1.0,          # Aggressive limit price multiplier
        'executorNbboSizePct': 100.0,              # NBBO quote size percentage

        # --- Order Size Limits ---
        'minOrderSize': 100,            # Minimum order quantity
        'maxOrderSize': 1000000,        # Maximum order quantity
        'minOrderNotional': 1000.0,     # Minimum order notional ($)
        'maxOrderNotional': 10000000.0, # Maximum order notional ($)
        'exchangeMinOrderNotional': 5,  # Exchange minimum notional

        # --- Timing ---
        'refreshMillis': 100,                    # Entry evaluation throttle (ms)
        'exitRefreshIntervalInMillis': 30000,    # Exit evaluation throttle (ms)
        'executorRefreshMillis': 300,             # Executor internal refresh (ms)

        # --- Position Scaling ---
        'mm': 1.0,        # Market multiplier applied to target position
        'mmReset': 1.0,   # Market multiplier reset value

        # --- Feature Flags ---
        'enableExit': True,                         # Enable automatic exit window execution
        'disableExit': False,                        # Force-disable exit execution
        'disableTradingWindows': False,              # Ignore time windows entirely
        'enableShortSell': True,                     # Allow short selling
        'ignoreETB': False,                          # Skip ETB locate checks
        'reduceEntryPosition': True,                 # Allow reducing position on new instruction
        'enableDynamicSymbolRegistration': False,     # Register unknown symbols at runtime
        'enableFuturesRollover': False,              # Auto-transfer positions on contract rollover
        'longSellNotional': 0,                       # Notional cap for short positions ($)
    }
)

Signal File (userData CSV) Format

date,time,sym,ticker,desiredpos,signal1,weight1,locate_id,desk_qty,algo_config_id,exit
ColumnTypeDescription
datestringDate in YYYY-MM-DD format
timestringTime in HH:MM:SS.mmm format
symstringSymbol (fallback if ticker is empty)
tickerstringPrimary symbol identifier
desiredposfloatTarget position (position mode)
signal1floatSignal value (signal mode)
weight1floatSignal weight for position sizing
locate_idstringBroker ID for ETB locate allocation
desk_qtyintLocate shares for short selling
algo_config_idstringReference to a named config from the algo config file (see Algo Config File)
exitstringSet to 1/true to trigger exit instruction (for no-window mode)

Additional columns recognized: user_shortname, broker_id, locate_qty, risk_qty


Instruction Type Auto-Detection

The instruction type is auto-detected from the columns present, evaluated in this order:

PriorityTypeDetection Rule
1etblocate_id, user_shortname, or broker_id is set, OR event_id contains "etb"
2positiondesiredpos has a non-zero value
3signalsignal1 and/or weight1 have values
4riskrisk_qty > 0 column is present on the signal row
5exitexit column is 1/true/yes on the signal row

Note: Risk is detected from the risk_qty column. Exit is triggered via the exit column. The executor config for risk/exit is resolved from the algo config file — the signal row only needs the trigger column (risk_qty or exit).

Signal-to-Position Conversion

When instruction_type == 'signal':

desired_pos = signal1 * weight1 * (capital / (ref_price * multiplier))

algo_params Column Specification

The algo_params column configures per-instruction executor behavior across three slots: entry, exit, and risk. Each slot can have its own executor type and parameters.

Supported Formats

entry=POV;entry_participatePercentage=10;exit=MOC;risk=TWAP;risk_qty=100

Syntax rules:

  • Bare slot name sets executor type: entry=POV, exit=MOC, risk=TWAP
  • Slot-prefixed params: entry_<param>=<value>, exit_<param>=<value>, risk_<param>=<value>
  • Executor-type prefixed params: pov_<param>=<value>, twap_<param>=<value> (routed to the matching slot)
  • Unprefixed params default to the entry slot

2. Nested JSON

{
  "entry": { "executor_type": "POV", "participatePercentage": "10" },
  "exit": { "executor_type": "MOC" },
  "risk": { "executor_type": "TWAP", "qty": "100" }
}

Slot values can be a string (executor type only) or a dict (executor type + params):

{ "entry": "POV", "exit": { "executor_type": "AUCTION", "orderType": "MOC" } }

3. Flat JSON (Legacy)

{
  "executorType": "POV",
  "participatePct": 10,
  "exitExecutorType": "AUCTION",
  "exitParticipatePct": 20
}

Executor Types

TypeDescription
POVPercentage of Volume
TWAPTime-Weighted Average Price
VWAPVolume-Weighted Average Price
PASSIVEPassive limit orders
AUCTIONAuction orders (MOC/MOO)
POV_PASSIVEHybrid POV + Passive
ALGO_COBRAAlgo variant: Cobra
ALGO_TWAPAlgo variant: TWAP
ALGO_VWAPAlgo variant: VWAP
MOCMarket-on-Close (shorthand for AUCTION + orderType=MOC)
MOOMarket-on-Open (shorthand for AUCTION + orderType=MOO)

All Supported algo_params Parameters

Each parameter below can be set per-slot using the prefix convention (e.g., entry_participate_pct, exit_order_type, risk_start_time).

Participation & Pricing

ParameterAliasesTypeDefaultDescription
participatePercentageparticipate_pct, pov, pov_percentage, participatepctfloat0.0 (falls back to strategy default)Participation rate as percentage of volume
aggressivePriceMultiplieraggressive_mult, aggrfloat0.0 (falls back to strategy default)Multiplier for aggressive limit price calculation
executorNbboSizePctnbbo_size_pct, nbbofloat100.0NBBO quote size percentage used for sizing

Order Configuration

ParameterAliasesTypeValuesDescription
orderTypeorder_type, otypestringLIMIT, MARKET, MOC, MOOOrder type for the executor
timeInForcetif, time_in_forcestringDAY, GTX, GTC, IOCTime-in-force for orders
marketCentermarket_center, mcstringExchange codeTarget trading venue / market center
accountacctstringBroker account IDBroker account for order routing

Time Control

ParameterAliasesTypeFormatDescription
startTimestart_timestringHH:MM:SSExecutor start time (overrides strategy window)
endTimeend_timestringHH:MM:SSExecutor end time (overrides strategy window)
durationstring30s, 5m, 1h, 2h30mDuration from start (alternative to endTime)

Duration format: Supports h (hours), m (minutes), s (seconds) suffixes and combinations. Plain integer is treated as seconds. Examples: 30s, 5m, 1h, 2h30m, 1h30m45s, 300.

Risk Slot Specific

ParameterTypeDescription
qtyintRisk order quantity (only valid in the risk slot)

Custom FIX Parameters

ParameterTypeDescription
custom_fix_<tag>stringCustom FIX protocol tag-value pair. <tag> is the integer FIX tag number.

Example: custom_fix_5700=MyAlgo;custom_fix_5701=aggressive


Full algo_params Examples

Position Entry with POV

entry=POV;entry_participatePercentage=15;entry_aggressive_mult=1.2

Buy/sell to target position using 15% POV with 1.2x aggressive pricing.

Entry + Timed Exit

entry=TWAP;entry_start_time=09:35:00;entry_end_time=10:00:00;exit=MOC

TWAP entry between 09:35-10:00, close position at market-on-close auction.

Entry with Duration

entry=VWAP;entry_duration=30m;exit=AUCTION;exit_order_type=MOC

VWAP entry over 30 minutes from instruction time, MOC exit.

Multi-Slot with Risk

entry=POV;entry_participatePercentage=10;exit=TWAP;exit_participatePercentage=20;risk=POV;risk_qty=50;risk_participatePercentage=100

POV entry at 10%, TWAP exit at 20%, POV risk order for 50 shares at 100%.

Venue and Account Routing

entry=PASSIVE;entry_market_center=ARCA;entry_account=BROKER_A;entry_tif=DAY;entry_nbbo_size_pct=50

Passive limit orders on ARCA via BROKER_A account, DAY TIF, using 50% of NBBO size.

Custom FIX Tags

entry=POV;entry_participatePercentage=10;entry_custom_fix_5700=StrategyTag;entry_custom_fix_5701=aggressive

POV entry with custom FIX tags 5700 and 5701.

Nested JSON with All Parameters

{
  "entry": {
    "executor_type": "POV",
    "participatePercentage": "15",
    "aggressivePriceMultiplier": "1.5",
    "orderType": "LIMIT",
    "timeInForce": "DAY",
    "marketCenter": "ARCA",
    "account": "PRIME_1",
    "nbbo_size_pct": "75",
    "startTime": "09:35:00",
    "endTime": "11:00:00",
    "custom_fix_5700": "MyTag"
  },
  "exit": {
    "executor_type": "AUCTION",
    "orderType": "MOC"
  },
  "risk": {
    "executor_type": "POV",
    "participatePercentage": "100",
    "qty": "200"
  }
}

ETB Locate Row

2025-09-16,04:00:00.000,AAPL,AAPL,,,,BROKER_A,5000,entry=POV;entry_participatePercentage=10

Allocates 5000 shares of AAPL locate from BROKER_A. Locates are persistent ceiling (only increases).

Signal Mode Row

2025-09-01,18:42:11.000,ES.c.0,ES.c.0,,0.00329,1,,,entry=POV;entry_participatePercentage=10

Signal=0.00329, weight=1. Position computed as signal * weight * (capital / (price * multiplier)).


Executor Slot Lifecycle

Each symbol maintains 3 independent executor slots:

SlotPurposeTriggered By
entryOpen or adjust position toward targetPosition/signal instruction during entry window
exitClose position at end of dayExit window start, or explicit exit instruction
riskEmergency position reductionRisk instruction with risk_qty

States: RUNNINGSTOPPINGSTOPPED

  • Active executors are stopped before replacement
  • Stopped executors are cleared via the executor state callback
  • All slots reset at start of each trading day (broker locates persist)

Operating Modes: Window Mode vs Window-Disabled Mode

The strategy operates in two fundamentally different modes depending on the disableTradingWindows flag (also triggered by disableExit).

Window Mode (disableTradingWindows=False, default)

Time-driven execution with automatic transitions between entry, exit, and risk phases.

How it works:

  1. Entry window (entryBeginTimeentryEndTime): Entry executors run. New position/signal instructions are accepted and evaluated each cycle.
  2. Entry window closes: All non-auction entry executors are stopped automatically.
  3. Exit window (exitBeginTimeexitEndTime): If enableExit=True, exit is triggered automatically for every symbol with a position. Entry is stopped before exit starts.
  4. Exit window closes: All exit executors are stopped.
  5. Risk window (riskBeginTimeriskEndTime): Risk instructions are evaluated whenever the risk window is active (can overlap with entry/exit windows, but mutual exclusion still applies).

Key behaviors in window mode:

  • exitAlgo (global exit executor type) is required — logged as warning if not set
  • Exit uses the global exitAlgo as fallback if no per-instruction exit config is provided
  • Entry evaluation is skipped outside the entry window
  • Exit evaluation is skipped outside the exit window
  • Risk instructions received outside the risk window are deferred until the window opens
  • Overnight windows are supported (e.g., entryBeginTime=17:45:00, entryEndTime=15:59:00 spans midnight)

Typical timeline:

09:30          15:45  15:45:30      16:00
  |--- entry ---|      |--- exit ---|
  |--- risk window --|

Risk window typically ends before the exit window opens (e.g., riskEndTime=15:45:25) since risk and exit cannot run concurrently — risk blocks exit and vice versa. Configuring them to overlap is allowed but the mutual exclusion rules mean only one will be active at a time.

Window-Disabled Mode (disableTradingWindows=True)

Instruction-driven execution with no automatic time transitions. Every action must be explicitly triggered by an instruction.

How it works:

  • Time windows are ignored — entry, exit, and risk can execute at any time
  • Entry runs continuously whenever there is a position delta
  • Exit requires an explicit exit instruction with per-instruction executor config (no global fallback)
  • Risk evaluates immediately (no window gating)
  • Note: setting disableExit=True also activates this mode as a side effect — it disables all time windows, not just exit

Key behaviors in window-disabled mode:

  • No automatic entry stop at end of day
  • No automatic exit trigger — exit only happens via explicit exit instruction
  • Exit instructions require per-instruction exit config via the algo config file (global exitAlgo fallback is not used)
  • Manual exit with auction type (MOC/MOO): placed immediately alongside active entry (no need to stop entry first — the auction order does not compete with the entry executor for market liquidity)
  • Manual exit with non-auction type: entry and risk are stopped first, then exit triggers. If exit can't start immediately (e.g., risk still stopping), it's marked as pending and retried each evaluate cycle
  • Entry and risk run on every evaluate cycle without time checks

When to use each mode:

ModeUse Case
Window modeIntraday strategies with defined entry/exit periods (e.g., enter 09:30-15:45, MOC exit)
Window-disabledEvent-driven strategies, manual trading, strategies that manage their own timing via startTime/endTime in the algo config file

Window Mode Examples

Strategy config:

params={
    'entryBeginTime': '09:30:00',
    'entryEndTime': '15:45:00',
    'exitBeginTime': '15:45:30',
    'exitEndTime': '16:00:00',
    'riskBeginTime': '09:30:00',
    'riskEndTime': '15:45:25',
    'entryExecutorType': 'POV',
    'exitAlgo': 'MOC',
    'enableExit': True,
    'disableTradingWindows': False,
}

Timeline:

09:30          15:45  15:45:30      16:00
  |--- entry ---|      |--- exit ---|
  |--- risk window --|

Example 1: Simple entry, auto exit at MOC

timeCSV rowwhat happens
09:352025-09-16,09:35:00.000,AAPL,AAPL,500,,,,, entry=POV;entry_participatePercentage=10Entry starts: POV buying toward 500 shares at 10% participation
15:45(automatic)Entry window closes, entry executor stopped
15:45:30(automatic)Exit window opens, MOC exit triggered automatically (uses global exitAlgo=MOC)
16:00(automatic)Auction fills, position flat

Example 2: Entry with timed risk, then auto exit

timeCSV rowwhat happens
10:002025-09-16,10:00:00.000,AAPL,AAPL,1000,,,,,entry=POV;entry_participatePercentage=15;risk=POV;risk_qty=500;risk_start_time=14:00:00;risk_participatePercentage=100Entry starts: POV buying toward 1000 shares. Risk stored, deferred until 14:00
14:00(risk triggers)Entry stopped. Risk starts: POV selling 500 shares at 100% participation
14:02(risk completes)Risk done. Position reduced to ~500 shares. Entry does NOT restart (risk flag set)
15:45:30(automatic)Exit window opens, MOC exit triggered for remaining ~500 shares

Example 3: Multiple position updates during entry window

timeCSV rowwhat happens
09:35...,AAPL,AAPL,300,,,,,entry=POV;entry_participatePercentage=10Entry starts toward 300 shares
11:00...,AAPL,AAPL,600,,,,,entry=POV;entry_participatePercentage=10Target updated to 600, entry executor target adjusted (delta recalculated)
13:00...,AAPL,AAPL,200,,,,,entry=POV;entry_participatePercentage=10Target reduced to 200, entry executor reverses direction (sells excess)
15:45:30(automatic)MOC exit triggered for remaining 200 shares

Window-Disabled Mode Examples

Strategy config:

params={
    'entryExecutorType': 'POV',
    'disableTradingWindows': True,
    # exitAlgo not needed — exit must be explicit per-instruction
}

No timeline diagram — there are no windows. Everything is driven by instructions arriving at any time.

Example 1: Entry then explicit MOC exit (auction overlap)

timeCSV rowwhat happens
09:352025-09-16,09:35:00.000,AAPL,AAPL,500,,,,,entry=POV;entry_participatePercentage=10Entry starts: POV buying toward 500 shares
15:302025-09-16,15:30:00.000,AAPL,AAPL,0,,,,,exit=MOCMOC exit placed alongside active entry (auction overlap — entry keeps running)
16:00(auction fills)Position flat

Note: The exit instruction has desiredpos=0 and an exit column set, so it is detected as type exit.

Example 2: Entry then explicit POV exit (non-auction)

timeCSV rowwhat happens
09:352025-09-16,09:35:00.000,AAPL,AAPL,500,,,,,entry=POV;entry_participatePercentage=10Entry starts toward 500 shares
14:002025-09-16,14:00:00.000,AAPL,AAPL,0,,,,,exit=POV;exit_participatePercentage=20Entry and risk stopped first, then POV exit starts at 20% participation to flatten

Note: Non-auction exit stops entry before starting (unlike auction exit which can overlap).

Example 3: Entry with duration-based timing

timeCSV rowwhat happens
10:002025-09-16,10:00:00.000,AAPL,AAPL,1000,,,,,entry=VWAP;entry_duration=2hVWAP entry starts, executor runs from 10:00 to 12:00
12:00(executor time expires)Entry executor stops after 2-hour duration
15:302025-09-16,15:30:00.000,AAPL,AAPL,0,,,,,exit=AUCTION;exit_order_type=MOCExplicit MOC exit to flatten at close

Example 4: Entry, risk, then exit — all instruction-driven

timeCSV rowwhat happens
09:352025-09-16,09:35:00.000,AAPL,AAPL,1000,,,,,entry=POV;entry_participatePercentage=10Entry starts toward 1000 shares
11:002025-09-16,11:00:00.000,AAPL,AAPL,0,,,,,risk=POV;risk_qty=400;risk_participatePercentage=100Entry stopped immediately. Risk starts: sell 400 shares at 100%. desiredpos=0 with risk config → detected as type risk
11:05(risk completes)Position reduced to ~600 shares. Entry blocked (risk flag set)
15:302025-09-16,15:30:00.000,AAPL,AAPL,0,,,,,exit=MOCMOC exit placed for remaining ~600 shares

Example 5: New entry after risk resets the flag

timeCSV rowwhat happens
09:35...,AAPL,AAPL,1000,,,,,entry=POV;entry_participatePercentage=10Entry starts toward 1000
11:00...,AAPL,AAPL,0,,,,,risk=POV;risk_qty=500;risk_participatePercentage=100Entry stopped, risk sells 500 shares
11:05(risk completes)~500 shares remaining. Entry blocked by risk flag
13:00...,AAPL,AAPL,800,,,,,entry=POV;entry_participatePercentage=10New entry instruction resets risk flag. Entry resumes toward new target of 800 shares
15:30...,AAPL,AAPL,0,,,,,exit=MOCMOC exit to flatten

What Makes AlgoInstructionStrategy Different

Unlike simple signal-to-order strategies, AlgoInstructionStrategy manages overlapping executor slots with strict mutual exclusion rules. Entry, exit, and risk executors cannot run concurrently for the same symbol — the strategy enforces a priority-based handoff protocol that ensures orderly transitions between execution phases.

Slot Mutual Exclusion Rules

The three slots follow these mutual exclusion constraints:

Running SlotBlocked SlotsBehavior
entry active/stoppingrisk cannot startRisk waits (retries each evaluate cycle) until entry fully stops
risk active/stoppingentry skipped, exit skippedEntry evaluation returns immediately; exit trigger returns immediately
risk active/stopping (full flatten)exit stoppedIf risk qty >= current position, exit is stopped and marked as triggered
exit triggeredentry blockedEntry will not start if exit has already been triggered (auction overlap case)

Key Principle: Risk Always Wins

When a risk instruction arrives while entry is running:

  1. Entry executor is immediately stopped
  2. Risk instruction is stored on the risk slot
  3. Risk executor waits for entry to fully transition from STOPPING → STOPPED (cleared via the executor state callback)
  4. Once entry is cleared, risk executor starts on the next evaluate cycle
  5. After risk executes, the risk_triggered flag is set — entry will not restart unless a new entry instruction arrives and resets the flag

Workflow Combinations

Below are all supported execution workflows for a single symbol within a trading day:

1. Entry Only

entry(RUNNING) → entry(STOPPED)

Position instruction arrives during entry window. Entry executor runs to target, stops when delta < 1.

2. Entry → Exit

entry(RUNNING) → entry(STOPPED) → exit(RUNNING) → exit(STOPPED)

Entry runs during entry window. When exit window opens (or in window-disabled mode with an explicit exit instruction), entry is stopped (if still active) and exit executor starts to flatten the position.

3. Entry → Risk (Partial)

entry(RUNNING) → entry(STOPPING) → entry(STOPPED) → risk(RUNNING) → risk(STOPPED)

Risk instruction arrives while entry is running. Entry is stopped immediately. Risk executor waits for entry to fully stop, then executes for risk_qty shares (partial reduction). Entry does not restart after risk — the risk_triggered flag blocks it until a new instruction.

4. Entry → Risk (Full Flatten)

entry(RUNNING) → entry(STOPPING) → entry(STOPPED) → risk(RUNNING) → risk(STOPPED)

Same as partial risk, but risk_qty >= current_position. Additionally, exit is stopped (if running) and marked as triggered to prevent exit from starting after risk completes.

5. Entry → Exit → Risk

entry(RUNNING) → entry(STOPPED) → exit(RUNNING) → exit(STOPPED) → risk(RUNNING) → risk(STOPPED)

Entry completes, exit begins during exit window. A risk instruction arrives — exit continues until risk is ready. Exit evaluation is skipped while risk is active/stopping. If risk arrives as a new standalone instruction, it does not interrupt exit directly but blocks exit re-evaluation while risk is active.

6. Entry → Risk → Exit

entry(RUNNING) → entry(STOPPED) → risk(RUNNING) → risk(STOPPED) → exit(RUNNING) → exit(STOPPED)

Entry completes, risk triggers (e.g., timed risk via risk_start_time). Exit is blocked while risk is active/stopping. After risk completes (partial reduction), if there is remaining position and exit window is active, exit starts to flatten the rest.

7. Risk Only (No Entry)

risk(RUNNING) → risk(STOPPED)

Standalone risk instruction with no prior entry. Entry is stopped if active. Risk executes to reduce or flatten the existing position.

8. Entry → Risk (Partial) → Exit

entry(RUNNING) → entry(STOPPED) → risk(RUNNING) → risk(STOPPED) → exit(RUNNING) → exit(STOPPED)

Entry runs to target. Risk reduces position partially. Exit then flattens remaining position during exit window.

9. Exit Only

exit(RUNNING) → exit(STOPPED)

No entry instruction — position already exists. Exit runs during exit window to flatten.

10. New Entry Resets Risk

entry(RUNNING) → entry(STOPPED) → risk(RUNNING) → risk(STOPPED) → [new entry instruction] → entry(RUNNING) → entry(STOPPED)

After risk executes, the risk flag blocks entry. A new position instruction with entry config resets the flag, allowing entry to resume toward the new target.

11. Entry (AUCTION) + Exit Overlap (Window Mode)

entry[AUCTION](RUNNING) ──────────────────────────── entry(STOPPED)
                          exit(RUNNING) → exit(STOPPED)

When entry is AUCTION and exit is non-AUCTION, the entry executor is not stopped at exit window open. Both run concurrently because the auction entry order does not compete with the non-auction exit for market liquidity. Once exit is triggered, new entry evaluation is blocked.

12. Entry + Auction Exit Overlap (Window-Disabled Mode)

entry(RUNNING) ──────────────────── entry(RUNNING/STOPPED)
                exit[AUCTION](RUNNING) → exit(STOPPED)

In window-disabled mode, an explicit exit instruction with auction type (MOC/MOO) is placed immediately without stopping entry. The auction exit runs alongside the active entry executor since they don't compete for market liquidity.

13. Early Auction Exit (Window Mode)

                    [exit timer fires]
entry(RUNNING) ──── exit[AUCTION](RUNNING) ──── exit window opens ──── exit(continues)

When exit is AUCTION type (equity only, not futures), the strategy sends early auction exit orders before the exit window opens, on a separate timer controlled by exitRefreshIntervalInMillis. This allows MOC/MOO orders to be placed well in advance. The exit executor is started/amended on the exit timer, independent of the main evaluate loop.

Deferred Risk Execution

Risk instructions can be deferred (stored but not immediately triggered):

  • If risk_start_time is in the future — waits until that time
  • If outside the risk time window (riskBeginTime/riskEndTime) — waits until window opens

During deferral, entry is already stopped (mutual exclusion is enforced at instruction arrival), but the risk executor won't start until the trigger condition is met.

Auction Entry Overlap

When entry is AUCTION (e.g., MOC/MOO) and exit is NOT AUCTION, the two slots can overlap — the auction entry order does not compete with the non-auction exit for market liquidity. In this mode:

  • Entry executors are not stopped when the exit window opens
  • Exit can be triggered alongside the running auction entry
  • However, once exit is triggered, new entry evaluation is blocked

This enables workflows like placing an MOC entry order early while a POV exit is already working the position.


Futures Rollover

When trading continuous futures contracts (e.g., ES.c.0, ES.v.0), the strategy supports automatic position transfer when a contract rolls to the next expiry.

Enabling Rollover

Rollover requires configuration at both the strategy and backtest level:

Strategy params:

params={
    'futuresRollOverEnabled': True,   # or 'enableFuturesRollover': True
    'enableExit': False,              # typically disabled so position carries overnight
}

Backtest config:

backtest_config = BacktestConfig(
    symbols=['ES.v.0'],
    enable_auto_rollover=True,  # Auto-injects filter_mode='continuous' and enableFuturesRollover
    session_start='18:00',      # CME Globex: 6 PM ET previous day
    session_end='17:00',        # CME Globex: 5 PM ET
)

Engine config (optional):

engine_config = EngineConfig(
    oms=OMSType.SIGMA.value,
    params={
        # Pre-close rollover exit window: EXIT old contract while trade data still available
        'rollover_exit_window_start_utc': '13:00',
        'rollover_exit_window_end_utc': '14:00',
    }
)

Continuous Contract Formats

FormatDescriptionExample
ES.c.0Calendar front monthRolls by expiration date
ES.v.0Volume-weighted front monthRolls when next contract has more volume
ESH6Specific contractNo rollover (fixed contract)

Rollover Lifecycle

The rollover is handled by the C++ engine and follows this sequence:

  1. ROLLOVER_DUE: Engine detects the continuous contract needs to roll (e.g., ESU5 → ESZ5)

    • All Python executors are immediately stopped
    • Entry/exit/risk evaluation is blocked (rollover_in_progress flag)
  2. ROLLOVER_EXIT: Engine exits the position on the old contract

    • C++ handles the square-off automatically
  3. ROLLOVER_ENTRY: Engine re-enters the position on the new contract

    • C++ handles the re-entry automatically
  4. ROLLOVER_COMPLETE: Rollover finished successfully

    • Python-side bookkeeping is updated (symbol mapping, contract references)
    • Evaluation resumes on the new contract
    • Executor slots are reset, risk/exit flags are cleared
  5. ROLLOVER_FAILED: Rollover encountered an error

    • Logged as warning, evaluation resumes

Rollover Timing

The rollover date comes from the HiveQ API's fut_continuous_def schema, which provides start_date/end_date for each contract. The engine computes the rollover at 22:00 UTC (18:00 ET) on the day BEFORE the next contract's start_date.

Rollover Callbacks

Strategies can observe rollovers via two callbacks:

on_rollover — fired after a successful rollover:

def on_rollover(self, ctx, event):
    rollover = event.data()
    print(f"Rolled: {rollover.continuous_symbol}")
    print(f"  From: {rollover.prev_contract}")
    print(f"  To:   {rollover.current_contract}")

on_security_event — fired for each lifecycle stage (ROLLOVER_DUE, ROLLOVER_EXIT, etc.):

def on_security_event(self, ctx, event):
    data = event.data()
    print(f"Security event: {data.event_type} for {data.symbol}")

Rollover Example

UserData CSV (rollover_instructions.csv):

date,time,sym,ticker,desiredpos,signal1,weight1,locate_id,desk_qty
2025-09-16,04:00:00.000,ESU5,ESU5,100,,,,

Strategy config:

strategy_configs = [
    StrategyConfig(
        name='AlgoInstrRollover',
        type='AlgoInstructionStrategy',
        symbols=['ES.v.0'],
        params={
            'userDataList': 'RolloverInstructions',
            'assetType': 'FUTURES',
            'entryBeginTime': '04:00:00',
            'entryEndTime': '15:45:00',
            'enableExit': False,           # Position carries overnight
            'futuresRollOverEnabled': True,
        }
    )
]

Data config:

data_configs = [
    {
        'type': 'hiveq_historical',
        'dataset': 'HIVEQ_US_FUT',
        'schema': ['fut_trades'],
        # filter_mode='continuous' is auto-injected by enable_auto_rollover
    },
    {
        'type': 'csv',
        'data_type': 'custom',
        'path': 'userdata/rollover_instructions.csv',
        'id': 'RolloverInstructions',
    },
]

Backtest config:

backtest_config = BacktestConfig(
    symbols=['ES.v.0'],
    start_date='2025-09-16',
    end_date='2025-09-17',
    initial_capital=1000000.0,
    venue='SIM',
    session_start='18:00',
    session_end='17:00',
    fetch_mode='daily',
    enable_auto_rollover=True,
)

What happens:

  1. Sep 16: BUY 100 ESU5 via POV at 50% participation
  2. Sep 19-20 (weekend): Engine detects ESU5 → ESZ5 rollover
  3. Engine exits 100 ESU5 and re-enters 100 ESZ5 automatically
  4. Strategy receives on_rollover callback with prev_contract=ESU5, current_contract=ESZ5
  5. Subsequent instructions route to ESZ5

Algo Config File (Decoupled Execution Config)

Execution instructions (algo_params) can be separated from signal data into a standalone config file. This allows changing execution parameters (e.g., switching from POV to TWAP) without regenerating the signal file.

Setup

Add a new strategy param and data config:

StrategyConfig(
    type='AlgoInstructionStrategy',
    params={
        'userDataList': 'signals_feed',
        'algoConfigEventID': 'algo_config_feed',   # NEW: references the config data source
        ...
    }
)
data_configs = [
    {'type': 'csv', 'data_type': 'custom', 'path': 'signals.csv', 'id': 'signals_feed'},
    {'type': 'csv', 'data_type': 'custom', 'path': 'algo_config.csv', 'id': 'algo_config_feed'},
]

Algo Config File Format

algo_config_id,sym,override,algo_params
default_es,ES.c.0,true,entry=POV;entry_participatePercentage=10;exit=AUCTION;exit_orderType=MOC
aggressive,,false,entry=POV;entry_participatePercentage=25;exit=AUCTION;exit_orderType=MOC
risk_reduce,,false,risk=POV;risk_participatePercentage=100
exit_moc,,false,exit=AUCTION;exit_orderType=MOC
ColumnRequiredDescription
algo_config_idYesUnique name for this config
sym / tickerNoSymbol this config applies to (used with override)
overrideNoIf true and sym is set, auto-applies to all signals for that symbol
algo_paramsYesExecution config (same semicolon or JSON format as inline)
  • date/time columns are optional. If present, config is loaded as a time-series (can change mid-day).
  • Multiple configs can exist for the same symbol (different IDs), but only one with override=true per symbol.

Override Mode (No Signal File Changes)

With override=true, you don't need to modify signal files at all:

algo_config.csv:

algo_config_id,sym,override,algo_params
default_es,ES.c.0,true,entry=POV;entry_participatePercentage=10

signals.csv:

date,time,sym,ticker,desiredpos,signal1,weight1,locate_id,desk_qty
2025-09-01,18:42:11.000,ES.c.0,ES.c.0,,0.00329,1,,

To change from POV 10% to TWAP 25%, edit the one line in algo_config.csv — the signal file stays untouched.

Explicit Config ID (Per-Row Override)

For rows that need a different config, use algo_config_id:

algo_config.csv:

algo_config_id,sym,override,algo_params
default_es,ES.c.0,true,entry=POV;entry_participatePercentage=10
aggressive,,,entry=POV;entry_participatePercentage=25

signals.csv:

date,time,sym,signal1,weight1,algo_config_id
09:35,ES.c.0,0.00329,1,
14:01,ES.c.0,0.00297,1,aggressive
15:14,ES.c.0,0.00694,1,

Row 2 uses aggressive config (25%), all others use the symbol override (10%).

Backwards Compatibility

  • If algoConfigEventID is not set, the strategy uses global strategy params as defaults.
  • The algo_params column is no longer supported on signal rows. Execution config must be provided via the algo config file or global strategy params.

Parameter Resolution Order

For executor config (entry, exit, risk), the resolution order is:

  1. algo_config_id reference from signal row → named config lookup
  2. Symbol override from algo config file (override=true)
  3. Strategy config global default (entryExecutorType, exitAlgo, participatePercentage, etc.)
  4. Built-in default (POV 10%, 1.0x)

On this page

AlgoInstructionStrategy SpecificationStrategy ConfigurationSignal File (userData CSV) FormatInstruction Type Auto-DetectionSignal-to-Position Conversionalgo_params Column SpecificationSupported Formats1. Semicolon-Separated (Recommended)2. Nested JSON3. Flat JSON (Legacy)Executor TypesAll Supported algo_params ParametersParticipation & PricingOrder ConfigurationTime ControlRisk Slot SpecificCustom FIX ParametersFull algo_params ExamplesPosition Entry with POVEntry + Timed ExitEntry with DurationMulti-Slot with RiskVenue and Account RoutingCustom FIX TagsNested JSON with All ParametersETB Locate RowSignal Mode RowExecutor Slot LifecycleOperating Modes: Window Mode vs Window-Disabled ModeWindow Mode (disableTradingWindows=False, default)Window-Disabled Mode (disableTradingWindows=True)Window Mode ExamplesWindow-Disabled Mode ExamplesWhat Makes AlgoInstructionStrategy DifferentSlot Mutual Exclusion RulesKey Principle: Risk Always WinsWorkflow Combinations1. Entry Only2. Entry → Exit3. Entry → Risk (Partial)4. Entry → Risk (Full Flatten)5. Entry → Exit → Risk6. Entry → Risk → Exit7. Risk Only (No Entry)8. Entry → Risk (Partial) → Exit9. Exit Only10. New Entry Resets Risk11. Entry (AUCTION) + Exit Overlap (Window Mode)12. Entry + Auction Exit Overlap (Window-Disabled Mode)13. Early Auction Exit (Window Mode)Deferred Risk ExecutionAuction Entry OverlapFutures RolloverEnabling RolloverContinuous Contract FormatsRollover LifecycleRollover TimingRollover CallbacksRollover ExampleAlgo Config File (Decoupled Execution Config)SetupAlgo Config File FormatOverride Mode (No Signal File Changes)Explicit Config ID (Per-Row Override)Backwards CompatibilityParameter Resolution Order