OnePort Research documentation
Research starts in a versioned project. A project may contain one Python file or a complete source tree with pinned Factor versions. Backtests execute that immutable project version through the opbt SDK; each run records its own dates, universe, parameters and cost assumptions.
On this page
Quickstart
From project to report#
Run configuration reference#
These values become part of the run configuration. The table shows the current Run-panel defaults; REST and MCP clients should still send every research assumption explicitly.
| Parameter | Default | Description |
|---|---|---|
| start / end | — | UTC half-open interval [start, end): start is included and end is excluded. End cannot be in the future. Resource admission governs long runs; if admission is disabled, the fallback maximum span is 1,095 days. |
| frequency | 1h | Bar and decision frequency. OPR aggregates supported data to this interval; the strategy runs only at timestamps available on that grid. |
| universe | — | Choose an exchange, product type and official symbol. Backtest and Paper currently support SPOT and linear UPERP when K-line data is available for the selected period. Order, fill and position quantities are always coin amounts. CPERP is not available for execution yet; older runs remain available for review but cannot be restarted. |
| base_coin | USD | Frozen denomination: USD/BTC/ETH/SOL/XRP. |
| accounts | 1 account | One to 16 isolated accounts with their own initial_assets. |
| instrument_account_map | single account: optional | Exact candidate routes; mandatory for multiple accounts. |
| leverage_max | 3x | Maximum estimated leverage after an order: reporting-currency debt plus derivative gross notional, divided by monetary equity. Positive spot assets do not enter the numerator. The range is above 0 and up to 10x; the default is 3x. Orders that would increase leverage above the limit are rejected, while reductions remain allowed. Simulations do not model collateral haircuts or liquidation. |
| slippage_bps | 2 bps | Backtest applies this adverse adjustment to the current real completed-bar close logical BBO after the strategy decision. It never uses an unseen next open. Paper instead reads the current OnePort best bid and ask when an order is submitted; it does not use a K-line price as the quote. Unit: bps (1 bp = 0.01%); range 0–1,000. |
| maker_fee | 1 bps | Fee charged on passive limit-fill notional. Unit: bps; range 0–1,000. Enter the research assumption explicitly rather than relying on a venue default. |
| taker_fee | 4 bps | Fee charged on market-fill notional. Unit: bps; range 0–1,000. It is recorded separately from modelled slippage. |
| params | {} | A JSON object exposed to strategy code as ctx.params. Keys and meanings are defined by the project; values must be finite JSON and the encoded object must not exceed 64 KiB. Fixed and searched parameter names cannot overlap. |
Instrument format#
Instruments use venue:TYPE:SYMBOL, for example binance:UPERP:BTCUSDT. Symbols keep each venue's native naming. The Run panel reads the executable venue, product and symbol combinations from the data catalog.
Dynamic universe#
A dynamic run declares venues and product types instead of hand-written symbols. A backtest freezes the union present during its window and uses membership plus real bars at each decision; it does not require full-window survival or 95% completeness. Confirm a changed preflight again. Running Paper/Live can discover newly listed instruments at selection boundaries.
from opbt import Strategy
class DailySelectionStrategy(Strategy):
def initialize(self, ctx):
# Candidate identities are not proof of historical eligibility.
ctx.log.info("candidates=%s", len(ctx.candidate_universe))
def select_universe(self, ctx, candidates):
# Called on each frozen DAILY or EVERY_N_BARS decision. candidates contains
# only eligible instruments with a real bar at that decision timestamp.
history = ctx.history_many(candidates, fields="turnover", window=24)
ranked = sorted(
candidates,
key=lambda instrument: float(history[instrument]["turnover"].sum()),
reverse=True,
)
return ranked[:10]
def handle_bar(self, ctx, bars):
# bars and ctx.universe contain only the active selection for this day.
passLegacy Paper/Live receipts without active_limit_mode migrate to ALL_CANDIDATES at the next normal selection boundary. The old numeric limit is recorded in active_limit_migration for audit, not retained as a trading cap. These deployments do not require select_universe to expand. Only recorded EXPLICIT limits remain fixed: expanding their candidate pool requires checkpoint evidence that the current frozen strategy implements select_universe. Without that callback, an EXPLICIT deployment keeps its existing pool. Migration does not reset strategy state, balances or orders, or automatically resume stopped deployments.
One-shot notebooks#
An .ipynb file runs as a bounded Notebook Job: all Python cells execute once in order, outputs are stored in an immutable executed-notebook snapshot, and the process exits. There is no persistent kernel, terminal, shell magic or network access. Notebook Jobs can import saved project modules and locked packages. Stored output is capped at 256 KiB per cell and 2 MiB per display job; runtime errors remain visible when earlier output is truncated. Use backtests and Factor Evaluation whenever the research needs OPR point-in-time market data.

