Manager API

manager is the single object your agent code receives. Every capability the engine exposes is a method on it. This page gives each method's exact signature (generated from the engine and checked in CI), what its parameters mean, what it returns and how it behaves in a backtest and in a live or demo run. Read Structure and lifecycle first for the root-versus-callback model that governs where each call is allowed. The object shapes named in signatures (CreateOrder, Trade, Order, …) are spelled out in Types.

Two conventions run through the whole API:

  • Root versus callbacks. Setup calls (create an indicator, attach a listener) belong at the root. Calls that read "now" or act on the market belong inside a callback. Where it matters, it is noted below, and the validator enforces it.
  • Guard before you read. Anything that may not have a value yet returns undefined (a price before the market opens, an indicator still warming up). Check for undefined before using the value. Never coerce a missing value to 0.

Units used everywhere: times are epoch milliseconds; intervals are minutes (or 'week' / 'month'); prices are in the instrument's quote currency; risk and proportion are fractions (0.01 = 1%); delta on exits is a percentage (0 to 100).

How the engine runs your agent

Where a run is a backtest (history replayed on a fresh paper wallet), a demo run (live prices, paper wallet) or a live run (your broker account), the behaviour below differs as noted.

Start-up. The root runs once. No callback fires until it has finished, including everything it awaits. In a demo or live run the engine then replays recent history into every candle series (callbacks stay silent during this replay), so indicators are warm when the first live callback fires. A backtest has no warm-up: indicators start empty on the backtest's start date, so a 200-day average has no value for the first 200 days of the window. Choose a start date that leaves room for your longest indicator.

Clock. A backtest advances minute by minute on 1-minute candles, and also stops at every schedule time and candle boundary even when the market is closed. A demo or live run ticks every second, a couple of seconds behind real time.

Order of events within one tick. When several things happen at the same instant, they run in this order:

  1. Resting orders are processed by the wallet (fills, cancellations, and their onOrderExecuted / onOrderCanceled / onTradeFilled callbacks).
  2. The current price moves to the new candle.
  3. Stop-losses and take-profits are checked, then onPreciseCandle callbacks run.
  4. onAltData callbacks, then cron callbacks, in the order they were attached.
  5. Candle series update, then onIntervalCandle callbacks run (all series that close at this instant are updated before the first of these callbacks).
  6. onIntervalCandleOpen callbacks.

So a cron that fires exactly on a candle boundary runs before that candle closes: a '0 0 * * *' schedule reads daily indicators that do not yet include the day that just ended. Schedule a minute later, or use onIntervalCandle, when you need the closed candle.

Async callbacks. A callback may be async. In a backtest the engine waits for it before advancing the clock, so awaited work completes at its simulated time. In a live or demo run it is not awaited: the next tick can start before it finishes. Return (or await) every promise you start; work left running after a backtest's last tick fails the run.

Errors. An exception that escapes a callback fails a backtest, and stops a live or demo agent (it is restarted automatically some minutes later). Catch errors from awaited calls and report them with manager.handleError(error).

Restarts. Live and demo agents restart on deployments and after a crash. A restart runs the root again, rebuilds indicators from history, restores status bags, open orders, open trades and positions, and re-arms schedules. Local variables are lost, and a schedule occurrence that fell during the downtime is not replayed.

Time and price

getCurrentTime(): number

manager.getCurrentTime() returns the engine's current time, in epoch milliseconds: the simulated time in a backtest, the real time otherwise. Use it instead of the real clock so backtests stay deterministic. Callback only.

getCurrentPrice(symbol: string): number | undefined

manager.getCurrentPrice(symbol) returns the latest price for a symbol, or undefined if none has been seen yet. In a backtest a symbol has no price until its first candle inside the window; in a demo or live run subscribed symbols are primed before the first callback. Inside onIntervalCandle and cron callbacks this is normally the open of the minute that just started. A symbol you never subscribed (see subscribing) stays undefined. Callback only.

hasPriceData(symbol: string): boolean

manager.hasPriceData(symbol) reports whether a price has been seen for the symbol.

getSymbolMetadata(symbol: string): Promise<SymbolMetadata>

