Periscøpe
Docs menu Strategy SDK

Strategy SDK

Reference for the public SDK surface: framework, events, actions, enums, and data shapes.

Everything your strategy code can import comes from two packages: libs.shared_ipc.src.framework for the base class, data coordinator, config validator, and config extractors, and libs.shared_ipc.src.protocol for events, actions, enums, and data shapes. JSONValue comes from libs.common_types.src.types.

What you need to start

A minimal strategy imports these symbols. This is enough to register a bar stream, handle bars, and place orders.

If you want runnable end to end examples instead of symbol by symbol reference, see Strategy Examples.

from libs.shared_ipc.src.framework import (
    StrategyBase, DataCoordinator, validate_config,
    require_str, require_int, require_decimal, require_bool,
    require_symbol, require_bar_time_span,
    optional_str, optional_int, optional_decimal, optional_bool,
    optional_symbol, optional_bar_time_span,
)
from libs.shared_ipc.src.protocol import (
    Action, BarEvent, OrderUpdateEvent, TimerEvent, ExpirationEvent,
    RegisterSecurityAction, BarRegistrationRequest,
    RegisterTimeStepAction, PlaceOrderAction, CancelLimitsAction,
    InsertCashAction,
    AssetType, BarTimeSpan, OrderIntent, OrderType, OrderState,
    StrategyBar, StrategyPosition, Symbol,
)
from libs.common_types.src.types import JSONValue

The mental model is: events arrive, your handler decides, you return actions. All prices and quantities are Decimal. All times are datetime. Symbol arguments on actions are plain str.

Reference

The rest of this page is a per symbol reference. Skim or search. Sections: Framework, Events, Actions, Enums, Data shapes, Runtime environment.

Framework

StrategyBase

The abstract base your Strategy class extends. You must implement on_start; every other handler defaults to a no op returning an empty action list. simulation_time is a property available from on_start onward. See Strategy Protocol for the full hook set and lifecycle.

DataCoordinator

An async handle passed to your __init__. Two methods.

async def get_position_by_symbol(
    self, symbol: str, asset_type: AssetType
) -> StrategyPosition | None: ...

async def get_front_month_contract_by_volume(
    self, root_symbol: str, as_of_time: datetime | None = None
) -> str: ...

validate_config(config)

Call in __init__ before reading any fields. Validates the config dict against the schema the framework loads for the run and raises on violations.

Typed config extractors

All take (config, key). The optional_* variants also take a default. They raise ConfigError on wrong types.

  • require_str, optional_str
  • require_int, optional_int
  • require_bool, optional_bool
  • require_decimal, optional_decimal (accepts int, float, or string values; normalizes to Decimal)
  • require_symbol, optional_symbol: for fields declared with x-periscope-type: "symbol". The operator submits a bare ticker; the extractor returns a Symbol with .symbol and .asset_type. See Strategy parameters for the schema shape.
  • require_bar_time_span, optional_bar_time_span: for fields declared with x-periscope-type: "bar_time_span". Returns a BarTimeSpan.

Events

All events are frozen dataclasses. Every event carries simulation_time: datetime, the clock position when the event was dispatched. Use self.simulation_time rather than wall clock time.

BarEvent

Fires for each bar on a registered symbol and timeframe. Fields: symbol: str, asset_type: AssetType, bar: StrategyBar.

QuoteEvent

Fields: symbol: str, asset_type: AssetType, quote: StrategyQuote. Currently disabled (see the warning above).

TimerEvent

Fires on timers you registered with RegisterTimeStepAction. Field: timer_id: str. Timers produced by RegisterTimeStepAction carry timer_id="time_step".

ExpirationEvent

Fires when a security you registered expires. Fields: symbol: str, asset_type: AssetType. The runtime automatically unsubscribes all bar and quote registrations for the expired security, so this is where you roll to a new contract.

OrderUpdateEvent

Fires on every state change for an order your strategy placed. Fields: order_id: UUID, symbol: str, asset_type: AssetType, order_state: OrderState, cumulative_quantity_filled: Decimal, cumulative_net_money: Decimal, updated_at: datetime.

Event, EventType

Event is the union of all five event types. EventType is the enum discriminator for wire serialization.

Actions

Handlers return list[Action]. An empty list is fine. on_stop is the exception: it returns None.

PlaceOrderAction

PlaceOrderAction(
    symbol: str,
    asset_type: AssetType,
    intent: OrderIntent,
    order_type: OrderType = OrderType.MARKET,
    contract_quantity: Decimal | None = None,
    limit_price: Decimal | None = None,
    stop_price: Decimal | None = None,
    time_in_force: TimeInForce | None = None,
)

CancelLimitsAction

Cancels all open limit orders for a symbol. Fields: symbol: str, asset_type: AssetType.

RegisterSecurityAction

Wraps one registration request. Required before events arrive for a symbol.

RegisterSecurityAction(
    request=BarRegistrationRequest(
        symbol: str,
        asset_type: AssetType,
        bar_time_span: BarTimeSpan,
    ),
)

QuoteRegistrationRequest(symbol, asset_type, quote_timespan) exists but is currently disabled.

RegisterTimeStepAction

Schedules a recurring timer. Field: time_step_seconds: float. Produces TimerEvent(timer_id="time_step").

UnregisterBarAction

Stops a specific bar stream. Fields: symbol: str, asset_type: AssetType, bar_time_span: BarTimeSpan.

UnregisterQuoteAction

Fields: symbol: str, asset_type: AssetType. Disabled along with quote subscriptions.

InsertCashAction, WithdrawCashAction

Adjust strategy cash. Field: amount: Decimal. Call InsertCashAction in on_start to fund the portfolio; strategies that skip this often see "out of cash" warnings.

Action, ActionType

Action is the union of every action type. ActionType is the enum discriminator for wire serialization.

Enums

AssetType

OPTION, EQUITY, INDEX, FUTURE, ETF, WARRANT, PREFERRED, RIGHT, UNIT.

Symbol parameters in config_schema.json are limited to a deployed subset, currently EQUITY and FUTURE. The other values remain valid on event and action payloads.

Direction

On positions: LONG, SHORT.

BarTimeSpan

SECOND, MINUTE, HOUR, DAY.

QuoteTimeSpan

STREAM, SECOND, MINUTE, HOUR, DAY.

OrderType

MARKET, LIMIT, STOP, STOP_LIMIT.

OrderIntent

BUY, SELL, CLOSE, BUY_TO_CLOSE, SELL_TO_CLOSE. When exact close behavior matters, prefer the explicit close intents over CLOSE.

TimeInForce

DAY, GTC, OPG, ATC, IOC, FOK, GTD, GTX.

OrderState

WORKING, FILLED, CANCELLED, REJECTED, EXPIRED.

Data shapes

StrategyBar

Fields: timestamp: datetime, open, high, low, close (all Decimal), volume: Decimal | None.

StrategyQuote

Fields: timestamp: datetime, bid_price, ask_price (Decimal), bid_size, ask_size (int), price: Decimal | None.

StrategyPosition

Fields: symbol: str, direction: Direction, net_contract_quantity: Decimal, average_price: Decimal.

Symbol

Frozen dataclass returned by require_symbol / optional_symbol. Fields: symbol: str, asset_type: AssetType. Exported from both libs.shared_ipc.src.framework and libs.shared_ipc.src.protocol.

Runtime environment

Strategy code runs in a sandboxed container: read only filesystem, no network, no subprocesses. The framework owns the event loop, market data subscriptions, order routing, portfolio state, and persistence. Your code is the decision layer. Python 3.13+.