Documentação

Marketplace

A superfície Marketplace do SDK ajuda usuários técnicos a empacotar algos criados com o SDK, executar a revisão determinística de publicação, publicar versões aprovadas, assinar algos com controles de risco da plataforma e operar comandos de ciclo de vida.

A execução no Marketplace deve usar controles de risco da própria plataforma. O código do autor não recebe credenciais brutas do assinante no runtime gerenciado, e toda ordem de runtime deve passar pelo risk gate da plataforma.

Revisar e enviar um pacote

from investflex import AsyncInvestflexClient

manifest = {
    "id": "breakout-rsi",
    "name": "Breakout RSI",
    "version": "1.0.0",
    "entrypoint": "algo:BreakoutRsiAlgo",
    "markets": ["BVMF"],
    "permissions": {
        "marketdata": ["quotes", "candles"],
        "execution": ["orders"],
    },
    "lifecycle": {
        "required_commands": [
            "start",
            "suspend",
            "resume",
            "cancel",
            "cancel_and_flat",
            "stop",
        ],
    },
    "risk": {
        "requires_stop_loss": True,
        "requires_stop_gain": True,
        "requires_max_position": True,
        "requires_max_order_quantity": True,
        "requires_max_orders_per_second": True,
    },
    "dependencies": {
        "allowed_imports": [],
        "packages": [],
    },
    "parameters": {
        "symbol": {"type": "string", "required": True},
        "quantity": {"type": "integer", "minimum": 1, "required": True},
    },
}

code = """
from investflex.marketplace import MarketplaceAlgo, AlgoContext

class BreakoutRsiAlgo(MarketplaceAlgo):
    async def start(self, ctx: AlgoContext) -> None:
        await ctx.lifecycle.heartbeat("started")

    async def suspend(self, ctx: AlgoContext) -> None:
        await ctx.lifecycle.heartbeat("suspended")

    async def resume(self, ctx: AlgoContext) -> None:
        await ctx.lifecycle.heartbeat("resumed")

    async def cancel(self, ctx: AlgoContext) -> None:
        await ctx.lifecycle.stop("cancelled")

    async def cancel_and_flat(self, ctx: AlgoContext) -> None:
        await ctx.lifecycle.stop("cancelled and flattened")

    async def stop(self, ctx: AlgoContext) -> None:
        await ctx.lifecycle.stop("stopped")
"""

async with AsyncInvestflexClient.from_token(token, base_url=backend_url) as client:
    review = await client.marketplace.review(manifest=manifest, code=code)
    print(review["status"], review["score"])

    package = await client.marketplace.submit_package(manifest=manifest, code=code)
    print(package["package_id"], package["status"])
import { InvestflexClient } from "@investflex/sdk";

const manifest = {
  id: "breakout-rsi",
  name: "Breakout RSI",
  version: "1.0.0",
  entrypoint: "algo:BreakoutRsiAlgo",
  markets: ["BVMF"],
  permissions: {
    marketdata: ["quotes", "candles"],
    execution: ["orders", "algos"],
  },
  lifecycle: {
    required_commands: [
      "start",
      "suspend",
      "resume",
      "cancel",
      "cancel_and_flat",
      "stop",
    ],
  },
  risk: {
    requires_stop_loss: true,
    requires_stop_gain: true,
    requires_max_position: true,
    requires_max_order_quantity: true,
    requires_max_orders_per_second: true,
  },
  dependencies: {
    allowed_imports: [],
    packages: [],
  },
  parameters: {
    symbol: { type: "string", required: true },
    quantity: { type: "integer", minimum: 1, required: true },
  },
};

const code = `
import {
  type AlgoContext,
  ManagedExecution,
  MarketplaceAlgo,
} from "@investflex/sdk/marketplace";

async function currentPosition(ctx: AlgoContext): Promise<number> {
  const positions = ctx.execution?.positions;
  if (!positions) return 0;
  return Number(await positions.net(ctx.params.symbol));
}

export class BreakoutRsiAlgo extends MarketplaceAlgo {
  private readonly execution = new ManagedExecution();

  override async start(ctx: AlgoContext): Promise<void> {
    for (const _signal of []) {
      await ctx.lifecycle.waitIfSuspended();
      await ctx.lifecycle.heartbeat("checking signal");
      const position = await currentPosition(ctx);
      ctx.risk.checkOrder({ quantity: ctx.params.quantity, currentPosition: position });
      await this.execution.createOrder(ctx, {
        symbol: ctx.params.symbol,
        quantity: ctx.params.quantity,
      });
    }
  }

  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 resume(ctx: AlgoContext): Promise<void> {
    await ctx.lifecycle.heartbeat("resumed");
  }

  override async cancel(ctx: AlgoContext): Promise<void> {
    await this.execution.cancelAllOrders(ctx, "parent cancelled");
    await this.execution.cancelAllStrategies(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: "parent cancelled and flattened",
    });
    await ctx.lifecycle.stop("cancelled and flattened");
  }

  override async stop(ctx: AlgoContext): Promise<void> {
    await this.execution.cancelAllOrders(ctx, "parent stopped");
    await this.execution.cancelAllStrategies(ctx, "parent stopped");
    await ctx.lifecycle.stop("stopped");
  }
}
`;

const client = InvestflexClient.fromToken(token, { baseUrl: backendUrl });

const review = await client.marketplace.review({ manifest, code });
console.log(review.status, review.score);

const pkg = await client.marketplace.submitPackage({ manifest, code });
console.log(pkg.package_id, pkg.status);

Publicar e assinar

listing = await client.marketplace.publish_package(
    package["package_id"],
    description="Estrategia de rompimento validada no simulador.",
)

subscription = await client.marketplace.subscribe(
    listing["listing_id"],
    account="99999",
    parameters={"symbol": "PETR4", "quantity": 100},
    risk={
        "stop_loss": 100,
        "stop_gain": 150,
        "max_position": 500,
        "max_order_quantity": 100,
        "max_orders_per_second": 2,
    },
)
const listing = await client.marketplace.publishPackage(pkg.package_id, {
  description: "Estratégia de rompimento validada no simulador.",
});

const subscription = await client.marketplace.subscribe(listing.listing_id, {
  account: "99999",
  parameters: { symbol: "PETR4", quantity: 100 },
  risk: {
    stop_loss: 100,
    stop_gain: 150,
    max_position: 500,
    max_order_quantity: 100,
    max_orders_per_second: 2,
  },
});

Comandos de runtime e auditoria

await client.marketplace.command_subscription(subscription["subscription_id"], "start")

order = await client.marketplace.submit_runtime_order(
    subscription["subscription_id"],
    symbol="PETR4",
    side="BUY",
    quantity=1,
    price=40.0,
)

events = await client.marketplace.list_events(subscription["subscription_id"])
audit = await client.marketplace.audit(
    entity_type="subscription",
    entity_id=subscription["subscription_id"],
    limit=50,
)
await client.marketplace.commandSubscription(subscription.subscription_id, "start");

const order = await client.marketplace.submitRuntimeOrder(
  subscription.subscription_id,
  {
    symbol: "PETR4",
    side: "BUY",
    quantity: 1,
    price: 40.0,
  },
);

const events = await client.marketplace.listEvents(subscription.subscription_id);
const audit = await client.marketplace.audit({
  entityType: "subscription",
  entityId: subscription.subscription_id,
  limit: 50,
});

Use cancel-and-flat quando o runtime precisar cancelar ordens em aberto e zerar a exposição residual por controles aprovados pela plataforma.