manager.getSymbolMetadata(symbol) returns a symbol's tickSize, and when known pipSize, pointSize, contractSize, lotSize, timeZone (the listing's IANA session zone), quoteAssetSymbol and display identity (ticker, name). It is asynchronous: await it at the root, or call it inside an event callback (onNews, onMessage, onPieuvreEvent), never in a candle or schedule callback. It rejects when no metadata exists for the symbol. On futures, qty counts contracts and the engine applies contractSize itself; use contractSize only to convert a dollar risk into a contract count.

Candles and indicators

getIntervalData(symbol: string, interval: Interval, timeZone?: string): ICandleIndicator

manager.getIntervalData(symbol, interval, timeZone?) returns the candle series of a symbol at an interval given in minutes (60 for hourly, 1440 for daily; 'week' or 'month' for coarser). Create it at the root; it also subscribes the symbol. Bars align on the time zone: 1440 closes at local midnight of timeZone, 'week' on Monday 00:00, 'month' on the 1st. When timeZone is omitted the asset's exchange zone is used; for daily or coarser intervals always pass it (see Intervals and time zones). The same symbol, interval and zone always return the same series. Candles with no trades are not emitted (no gap filling).

The series' value is the last closed Candle ({ openTime, closeTime, open, high, low, close, volume }); getPending() returns the candle still forming. .open, .high, .low, .close, .volume, .hl2, .hlc3, .ohlc4, .openTime and .closeTime are number streams you can feed to indicators.

onIntervalCandle(symbol: string, interval: Interval, callback: () => void, timeZone?: string): void

manager.onIntervalCandle(symbol, interval, callback, timeZone?) runs the callback when a candle closes. For an exchange-listed asset, a daily candle closes at midnight in the exchange's zone, after the session: a market order placed from that callback fills at the last traded price on paper, and at the next session's open on a live broker. To trade during the session, act at the next candle's open (onIntervalCandleOpen) or on a schedule inside market hours (for example '55 15 * * 1-5' New York for "at the close"). The callback takes no argument: read the closed candle with getIntervalData(...).getValue() and your indicators with getValue(); they are already updated. A daily-or-coarser interval must pass the time zone. Attach at the root.

onIntervalCandleOpen(symbol: string, interval: Interval, callback: () => void, timeZone?: string): void

