Core Concepts
Strategy Contract (Shape-C)
.md↗Every new Finny strategy implements Shape-C — a Strategy class with on_bar, an injected broker, and explicit rules that prevent lookahead bias and unrealistic fills.
The Shape-C class
class Strategy:
def __init__(self, broker, params=None):
self.broker = broker
self.params = params or {}
def on_bar(self, symbol: str, bar: dict) -> None:
# bar keys: open, high, low, close, volume, timestamp, symbol
passThe runner instantiates Strategy once and calls on_bar for every bar in the backtest window or live stream. The broker instance is injected — you never import or build one.
Broker API
Two rules that trip up new strategies
Next-bar fill
Orders submitted inside on_bar fill at the next bar. Calling broker.buy() and then broker.position() in the same bar returns 0. Treat the position as updated only on the bar after the order.
Lookahead rule
Only bar["open"] is decision-time-safe. By the time you know high, low, or close, the bar is over — using them to decide an entry is lookahead bias and your backtest will lie to you.
Parameters
Read parameters from self.params with sensible defaults — that way a parameter sweep (see /backtest sweep) can actually vary them. Hard-coded constants make sweeps return identical metrics.
def on_bar(self, symbol, bar):
rsi_period = self.params.get("rsi_period", 14)
oversold = self.params.get("oversold", 30)
# ...Shape auto-detection
The backtest runner inspects the module to figure out which contract is in use:
- A
Strategyclass whose__init__takesbrokeras its first parameter → Shape C (current). - A legacy class name → Shape A, routed through a v1 compatibility adapter.
- Neither → the runner aborts with a hint pointing at this page.
The Shape-C loader also accepts simpler strategy formats and maps them into the institutional engine_v2 — see the /backtest reference.