Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# OpenBB Lab Helpers

This repository provides lightweight helpers that emulate the platform surface
of OpenBB Lab. The modules introduced in this change expose the backtesting and
risk engines at the top level and provide Arrow Flight integration helpers for
data ingestion services.

For usage examples see [docs/sdk.md](docs/sdk.md).
48 changes: 48 additions & 0 deletions docs/sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# OpenBB Lab SDK Helpers

The SDK exposes a thin layer around the platform engines so that notebooks and
external runtimes can interact with the Lab components without importing the
internal modules directly.

## Backtesting

Use `openbb_lab.sdk.run_backtest` to execute a strategy via a
`openbb_lab.platform.engine.BacktestEngine`. The helper forwards all arguments
to the engine and persists any tabular outputs as Apache Parquet files when an
`output_dir` is provided.

```
from pathlib import Path
from openbb_lab.sdk import BacktestEngine, run_backtest

engine = BacktestEngine()
result = run_backtest(engine, data=my_table, output_dir=Path("./artifacts"))
```

## Risk Checks

`openbb_lab.sdk.risk_check` runs a batch of `OrderTicket` instances through the
`RiskEngine` and returns a single `RiskAssessment` object describing the
violations discovered.

```
from openbb_lab.sdk import OrderTicket, risk_check

assessment = risk_check([
OrderTicket(symbol="AAPL", quantity=10, side="buy", price=188.2),
])
```

## Data Plane

The helper `openbb_lab.sdk.flight_register_table` registers callable readers
with a `FlightCatalog`. Pass one of the well known schema aliases (`"ticks"`,
`"bars"`, or `"depth"`) or your own `pyarrow.Schema` to describe the payload
returned by the reader.

```
from openbb_lab.sdk import FlightCatalog, flight_register_table

catalog = FlightCatalog()
flight_register_table(catalog, "sample_ticks", lambda: table, schema="ticks")
```
39 changes: 39 additions & 0 deletions openbb_lab/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Convenience exports for the OpenBB Lab helpers.

This lightweight package exposes the high level helpers that are designed to
be called from notebooks or foreign language bindings. The modules live under
``openbb_lab.platform`` but re-exporting them here keeps the public import path
short and backwards compatible with the older SDK layout.
"""

from .platform.engine import (
BacktestEngine,
BacktestResult,
OrderTicket,
RiskAssessment,
RiskEngine,
risk_check,
run_backtest,
)
from .platform.data_plane import (
BAR_SCHEMA,
DEPTH_SCHEMA,
FlightCatalog,
TICK_SCHEMA,
flight_register_table,
)

__all__ = [
"BAR_SCHEMA",
"BacktestEngine",
"BacktestResult",
"DEPTH_SCHEMA",
"FlightCatalog",
"OrderTicket",
"RiskAssessment",
"RiskEngine",
"TICK_SCHEMA",
"flight_register_table",
"risk_check",
"run_backtest",
]
33 changes: 33 additions & 0 deletions openbb_lab/platform/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Platform level helpers for OpenBB Lab."""

from .engine import (
BacktestEngine,
BacktestResult,
OrderTicket,
RiskAssessment,
RiskEngine,
risk_check,
run_backtest,
)
from .data_plane import (
BAR_SCHEMA,
DEPTH_SCHEMA,
FlightCatalog,
TICK_SCHEMA,
flight_register_table,
)

__all__ = [
"BAR_SCHEMA",
"BacktestEngine",
"BacktestResult",
"DEPTH_SCHEMA",
"FlightCatalog",
"OrderTicket",
"RiskAssessment",
"RiskEngine",
"TICK_SCHEMA",
"flight_register_table",
"risk_check",
"run_backtest",
]
129 changes: 129 additions & 0 deletions openbb_lab/platform/data_plane.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"""Data plane helpers built around Apache Arrow Flight."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable, Dict, Iterable, Mapping

import importlib.util

_pa_spec = importlib.util.find_spec("pyarrow")
if _pa_spec is None: # pragma: no cover - runtime guard
raise ModuleNotFoundError("The `pyarrow` package is required to work with Flight data.")

import pyarrow as pa

TICK_SCHEMA = pa.schema(
[
("timestamp", pa.timestamp("ns")),
("symbol", pa.string()),
("price", pa.float64()),
("size", pa.float64()),
("exchange", pa.string()),
("conditions", pa.list_(pa.string())),
],
)

BAR_SCHEMA = pa.schema(
[
("timestamp", pa.timestamp("ns")),
("symbol", pa.string()),
("open", pa.float64()),
("high", pa.float64()),
("low", pa.float64()),
("close", pa.float64()),
("volume", pa.float64()),
("vwap", pa.float64()),
("trades", pa.int64()),
],
)

DEPTH_SCHEMA = pa.schema(
[
("timestamp", pa.timestamp("ns")),
("symbol", pa.string()),
("side", pa.dictionary(pa.int8(), pa.string())),
("price", pa.float64()),
("size", pa.float64()),
("level", pa.int32()),
("orders", pa.int32()),
],
)

_SCHEMA_ALIASES: Dict[str, pa.Schema] = {
"tick": TICK_SCHEMA,
"ticks": TICK_SCHEMA,
"trade": TICK_SCHEMA,
"trades": TICK_SCHEMA,
"bar": BAR_SCHEMA,
"bars": BAR_SCHEMA,
"candle": BAR_SCHEMA,
"depth": DEPTH_SCHEMA,
"orderbook": DEPTH_SCHEMA,
"book": DEPTH_SCHEMA,
}


@dataclass
class FlightRegistration:
"""Metadata stored for a Flight table registration."""

schema: pa.Schema
reader: Callable[..., pa.Table]
metadata: Mapping[str, Any] | None = None


class FlightCatalog:
"""Registry of Arrow Flight table providers."""

def __init__(self) -> None:
self._tables: Dict[str, FlightRegistration] = {}

def register(self, name: str, registration: FlightRegistration) -> None:
self._tables[name] = registration

def get(self, name: str) -> FlightRegistration:
return self._tables[name]

def list(self) -> Iterable[str]:
return self._tables.keys()


def flight_register_table(
catalog: FlightCatalog,
name: str,
reader: Callable[..., pa.Table],
*,
schema: pa.Schema | str,
metadata: Mapping[str, Any] | None = None,
) -> None:
"""Register *reader* under *name* within *catalog* using a well-known schema."""

if not callable(reader):
raise TypeError("Readers passed to flight_register_table must be callable.")

selected_schema = _resolve_schema(schema)
catalog.register(name, FlightRegistration(schema=selected_schema, reader=reader, metadata=metadata))


def _resolve_schema(schema: pa.Schema | str) -> pa.Schema:
"""Resolve schema aliases for common market data layouts."""

if isinstance(schema, pa.Schema):
return schema

alias = schema.lower()
if alias not in _SCHEMA_ALIASES:
raise KeyError(f"Unknown schema alias: {schema!r}")

return _SCHEMA_ALIASES[alias]


__all__ = [
"BAR_SCHEMA",
"DEPTH_SCHEMA",
"FlightCatalog",
"FlightRegistration",
"TICK_SCHEMA",
"flight_register_table",
]
Loading