Documentação

Marketplace Execution Helpers

Marketplace algos can use investflex.marketplace helpers to track every order and child strategy created during one execution. The helpers make lifecycle commands predictable: when an operator cancels, suspends, or flats the parent algo, the algo has one registry it can use to clean up the work it started.

New Marketplace Studio templates already include the helper pattern.

Imports

from investflex.marketplace import (
    AlgoContext,
    ExecutionReport,
    Fill,
    ManagedExecution,
    MarketplaceAlgo,
    PayoffControl,
    PositionControl,
    TrackedHandle,
)
import {
  type AlgoContext,
  ExecutionReport,
  type Fill,
  ManagedExecution,
  MarketplaceAlgo,
  PayoffControl,
  PositionControl,
  TrackedHandle,
} from "@investflex/sdk/marketplace";

ManagedExecution

Create one ManagedExecution instance per algo instance.

class MyMarketplaceAlgo(MarketplaceAlgo):
    def __init__(self) -> None:
        self.execution = ManagedExecution()
class MyMarketplaceAlgo extends MarketplaceAlgo {
  readonly execution = new ManagedExecution();
}

Use it when the algo creates orders or child strategies. The helper stores the returned handle, extracts common ids such as order_id, algo_id, strategy_id, or id, and keeps payload/status metadata for cleanup and diagnostics.

order = await self.execution.create_order(
    ctx,
    symbol=ctx.params.symbol,
    side="BUY",
    quantity=ctx.params.quantity,
)

child = await self.execution.create_strategy(
    ctx,
    strategy="TWAP",
    symbol=ctx.params.symbol,
    quantity=ctx.params.quantity,
)
class MyMarketplaceAlgo extends MarketplaceAlgo {
  readonly execution = new ManagedExecution();

  override async start(ctx: AlgoContext): Promise<void> {
    const order = await this.execution.createOrder(ctx, {
      symbol: ctx.params.symbol,
      side: "BUY",
      quantity: ctx.params.quantity,
    });

    const child = await this.execution.createStrategy(ctx, {
      strategy: "TWAP",
      symbol: ctx.params.symbol,
      quantity: ctx.params.quantity,
    });
  }
}

If an order or strategy was created through another helper, register the handle explicitly.

self.execution.register_order(order_response)
self.execution.register_strategy(strategy_response)
execution.registerOrder(orderResponse);
execution.registerStrategy(strategyResponse);

Lifecycle Cleanup

Call cleanup helpers from lifecycle methods before the parent algo stops emitting work.

async def suspend(self, ctx: AlgoContext) -> None:
    await self.execution.cancel_all_orders(ctx, reason="parent suspended")
    await self.execution.suspend_all_strategies(ctx, reason="parent suspended")
    await ctx.lifecycle.heartbeat("suspended")

async def cancel(self, ctx: AlgoContext) -> None:
    await self.execution.cancel_all(ctx, reason="parent cancelled")
    await ctx.lifecycle.stop("cancelled")

async def cancel_and_flat(self, ctx: AlgoContext) -> None:
    await self.execution.cancel_and_flat(
        ctx,
        symbol=ctx.params.symbol,
        reason="cancel and flat requested",
    )
    await ctx.lifecycle.stop("flat requested")
class MyMarketplaceAlgo extends MarketplaceAlgo {
  readonly execution = new ManagedExecution();

  override async suspend(ctx: AlgoContext): Promise<void> {
    await this.execution.cancelAllOrders(ctx, "parent suspended");
    await this.execution.suspendAllStrategies(ctx, "parent suspended");
    await ctx.lifecycle.heartbeat("suspended");
  }

  override async cancel(ctx: AlgoContext): Promise<void> {
    await this.execution.cancelAll(ctx, "parent cancelled");
    await ctx.lifecycle.stop("cancelled");
  }

  override async cancelAndFlat(ctx: AlgoContext): Promise<void> {
    await this.execution.cancelAndFlat(ctx, {
      symbol: ctx.params.symbol,
      reason: "cancel and flat requested",
    });
    await ctx.lifecycle.stop("flat requested");
  }
}

