Skip to content
DOCUMENTATION / Projects & factors
ONEPORT RESEARCH · P1

Projects & factors

On this page
P1

Projects & factors

Project model#

Every strategy is a project. A simple strategy may keep all code in its entrypoint; larger strategies can add Python modules and nested paths in the same file tree. The entrypoint owns the Strategy subclass and __data__ declaration, while supporting modules hold signals, portfolio rules or utilities.

PartPurpose
EntrypointThe executable Strategy module; marked in the project tree.
Supporting filesImportable Python modules organised by path. Add __init__.py where package imports require it.
DraftThe editable source tree. Every accepted change creates a new revision.
Frozen versionAn immutable source bundle used by backtests, batch studies and factor dependencies.

Python completion combines exact opbt SDK suggestions with static semantic analysis of the current scope, Python builtins and standard library, plus imports and inferred types across files in the same draft. Analysis reads source and type stubs without importing or executing project modules; requests are revision-bound so results never mix two draft versions.

Factor library#

Factors live in a separate library and follow the same draft-and-version model. Contract v2 records the callable, call style, output shape, value type and null policy. Strategy projects pin an exact Factor version under an alias and import it from opr_factors.<alias>.

The starter library covers per-instrument momentum, time-series mean reversion, cross-sectional reversal and a layered range-efficiency project. Each starter states its output meaning and recommended evaluation mode; these are observable contracts, not platform scores.

factor.py
def compute(factor_ctx, instrument, **factor_params):
    fast = int(factor_params.get("fast_window", 24))
    slow = int(factor_params.get("slow_window", 96))
    if not 2 <= fast < slow <= 2000:
        raise ValueError("windows must satisfy 2 <= fast < slow <= 2000")
    prices = factor_ctx.history(instrument, fields="close", window=slow + 1)
    if len(prices) < slow + 1:
        return None
    latest = float(prices["close"].iloc[-1])
    fast_base = float(prices["close"].iloc[-fast - 1])
    slow_base = float(prices["close"].iloc[-slow - 1])
    return float(latest / fast_base - latest / slow_base)
strategy.py
from opbt import Strategy
from opr_factors.momentum import compute as momentum_score

class FactorMomentumStrategy(Strategy):
    def handle_bar(self, ctx, bars):
        instrument = ctx.universe[0]
        score = momentum_score(
            ctx, instrument,
            fast_window=int(ctx.params.get("fast_window", 24)),
            slow_window=int(ctx.params.get("slow_window", 96)),
        )

Factor contract v2#

Contract fieldValuesMeaning
callable_namePython identifierExact function exported by the entrypoint; compute by convention.
call_stylePER_INSTRUMENT · CROSS_SECTIONOne instrument per call, or one complete symbol cross section.
output_kindSCALAR · CROSS_SECTIONOne value or a symbol-indexed cross section.
value_dtypeFLOAT64 · INT64 · BOOLThe value type enforced at the dependency boundary.
null_policyALLOW · DROP · ERRORPreserve missing observations, drop missing cross-section members, or fail on a missing return.
parameters_schemaobjectAllowed parameters, types, defaults and validation rules shared by evaluation and Factor users.
input_contractobjectVersioned data and invocation semantics.
output_schemaobjectThe result's meaning, unit and shape.

Parameter rules and evaluation values#

Versioned parameter contract for the Factor callable. Declare type: object, properties, required names, additionalProperties: false, and optional default/minimum/maximum/multipleOf/enum or x-constraints. Evaluation values are validated against it.

parameters_schema.json
{
  "type": "object",
  "properties": {
    "fast_window": {"type": "integer", "default": 24, "minimum": 2, "maximum": 1999},
    "slow_window": {"type": "integer", "default": 96, "minimum": 3, "maximum": 2000}
  },
  "required": [],
  "additionalProperties": false,
  "x-constraints": [
    {"left": "fast_window", "op": "<", "right": {"param": "slow_window"}}
  ]
}

JSON keyword arguments passed to the selected Factor callable. Names, types, required fields, defaults, ranges, enums, and cross-field constraints come from that frozen version's parameter contract; unknown keys are rejected and the encoded object is capped at 64 KiB.

Factor parameters · JSON
{
  "fast_window": 24,
  "slow_window": 96
}

Library lists and completed evidence#