manager.onIntervalCandleOpen(symbol, interval, callback, timeZone?) runs the callback when a new candle opens, that is on the first trade after the boundary (for a US stock's daily candle, the first minute of the session, not midnight). getCurrentPrice then returns the opening price. Read the new candle with getPending(). Attach at the root.

onPreciseCandle(symbol: string, callback: (candle: Candle) => void): void

manager.onPreciseCandle(symbol, callback) runs on each price update with the latest candle: once per 1-minute candle in a backtest, at most once per second in a demo or live run. It runs before candle series and indicators include that update. An empty manager.onPreciseCandle(symbol, () => {}) at the root is also how you subscribe a symbol so a scheduled callback can read its price. Attach at the root.

Indicator construction and reading is covered in Indicators, and every class in the Indicator reference.

Scheduling and other events

cron(cronStr: string, callback: () => void, timeZone?: string): void

manager.cron(expression, callback, timeZone?) runs the callback on a cron schedule: five fields (minute, hour, day of month, month, day of week), or six with a leading seconds field; ranges, steps, lists, month and day names, and @hourly / @daily / @weekly / @monthly are accepted. Sunday is 0 or 7; when both day of month and day of week are restricted, either one matching fires. It defaults to UTC: pass a time zone whenever a wall-clock time matters. A cron never fires at the moment the agent starts (or restarts); its first run is the next matching time, and occurrences missed while the agent was down are not replayed. In a backtest every fire is a simulation step, so a per-minute schedule makes long backtests slow or impossible: see Schedules and launch actions. Attach at the root.

onAltData<T>(identifier: string, callback: (point: AltDataPoint<T>) => void): void

manager.onAltData(identifier, callback) runs when an alternative-data point arrives on a named topic. The identifier must be an exact topic (no wildcards). Attach at the root.

onNews(request: NewsRequest, callback: (news: News) => void): void

manager.onNews(request, callback) runs when a post matching your description arrives, request being { source: 'x', matchPrompt, handle? }. matchPrompt must be a precise, headline-shaped event ("A major exchange halts withdrawals"): relevance is judged upstream, so act on news.content directly without re-checking it. Only posts from monitored X accounts are pushed, not press articles: for press coverage, poll searchNews on a schedule. handle does not filter anything. Live and demo only (not backtestable). Attach at the root.

onMessage(request: MessageRequest, callback: (message: Message) => void): void

manager.onMessage(request, callback) runs for every post of one followed source: { source: 'x', handle: 'elonmusk' } (without @) or { source: 'telegram', channel: 'channelname' } (a public channel). The callback receives { content, author, url, publishedAt, media }; content is '' on an image-only post, and media holds { id, kind } handles you can pass to manager.llm(..., { images }). There is no upstream filter: filter in code. Following an X account is billed per delivered post; Telegram is free. An X follow with a literal handle is backtestable (up to 5 handles: past posts are replayed at their time, without images); Telegram is not. Attach at the root.

onPieuvreEvent(request: PieuvreEventRequest, callback: (event: PieuvreEvent) => void): void

manager.onPieuvreEvent(request, callback) runs on a matching data-engine update (an economic release, a macro event). request is { matchPrompt, sourceIds?, composites?, minSeverity?, titleContains? }; set sourceIds (for example ['eodhd.economic_calendar'] for the economic calendar) and minSeverity whenever you can, because every candidate event is matched against matchPrompt and billed. The event is { sourceId, summary, value?, severity?, timestamp, payload? }; for an economic release, payload.actual, payload.estimate and payload.previous hold the figures at release time. Not backtestable. Attach at the root.

Orders

Size an order with either qty (a number of units you compute) or amount (a cash notional). Use amount when you do not know the price up front. Every cash amount must carry an explicit amountCurrency such as 'USD'.

placeOrder(order: CreateOrder): Order | undefined

manager.placeOrder(order) submits an order and returns it, or undefined. Use this for buying, selling, accumulating and rebalancing. Callback only. The fields you set:

Field Meaning
symbol Your declared symbol, or a trading key returned by the engine.
type 'MARKET', 'LIMIT', or 'MARKET_TRIGGER' (a stop-market). LIMIT and MARKET_TRIGGER are paper and backtest only for now: a live broker refuses them.
position 'buy' or 'sell'. A sell with nothing held opens a short on paper.
qty Units (contracts on futures). Exactly one of qty / amount.
amount Cash notional. MARKET orders only. Needs amountCurrency.
amountCurrency 'USD' by default choice; 'USDT' / 'USDC' are treated as USD in backtests; any other currency only sizes in demo and live runs. Only with amount.
limitPrice For LIMIT only.
triggerPrice For MARKET_TRIGGER only.
inhibitAutoInvest true when the order spends cash the wallet already holds; omit it for a contribution that brings new money in. See capital and auto-invest.

The other fields of CreateOrder are engine-internal: do not set them.

How orders execute:

  • Paper (backtest and demo): a MARKET order with a known price fills immediately, inside placeOrder, at the current price plus the configured spread; its onOrderExecuted callback fires before placeOrder returns. A MARKET order on a symbol with no price yet rests and fills on the next candle. A LIMIT order fills when a 1-minute close crosses the limit, at the limit price (a buy limit above the market fills at the limit, not at the better market price). A MARKET_TRIGGER fires when a close crosses the trigger and fills at that close. Paper fills are never partial.
  • Live: only MARKET orders, for now. The order goes to your broker, which decides the fill; fills are observed a few seconds later and can be partial (filledQty smaller than qty).
  • Fees: backtests apply the venue's fee schedule (for example 0% commission plus a 0.01% spread on stocks and ETFs, 0.10% commission on spot crypto). Demo runs apply none.
  • Returns undefined when the size is 0, when the symbol is outside the agent's allowed scope, in a live run when a cash amount cannot be priced yet, or for a LIMIT order on a live broker (for now). In the last two cases the order is skipped and the agent's journal says so.
  • Throws on an invalid shape (both or neither of qty / amount, a negative or non-finite size, amount on a non-MARKET order, a price field on the wrong type, a LIMIT or MARKET_TRIGGER price that is missing or not a positive number), on a symbol that was never subscribed in a backtest, beyond 500 pending orders, and for a MARKET_TRIGGER order on a live broker.

The returned Order is a live object the engine updates (executedTime, filledQty, price, canceledTime, cancelCause). Its _id is how you match it in onOrderExecuted / onOrderCanceled.

cancelOrder(order: Order): void

manager.cancelOrder(order) cancels a pending order of this agent. It throws if the order is already executed or cancelled: check manager.getSymbolPendingOrders(symbol) first. Callback only.

getSymbolPendingOrders(symbol: string): readonly Order[]
getPendingOrders(): readonly Order[]

manager.getSymbolPendingOrders(symbol) and manager.getPendingOrders() list this agent's orders that have neither executed nor been cancelled.

onOrderExecuted(callback: (order: Order) => void): void
onOrderCanceled(callback: (order: Order) => void): void

manager.onOrderExecuted(callback) and manager.onOrderCanceled(callback) observe this agent's own orders (including trade entries and exits and closePosition orders) reaching a terminal state. Match on order._id, and read filledQty for the executed quantity. A cancellation carries canceledTime and, when known, cancelCause ('insufficientMargin', 'noLivePrice') or cancelMessage (a broker refusal). On paper these can fire before placeOrder returns, so record the order id first and reconcile, or look the id up when the call returns. Past events are not replayed after a restart. Attach at the root.

Trades (entries with a matching exit)

A trade is an entry that you intend to close later, so it carries take-profit and stop-loss and produces realized profit when it closes. For buy-and-hold or accumulation, prefer orders.

openTrade(signal: V2EnterSignal): Trade | undefined

manager.openTrade(signal) opens a trade and returns it, or undefined. Callback only. The signal:

Field Meaning
symbol, position As for orders: 'buy' (long) or 'sell' (short).
type 'MARKET', 'LIMIT' or 'MARKET_TRIGGER' for the entry. A live broker takes 'MARKET' only, for now.
price The entry price for LIMIT / MARKET_TRIGGER (required for those).
positionSize { type: 'proportion', proportion } (fraction of the wallet's current value), { type: 'risk', risk } (fraction of the wallet's value lost if the stop is hit), { type: 'amount', amount } (cash) or { type: 'qty', qty } (units).
stoploss, stoploss2 … stoploss4 { type: 'fixed' | 'trailing' | 'special', value, delta? }. value is an absolute price. delta is the percentage of the initial position closed at that level (omit it to close everything that is left).
takeprofit, takeprofit2 … takeprofit4 { type: 'fixed', value, delta? }, { type: 'trigger', trigger, value, delta? } or { type: 'trailing', trigger, relative, delta? }. Prices are absolute.
comment One line naming the rule and the values that fired, shown in the trades table.

Sizing uses the whole wallet's current (mark-to-market) value. risk divides by the stop distance, so it needs a stop: without one, it behaves like proportion. The engine does not cap risk sizing; a very tight stop can ask for more than the wallet can margin, and the entry is then cancelled. A stop on the wrong side of the entry (a buy stop at or above the entry price) makes openTrade return undefined. If the symbol has no price yet, the request is queued until its first candle and undefined is returned. A LIMIT entry on a live broker also returns undefined, without being queued: it is skipped (for now), and the journal says so.

Stops and take-profits are watched by the engine, not placed at the broker: they are checked on every 1-minute candle's high and low in a backtest, and every second in a demo or live run. A level triggers when price crosses it strictly. A backtest exit fills at the level itself, even on a gap through it; a demo or live exit fills at the market price when triggered. A 'trailing' stop keeps its initial distance behind the best price; a 'trigger' take-profit arms when price reaches trigger and then exits at value; a 'trailing' take-profit arms at trigger and then trails relative behind the best price. To move a stop later, use the updateStoploss helper.

closeTrade(trade: Trade, signal: LeaveSignal): void

manager.closeTrade(trade, { cause, delta? }) closes a trade with a market order. cause is one line naming the rule and its values ("EMA20 crossed back below EMA50"). delta is an integer percentage (0 to 100) of the initial position; omit it to close everything left. If the entry has not filled yet, the entry order is cancelled instead. Callback only.

getSymbolPendingTrades(symbol: string): readonly Trade[]
getPendingTrades(): readonly Trade[]

manager.getSymbolPendingTrades(symbol) and manager.getPendingTrades() list this agent's open trades (entry pending or filled, not yet fully closed). There is no list of closed trades: collect what you need in onTradeFinished. Useful trade fields: _id, symbol, position, qty, filledQty, enterPrice, enterTime, stoploss (with absolute, the current level), takeprofit, exitHistory, and intelligence.earnings (realized profit), latent?.earnings (unrealized).

onTradeFilled(callback: (trade: Trade) => void): void
onTradeFinished(callback: (trade: Trade) => void): void

manager.onTradeFilled(callback) fires when a trade's entry executes (on paper, for a MARKET entry, before openTrade returns). manager.onTradeFinished(callback) fires once a trade is fully closed, with its final statistics. Attach at the root.

Positions and the account book

These read the portfolio the agent runs against. They let an agent act on positions it did not open (yours, or another agent's), not only on trades it created. The book belongs to the wallet, not to the agent: several agents on one demo portfolio see the same positions.

getPositions(): readonly OpenPosition[]

manager.getPositions() returns every open position: { symbol, ticker?, name?, qty, value, pnl }. qty is negative for a short. value is the capital committed as a signed cash flow (negative for a long, positive for a short), not the market value: compute a position's market value as qty × getCurrentPrice(symbol). pnl is the unrealized profit. Name a position by position.ticker ?? position.symbol in any user-facing text; pass position.symbol to order and price calls. Flat positions are omitted.

getPositionQty(symbol: string): number
getAveragePrice(symbol: string): number

manager.getPositionQty(symbol) returns the position's signed quantity (0 when flat). manager.getAveragePrice(symbol) returns its cost basis per unit (positive for longs and shorts, entry fees included, 0 when flat). For a position opened outside the agent, the average is the broker's reported one when available.

Both describe the whole portfolio the agent runs on. In a backtest that portfolio is the agent's alone, but a demo or live portfolio is shared: your own holdings and other agents' fills on the same symbol are part of the position. To know what the agent itself holds, read its trades (getSymbolPendingTrades(symbol), each with its enterPrice) or count its own fills in onOrderExecuted. A strategy that decides on getPositionQty is refused (TS9047, see Rules).

closePosition(symbol: string, qty?: number): Order | undefined

manager.closePosition(symbol, qty?) closes or reduces the position held right now (whoever opened it: this agent's orders, you, or another agent) with a market order on the reducing side, sized from the position at that instant (all of it, or qty units, capped at the position). This is the safe way to exit a position the agent did not open: it is bound to the position, so on a live account it can never flip into a reverse position on a race. It returns undefined when there is nothing to close. For a trade opened with openTrade, use closeTrade. Callback only.

getBalances(): readonly BalanceView[]

manager.getBalances() returns per-asset cash balances, each with its own currency ({ asset, total, available?, reserved?, borrowed? }). Never add up different currencies. On a demo portfolio or in a backtest it is one row, the free cash, as long as everything traded is quoted in the wallet's currency.

getHoldings(): readonly HoldingView[]

manager.getHoldings() returns the full investment book, one line per holding, each amount with its currency. Read each line's valuation rather than computing quantity × price: a holding may have no live price, and an absent field means "not reported", never zero.

getReportedEquity(): MoneyView | undefined
getReportedAvailableFunds(): MoneyView | undefined
getReportedBuyingPower(): MoneyView | undefined

manager.getReportedEquity(), manager.getReportedAvailableFunds() and manager.getReportedBuyingPower() return provider-reported account figures, each { amount, currency }, or undefined when not reported. They are not spendable cash; do not size orders from them.

getMetrics(): Metrics

manager.getMetrics() returns nested run metrics: orderMetrics, tradeMetrics (counts, win rate, …), capitalMetrics (realized equity, maxDrawdown), swapFeesMetrics, latentMetrics and latentCapitalMetrics (mark-to-market, drawdown). There is no top-level drawdown. The high-water mark behind a drawdown is the highest value since the agent started, not the portfolio's all-time high.

getLatent(): LatentData

manager.getLatent() returns latent (unrealized) profit information. It throws until the wallet has been valued once, so call it from a callback after the first tick.

Capital (paper and demo runs)

Read Orders, trades and risk before sizing any order: these are not interchangeable. On a backtest or demo wallet the figures are in the wallet's currency (USD for backtests). When the agent runs against a connected real account, the same methods describe that account, in its currency; for an exact multi-currency report prefer getBalances(), getHoldings() and getReportedEquity().

getCurrentCapital(): number

manager.getCurrentCapital() is realized capital: everything invested plus realized profit and fees. It moves only when a position is closed or reduced (or capital is added); opening a position does not lower it.

getAvailableCapital(): number

manager.getAvailableCapital() is the free cash: current capital minus the cost of every open position. Deploy it without triggering an automatic top-up. It is 0 when fully invested and can be negative. Use this for "invest the remaining cash".

getLatentCapital(): number

manager.getLatentCapital() is the whole wallet's mark-to-market value (realized capital plus open positions at their last price). It is a value to monitor, not spendable cash, and it only refreshes on the next tick after a fill.

getInvestedCapital(): number

manager.getInvestedCapital() is the cumulative capital ever put in: the starting capital, every increaseCapital and every automatic top-up.

increaseCapital(amount: number): void

manager.increaseCapital(amount) adds cash to the wallet, for a contribution or a lump sum. Callback only.

State and settings

getStatus<T extends Status = StatusBag>(key: string): Partial<T>

manager.getStatus<T>(key) returns a persistent state bag that survives restarts. Pass a state shape as a type argument, for example manager.getStatus<{ count: number }>('default'); every field is optional because the bag starts empty, so initialize before you rely on it. The same key always returns the same live object: mutate it in place. In a live or demo run it is saved about every minute and on a clean shutdown (a crash can lose the last minute of changes); a backtest starts with an empty bag. Store JSON-like values only, and remove a field with delete (an undefined value is saved as null).

getVariable(name: string, defaultValue: number): number
getVariable(name: string, defaultValue: string): string
getVariable(name: string, defaultValue: boolean): boolean

manager.getVariable(name, defaultValue) reads a launch-time setting a user can change without editing the code (an indicator period, a threshold, a size). It returns the run's value when one was set with the same type, otherwise the default. Both arguments must be literals. Call it directly, once, into a root constant. Rules: Launch variables.

Communication and analysis

sendNotification(message: string): void

manager.sendNotification(message) sends the user a notification through the channels they enabled (the channel is never chosen in code). Mark key figures with **bold**. Send at most one notification per callback run: build a digest instead of notifying inside a loop (the builder refuses a notification sent from a loop). Does nothing in a backtest. Only notify when the user asked to be notified.

comment(comment: string): void

manager.comment(text) writes a note into the agent's journal, visible to the user in the app. Use it for an agent's reasoning or a one-off status. Not shown for backtests.

sendWebhook(payload: unknown): void

manager.sendWebhook(payload) posts a JSON payload to the endpoint the user configured on the agent. There is no URL argument; with none configured it simply does nothing. It never throws and returns nothing: do not await it or retry. Payloads over 64 KB are dropped, and deliveries are rate-limited (about 60 an hour per agent). Does nothing in a backtest.

llm<T>(llmName: string, systemPrompt: string, userPrompt: string, responseSchema: ZodType<T>, options?: { webSearch?: boolean; pieuvre?: boolean; news?: boolean; marketData?: boolean; images?: string[]; }): Promise<T | undefined>

manager.llm(name, systemPrompt, userPrompt, responseSchema, options?) asks a language model for a structured result validated against a z schema, and resolves to that object, or undefined on any failure (invalid result, credits exhausted, limits reached). Guard the result.

  • name is the model id. When you hand code to create_agent, Obside's builder sets the model (name the one you want in the prompt); any string passes validation, so a placeholder such as 'default' is fine in a draft.
  • options gives the model tools it calls on its own: webSearch (open web, billed), news (Obside's news search, billed), pieuvre (fundamental and macro reads, including the economic calendar), marketData (quotes, daily price history, and search_assets, which resolves names to trading keys for this run), and images (media ids from onMessage).
  • Limits per call: 12 model turns (tool calls included) and a spend cap; beyond them the call resolves to undefined. Out of AI usage in a live or demo run, the call also resolves to undefined until the monthly reset; the agent keeps running and keeps its positions.
  • On a schedule or candle callback, guard the call with a period key saved in the status bag (the validator requires it: TS9026). See News, data and AI.
  • In a backtest, llm only works inside an onMessage callback (X follows are replayed); anywhere else the backtest fails.

Use it for open-ended reasoning over unstructured input, never to source a hard number you can read from the market. Every schema field the model might not know must be .nullable().

searchNews(query: string, options?: SearchNewsOptions): Promise<NewsResult[]>

manager.searchNews(query, options?) returns matching news items, most similar first: { id, title, summary, url, source, publishedAt, score, instruments?, industries?, countries? }. Put tickers and keywords in query. options: limit (default 10, max 25), since and until (epoch ms), instruments, industries, countries, source, minScore (0 to 1). It resolves to [] when nothing matches and rejects when the news service is unavailable. Billed per query. Not backtestable.

getPieuvreMetric(concept: string, query?: PieuvreMetricQuery): Promise<PieuvreMetric | undefined>

manager.getPieuvreMetric(concept, { entity, asOf? }) returns a point-in-time fundamental or macro number for one entity as an object: read .value. entity (a ticker such as 'AAPL') is required for fundamentals. Margin, yield and return metrics come back as fractions (a 20% margin is 0.20). It resolves to undefined when the data point does not exist and rejects when the data service is unavailable. Backtestable (point in time).

getPieuvreSeries(sourceId: string, query?: PieuvreSeriesQuery): Promise<PieuvreSeriesPoint[]>

manager.getPieuvreSeries(sourceId, { from?, to?, limit?, resolution? }) returns a historical series (for example 'fred.fedfunds', 'fred.dgs10', 'composite.fear_greed'). Not backtestable.

getPieuvreRank(request: PieuvreRankRequest): Promise<PieuvreRankedEntity[]>

manager.getPieuvreRank(request) returns a point-in-time ranked list of entities, for a dynamic universe: { metric, universe?, order?, limit?, country?, sector?, … }. metric must be one of the metric names; universe is a universe tag such as 'sp500', 'us_equities', 'eu_equities', 'asia_equities' or 'crypto_top30' (an unknown tag silently returns []); order is 'asc' or 'desc'. Each entry is { ticker, name?, value, rank, snaptradeSymbol, backtestSymbol }: trade entry.snaptradeSymbol ?? entry.backtestSymbol and skip the entry when both are null. It ranks fundamentals only (no RSI or volume spike). Backtestable (point in time).

getPieuvreUpcoming(request: PieuvreUpcomingRequest): Promise<PieuvreEvent[]>

manager.getPieuvreUpcoming({ withinMs?, sourceIds?, minSeverity?, eventTypes?, tickers?, limit? }) lists scheduled, not-yet-released calendar events (earnings, macro releases) between now and withinMs milliseconds ahead. timestamp is the scheduled release time. For an upcoming earnings, eventType is 'earnings', ticker is the company and value is the consensus EPS estimate. Always narrow it with tickers (at most 200) or eventTypes, or a busy day's thousands of events hit the row limit. A calendar ticker is an identity, not a trading key. Not backtestable.

resolveAssets(request: ResolveAssetsRequest): Promise<ResolvedAsset[]>

manager.resolveAssets({ tickers, category? }) maps up to 200 tickers or names to the trading keys valid for the current run, one { ticker, name, class, tradingKey } per input, in order. Trade only a non-null tradingKey, verbatim; a null means not tradable on this run's venue. Keys it returns have their price warmed before it resolves. In a backtest it only resolves symbols the run loaded (your declared assets).

Control and diagnostics

stop(reason?: string): void

manager.stop(reason?) stops the agent from inside its own code: no callback fires afterwards, stops and take-profits are no longer watched. Use it for a one-shot agent after its single action fires. It does not cancel pending orders or close trades; do that first if needed.

handleError(error: unknown): void

manager.handleError(error) reports an error from a failed asynchronous operation. In a live or demo run it logs and the agent keeps running; in a backtest it fails the run, so reserve it for real failures. Pass it the error, never the work (a promise).

debugLog(...args: unknown[]): void
debugFlag(flag: string): void

manager.debugLog(...args) records values and manager.debugFlag(flag) records that a line was reached (a string-literal label). Both are visible in backtest results and are the way to answer "why did it never trade?". debugLog does nothing in a live run: use comment there.

Not part of the public API

V2Manager has a few more members that are internal or deprecated and should not be used: askNews (use searchNews), askPieuvre (use the structured getPieuvre… reads) and onAnyData.

Browse the documentation