For suspend, the marketplace review accepts either canceling open orders or suspending child strategies. Use the behavior that matches the algo:

  • cancel_all_orders(ctx, reason=...)
  • cancel_all_strategies(ctx, reason=...)
  • suspend_all_strategies(ctx, reason=...)
  • resume_all_strategies(ctx, reason=...)
  • cancel_all(ctx, reason=...)
  • suspend_all(ctx, reason=...)
  • cancel_and_flat(ctx, symbol=..., reason=...)

All cleanup helpers are best effort and idempotent. They return an ExecutionReport instead of failing on the first rejected action.

ExecutionReport

ExecutionReport records each attempted lifecycle action.

report = await self.execution.cancel_all(ctx, reason="operator cancel")

if not report.ok:
    for failure in report.failures:
        await ctx.lifecycle.heartbeat(f"cleanup failed: {failure.error}")
const report = await execution.cancelAll(ctx, "operator cancel");

if (!report.ok) {
  for (const failure of report.failures) {
    await ctx.lifecycle.heartbeat(`cleanup failed: ${failure.error}`);
  }
}

Use report.to_dict() when the algo needs to write structured diagnostics.

TrackedHandle

Each tracked order or strategy is stored as a TrackedHandle.

Useful fields:

  • kind: order or strategy
  • handle_id: extracted platform id when available
  • status: local status such as submitted, cancelled, or suspended
  • payload: request fields used when the handle was created
  • is_final: true for final statuses such as cancelled, filled, rejected, expired, done, or closed

Use snapshot() to inspect the current registry.

snapshot = self.execution.snapshot()
open_orders = self.execution.open_orders()
open_strategies = self.execution.open_strategies()
const snapshot = execution.snapshot();
const openOrders = execution.openOrders();
const openStrategies = execution.openStrategies();

PositionControl

PositionControl is a pure helper for calculating position from fills.

fills = [
    Fill(symbol="PETR4", side="BUY", quantity=100, price=30.10),
    Fill(symbol="PETR4", side="SELL", quantity=40, price=30.30),
]

positions = PositionControl.from_fills(fills)
net = PositionControl.net("PETR4", positions)
const fills: Fill[] = [
  { symbol: "PETR4", side: "BUY", quantity: 100, price: 30.1 },
  { symbol: "PETR4", side: "SELL", quantity: 40, price: 30.3 },
];

const positions = PositionControl.fromFills(fills);
const net = PositionControl.net("PETR4", positions);

In managed runtime templates, prefer live runtime positions when available:

async def current_position(ctx: AlgoContext) -> int:
    positions = getattr(getattr(ctx, "execution", None), "positions", None)
    if positions is None:
        return 0
    return int(await positions.net(ctx.params.symbol))
async function currentPosition(ctx: AlgoContext): Promise<number> {
  const positions = ctx.execution?.positions;
  if (!positions) return 0;
  return Number(await positions.net(ctx.params.symbol));
}

PayoffControl

PayoffControl provides local payoff and PnL calculations for author-side accounting.

positions = PositionControl.from_fills(fills)
realized = PayoffControl.realized_pnl(fills)
unrealized = PayoffControl.unrealized_pnl(positions, {"PETR4": 30.50})
scenario = PayoffControl.scenario(positions["PETR4"], [29.80, 30.00, 30.50])
const positions = PositionControl.fromFills(fills);
const realized = PayoffControl.realizedPnl(fills);
const unrealized = PayoffControl.unrealizedPnl(positions, { PETR4: 30.5 });
const scenario = PayoffControl.scenario(positions.get("PETR4")!, [29.8, 30.0, 30.5]);

Available helpers:

  • realized_pnl(fills)
  • unrealized_pnl(positions, marks)
  • mark_to_market(position, mark)
  • breakeven(position, fees=0.0)
  • scenario(position, prices)
  • max_loss(position, prices)
  • max_gain(position, prices)

Review Expectations

Marketplace review checks lifecycle methods for cleanup calls. New algos should use ManagedExecution or the runtime ctx.execution helpers in suspend, cancel, and cancel_and_flat.

The recommended pattern is:

  • create orders and child strategies through self.execution
  • call cancel_all_orders or suspend_all_strategies during suspend
  • call cancel_all during cancel
  • call cancel_and_flat during cancel_and_flat
  • call ctx.lifecycle.heartbeat(...) or ctx.lifecycle.stop(...) after the lifecycle command reaches a stable state