A strategy is your trading logic: Python code that reacts to market events and returns orders. Periscøpe runs the same code against historical data (backtest), live data with simulated fills (paper), or a real broker (live). Each Publish snapshots the code so every run is reproducible.
What a strategy contains
Every strategy is a small bundle of files.
-
strategy.py(required). A Python module that defines a class namedStrategyextendingStrategyBase. This is the entry point. See Strategy Protocol for the shape it must have. -
config_schema.json(required). The per-run settings your strategy exposes: symbol, size, thresholds, time windows, or anything else you define. Periscøpe uses it to build the run config form and validate the values before the strategy starts. The visual editor creates and edits this file for you through the Strategy Parameters panel; open the raw JSON only if you need advanced schema control. -
Optional helper
.pyfiles. Anything you want to factor out ofstrategy.py: indicators, utilities, data classes. Import them fromstrategy.pylike any Python module.
File constraints
- At most 50 files per strategy (
strategy.py,config_schema.json, plus up to 48 helpers). - At most 10 MB per file.
- At most 50 MB total across the strategy.
-
Helper filenames must match
[a-zA-Z0-9_-][a-zA-Z0-9_.-]*\.py.strategy.pyandconfig_schema.jsonare reserved names.
The strategy editor
The editor has three surfaces.
- A file tree on the left with your Python files and a Strategy Parameters entry.
- A Monaco code editor for Python files.
- A visual editor for your strategy parameters, with preview and import options if you prefer to paste raw JSON.
The sidebar also shows an Available Imports quick reference and an AI Assistant toggle. The full import surface is in the Strategy SDK.
If you want something concrete before reading the full reference, start with Strategy Examples.
Versioning
Strategies are versioned. Every time you click Publish, the backend creates a new strategy version with an auto-incremented number and a full snapshot of the bundle at that moment.
Runs bind to a specific strategy_version_id. Editing the
strategy later does not change what a previously created run executed.
This is the reproducibility guarantee of the platform.
Strategy parameters
Strategy parameters are the settings for a strategy. You define them when the trading logic stays the same but the run conditions change: symbol, position size, threshold, lookback window, or anything else your strategy reads. Instead of editing Python for each variation, you define those knobs once on the strategy and Periscøpe turns them into a validated run form. Changing values on a run does not require a new publish; changing the parameter definitions does.
Add parameters from the Strategy Parameters panel in the editor sidebar. Click +, give the parameter a snake_case key, pick a Type, and (optionally) add a description and a default. The description you enter appears as help text under the matching field in the run form. Drag rows to reorder; operators see parameters in that order when they create a run.
Parameter types
The Type picker has two groups. Generic types cover the primitives you would expect from any config form. System types are Periscøpe-specific and give the run form extra affordances: inline ticker validation, asset-type badges, and frequency pickers.
Generic types
- Text. A short string. The run form renders a text
input. In code, read with
require_str/optional_str. - Integer. A whole number. The run form renders a
number input. In code, read with
require_int/optional_int. - Decimal. A real number (prices, quantities,
thresholds). The run form renders a number input that accepts decimal
digits. In code, read with
require_decimal/optional_decimal; the extractor normalises toDecimal. - Boolean. A true/false flag. The run form renders a
checkbox. In code, read with
require_bool/optional_bool. - Choice. A string constrained to a fixed list of
allowed values. The editor lets you manage the list inline; the run
form renders a dropdown. In code, read with
require_str/optional_strand treat the result as one of the listed options.
System types
- Symbol. Any instrument reference (ticker, root
symbol, leg of a pair). When you create the parameter, pick its
Asset Type. Supported asset types today are
EQUITYandFUTURE. The run form shows the asset type as a badge next to the field, validates the operator's ticker against the security master, offers suggestions for typos, and provides a search popover. In code, read withrequire_symbol/optional_symbol; the extractor returns aSymbolwith.symboland.asset_type. For multi-symbol strategies, declare separate Symbol parameters per role (long_leg,short_leg,hedge). There is no array form today. - Bar Frequency. A bar aggregation period
(
second,minute,hour,day). The visual editor pins one value, and the run form shows it as a read-only display. In code, read withrequire_bar_time_span/optional_bar_time_span; the extractor returns aBarTimeSpan.
Advanced: editing the raw config_schema.json
The Strategy Parameters panel creates and updates
config_schema.json for you. You only need this section if
you are importing a schema from elsewhere, hand-editing the raw JSON,
or trying to use a JSON Schema feature the visual editor does not
expose.
config_schema.json is a flat JSON Schema (Draft 7)
document. Each entry under properties describes one
parameter; entries in the top-level required array are
required. System-type fields carry an x-periscope-type
annotation.
Symbol shape
{
"long_leg": {
"x-periscope-type": "symbol",
"x-periscope-asset-type": "EQUITY",
"type": "string",
"description": "Equity to go long on."
}
}
The operator submits a bare ticker; require_symbol parses
it into a Symbol. Only EQUITY and
FUTURE are accepted in
x-periscope-asset-type today.
Bar Frequency shape
// Fixed by the strategy
{
"bar_freq": {
"x-periscope-type": "bar_time_span",
"type": "string",
"const": "minute"
}
}
// Operator chooses from a list
{
"bar_freq": {
"x-periscope-type": "bar_time_span",
"type": "string",
"enum": ["minute", "hour"]
}
}
Values must be a subset of second, minute,
hour, day. The visual editor only emits the
const form; the enum form is valid in raw
schemas but is not yet authored from the editor.
Schema and code consistency
When a field is listed in required, read it in Python
with require_*. Otherwise read it with
optional_* and supply a default. Publishing runs a
consistency check between the schema and the code; mismatches surface
as validation errors.
What requires a new publish
Any change to the bundle. That means:
- Any change to
strategy.py. - Any change to
config_schema.json. - Adding, removing, or editing any helper file.
What does not require a publish is changing the values the config form asks for. Those live on the run, not the strategy. Create a new run or Copy an existing run and edit the config.
Publishing
Publishing is how your edits become a new runnable version. When you click it, the backend validates the bundle (file layout, schema shape, and a consistency check between the schema and the code) and then stores an immutable snapshot. Validation errors surface as toasts; fix them and click Publish again.
On success you get a toast with a Create run action that takes you straight to a new run preloaded with the new version.