A compact snapshot of evidence available to you. For a shared project, the normal library includes the latest successful standalone run from the Artifact owner or owning organization, while runs by other direct collaborators remain private. The row identifies the exact frozen version used; a saved row stays scoped to the version you saved. Completion is evidence for that version, not a platform quality verdict.

ListEvidence sourceWhat the row means
Strategy libraryLatest accessible successful standalone backtestFor shared projects this includes the Artifact owner's or owning organization's canonical history, but not another direct collaborator's private runs. The row shows the tested frozen version, return, annualised return, Sharpe and maximum drawdown; Study folds and deleted history are excluded.
Factor libraryLatest accessible successful Factor EvaluationShared-history boundaries match the Factor report page. Compact metrics use the shortest configured horizon, measured in bars: Rank IC mean, non-annualised Rank ICIR and top-minus-bottom return. Open the report for complete evidence.
Saved versionsThe exact version you savedOnly successful evidence for that frozen version is shown. Saving does not copy source, grant access or make a mutable draft immutable.
No completed evidenceLatest frozen version, or draft-onlyMetrics remain unavailable. An older report applies only to the version it tested, and missing values are not replaced with zero.

Sorting is applied by the server to the complete visible collection before pagination. Missing metrics stay last. Metric order uses the stored numeric value and is not a platform score; compare rows only after checking their version, window, universe, costs and label definition.

Factor evaluation#

An evaluation tests one frozen Factor version over a fixed data window and forward-return definition. The report covers data availability, IC, HAC, horizons, rolling results, equal-weight quantiles, a rank-weighted factor-mimicking return and target-weight turnover. Completed reports never change; if the methodology is updated, the platform creates a separate report for the same frozen version. These are research diagnostics, not a platform score.

Evaluation configuration#

ParameterDefaultDescription
factor_versiondraft → frozenEvaluation always executes an immutable contract-v2 Factor. Selecting the draft freezes its exact source and parameter contract before queuing.
analysis_modeTIME_SERIESTime-series diagnostics correlate each instrument through time. Cross-sectional diagnostics rank instruments at each timestamp and require enough valid instruments per cross-section.
window180dUTC decision window [start, end). Forward-label prices may extend beyond end by the largest horizon, but no factor decision at end is included. One evaluation is capped at 10,000,000 estimated label pairs.
frequency1hDecision grid and unit used by every forward horizon. For example, horizon 24 at 1h means 24 hours, while horizon 24 at 5m means 120 minutes.
label.horizons1, 6, 24Comma-separated future distances in bars: 1–32 unique integers, each 1–10,000. Labels use the exact close at t + horizon × frequency and remain missing when that timestamp is absent.
factor_params{}JSON keyword arguments passed to the selected Factor callable. Names, types, required fields, defaults, ranges, enums, and cross-field constraints come from that frozen version's parameter contract; unknown keys are rejected and the encoded object is capped at 64 KiB.
quantiles5Number of equal-rank groups, 2–10. Cross-sectional mode groups instruments at each timestamp; time-series mode groups observations within each instrument.
rolling_window20Number of recent raw timestamps or rows inspected by rolling diagnostics, 2–1,000; invalid pairs are filtered after each window is sliced, so a reported window can contain fewer valid samples.
minimum_observations20Minimum valid sample for diagnostics, 2–1,000. Insufficient samples are reported as such rather than converted to zero; HAC inference also enforces its stricter overlap-aware minimum.
minimum_cross_section3Cross-sectional mode only: minimum valid instruments required at one timestamp for IC, membership turnover and the v3 rank-weighted factor portfolio, 2–1,000. Quantile returns require at least the selected quantile count. This UI also keeps the threshold within the selected universe.
return_typeSIMPLESIMPLE = P(t+h) / P(t) - 1. LOG = ln(P(t+h) / P(t)). This changes the label definition, not the Factor output.

Labels are matched exactly at t + horizon × frequency. If that target timestamp is missing, the label stays missing; the evaluator does not substitute a nearby value or carry one forward. Adjusted-price series that use information unavailable at the decision time cannot be selected as labels.

Versions, restore & access#

You may add release notes when freezing a version. Restore copies that version into a new editable draft; the original version and every historical run that used it remain unchanged.

Saving a version creates only a private reference for the current user. It does not duplicate the version or its source, and it never grants access; current Artifact permissions are checked again whenever the saved list is read.

VIEWRead source and metadata.
RUNRun an available frozen version.
EDITEdit drafts and create versions.
MANAGEManage access and project lifecycle.
ONEPORT RESEARCH · DOCUMENTATION