Plugin library

DocumentationWriting strategies

Plugin library

31 plugins written by OrionQuant as templates: data sources that fetch keyless public data before a run, indicators, strategy templates and exporters. None is switched on by itself. Install one from the Plugins view (New plugin, From library), read its code, change it, and switch it on. Plugins are Python and run in the desktop app only, in the engine on your machine; how to write your own is on the Plugins API page.

Every data source stores each value under the first day it was known, never the day it describes, and carries it forward over days without a release, so ctx.data(name, date) never hands a strategy a value it could not have had at that bar. The data is fetched on your machine from the publisher and is never republished by OrionQuant.

Overview

PluginKindWhat it does
Exchange calendar (exchange_calendar)Data sourceHolidays, early closes, the days around a holiday, the turn of the month and expiry days for NYSE, LSE, Xetra, Tokyo and FX, as feeds.
Binance spot bars (binance_bars)Data sourceDownloads closed Binance spot bars from M1 to D1 for crypto pairs like BTCUSDT, and writes daily close feeds for pairs a strategy wants to watch.
ECB euro reference rates (ecb_fx_reference)Data sourceThe daily euro reference rates of the European Central Bank for about 30 currencies since 1999, one feed per currency, plus the cross for your pair.
US macro releases (FRED) (fred_macro_releases)Data sourceCPI, core CPI, core PCE, payrolls, unemployment, jobless claims, industrial production and retail sales from FRED, each under a conservative release date.
CFTC Commitments of Traders (cftc_cot)Data sourceWeekly futures positioning from the CFTC for the market behind your instrument: speculators, leveraged funds, asset managers and a 0 to 100 COT index.
Central bank policy rates (central_bank_rates)Data sourceDaily policy rates of the Fed, ECB, BoE, BoJ, SNB, BoC, RBA, RBNZ, Norges Bank and Riksbank, with the last change and the rate gap of your pair.
US rates curve (us_rates_curve)Data sourceUS Treasury yields from 3 months to 30 years, the real 10 year yield and the fed funds rate, with curve spreads and breakeven inflation, from the Treasury and the New York Fed.
Crypto Fear & Greed Index (crypto_fear_greed)Data sourcealternative.me's daily crypto sentiment number, 0 (extreme fear) to 100 (extreme greed), with its label as feeds.
VIX close (vix_data)Data sourceThe daily Cboe VIX close from FRED as a feed, so a strategy can stand aside when volatility is high.
Heikin Ashi (heikin_ashi)IndicatorHeikin Ashi candles for a strategy: HA open, high, low and close, the candle color, the run of same colored candles and a no opposite wick flag.
Renko Bricks (renko_bricks)IndicatorRenko bricks built from bar closes, fixed or start ATR size: direction, last brick level, new bricks this bar, run length and reversals.
Bollinger Bands (how to write an indicator) (bollinger_bands)IndicatorA short streaming indicator to copy from: one bar in, the middle, upper and lower band out.
Prior period levels (prior_period_levels)IndicatorYesterday's, last week's or last month's open, high, low and close, the running period's range, and how far price is from them in ATR.
Opening range (opening_range)IndicatorThe high and low of the first minutes after a market opens, its size in daily ATR and where price is now, never before the window has closed.
Trading sessions (trading_sessions)IndicatorWhich FX centre is open on each bar, the London and New York overlap, and the high and low of the session you follow.
Pair correlation and beta (pair_correlation_beta)IndicatorRolling correlation, beta and relative strength of the instrument's daily returns against another market, a yield or any feed you have.
Seasonality (seasonality)IndicatorHow the instrument did in past months, weekdays, turns of the month or hours like the current one: mean return, hit rate, count and t-statistic.
Fair Value Gaps (fair_value_gaps)IndicatorThree bar fair value gaps with a minimum size in ATR: the nearest open gap on each side, how far each is filled, and an event when one is filled.
Market Structure (market_structure)IndicatorConfirmed swing highs and lows without repainting, the trend they form (HH and HL or LH and LL), and the last break of structure and change of character.
Market Structure Toolkit (market_structure_toolkit)IndicatorAn example that bundles the nineteen catalogue elements of the market structure group (BoS, CHoCH, FVG, order blocks, Fibonacci, OTE, liquidity, kill zones) in one plugin indicator.
London breakout (london_breakout)Strategy templateTrades the break of the Asian range in the London session: buffer, stop at the other side, target in R, out by the New York afternoon.
Opening range breakout (opening_range_breakout)Strategy templateTrades the break of the first minutes after a market opens: one trade a day, stop at the other side, target in R, flat before the close.
RSI trend pullback (rsi_trend_pullback)Strategy templateBuys short, sharp dips while price is above its 200 bar SMA and sells rallies below it, out when RSI(2) recovers or price crosses the 5 bar SMA.
Power of Three (power_of_three)Strategy templateICT's Power of Three from the market structure blocks: the Asian range, a London sweep of it, and the trade the other way until the London close.
Silver Bullet (silver_bullet)Strategy templateICT's Silver Bullet from the market structure blocks: in a kill zone a liquidity sweep, then a limit order in the fair value gap that follows.
Turtle breakout (turtle_breakout)Strategy templateThe Turtle Traders' published rules: channel breakouts, a stop at 2N, units sized by risk and the System 1 skip-after-winner filter.
Webhook summary (webhook_summary)ExporterPosts a run's headline figures to a webhook of your own, as generic JSON, a Discord message or a Slack message. HTTPS only, summary only by default.
CSV report (csv_report_exporter)ExporterWrites a finished backtest as one CSV file: the headline statistics, then every trade.
Notebook JSON export (notebook_json_export)ExporterWrites a whole run as one versioned, documented JSON file plus a trades CSV and a JSON Schema, ready for pandas or any notebook.
Realised P&L by year (realised_pnl_by_year)ExporterSums a run's closed trades per calendar year and instrument: gross gains, losses, commission, swap and net, as CSV and Markdown, in EUR when ECB rates are cached.
Trade journal CSV (trade_journal_csv)ExporterWrites every trade of a run as one CSV row for your trading journal, with local times, session, pips, R and empty columns for notes.

Data source

Exchange calendar (exchange_calendar)

Holidays, early closes, the days around a holiday, the turn of the month and expiry days for NYSE, LSE, Xetra, Tokyo and FX, as feeds.

Kind: Data source. Category: Calendar. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Calendar effects are some of the oldest ideas in trading: the day before a holiday, the turn of the month, the week of an expiry. This plugin gives a strategy the calendar to test them, without any network: it computes each exchange's days from the published holiday rules, year by year, plus a dated list of one-off closures (11 September 2001, the funerals of presidents and of Queen Elizabeth II, Hurricane Sandy, the Tokyo system failure of October 2020).

Before a run it writes one feed per calendar: calendar_nyse, calendar_lse, calendar_xetra, calendar_jpx and calendar_fx. Each day holds an object:

  • open: whether the exchange trades that day; holiday: the name of the holiday on a closed weekday.
  • early_close and early_close_utc: the local and UTC time of a shortened session (NYSE 13:00, LSE 12:30), else None.
  • before_holiday, after_holiday: the last trading day before and the first after a holiday.
  • days_to_holiday, days_from_holiday: trading days in between (0 on the day before or after).
  • month_day, month_days_left: the trading day of the month counted from the start and the trading days still to come, for turn of month rules.
  • expiry, quarterly_expiry: the monthly index options expiry, the third Friday (the second Friday for Tokyo), moved to the trading day before when that Friday is a holiday.

calendar_fx has no exchange behind it: weekends, Christmas Day and New Year's Day are closed, and thin with a reason marks the days most of the market is away (24, 26 and 31 December, Good Friday, Easter Monday, US Thanksgiving and Independence Day).

The CME is not included: its holiday sessions differ by product and year. For US equity index futures, the NYSE calendar is a close guide to trade dates; the CME's shortened holiday sessions are not modelled.

Configuration

KeyDefaultWhat it does
exchanges"nyse, lse, xetra, jpx, fx"Which calendars to write, separated by commas: nyse, lse, xetra, jpx, fx.
first_year2000The first year written. Xetra starts in 2012 whatever is set here, the first year of the official lists the plugin follows.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    day = ctx.data("calendar_nyse", bars[-1].ts)
    if day is not None and day["before_holiday"]:
        ctx.signal("EXIT")
    elif day is not None and day["month_days_left"] == 0:
        ctx.signal("LONG")

The calendars are written before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked, and again only when the year or the plugin's rules change. No network is used. A run on the server never writes them.

What it provides

  • calendar_<exchange>: One feed per calendar (calendar_nyse, calendar_lse, calendar_xetra, calendar_jpx), every calendar day from the first year to the end of next year, each an object with the fields above.
  • calendar_fx: The FX days by UTC date: open, thin, reason, the holiday fields and the turn of the month; no expiry fields.

Data source and terms

When values are known

Each value sits under the day it describes, unlike the other feeds: holiday rules are published a year or more ahead, so the calendar of a day and the weeks after it was known before the day began. One-off closures (11 September 2001, a funeral, a storm) carry the day they were announced, and the forward looking fields (before_holiday, days_to_holiday, month_days_left, expiry) of a day count only the closures announced by then. On 10 September 2001 the feed does not know that the next day is closed. Where an exchange's own notice could not be dated, the announcement is taken as the day after the government order, or the day itself.

Limits

  • NYSE, LSE and Tokyo from 2000, Xetra from 2012. The rules were checked against the NYSE notices, gov.uk and the Japanese Cabinet Office list; a closure missing from the dated list would show as a trading day.
  • Next year's one-off closures cannot be known yet, and Xetra's list for a new year follows the usual eight days until Eurex publishes it.
  • Early closes only for NYSE (13:00 New York) and LSE (12:30 London); Tokyo's old half days and Xetra's shortened year end sessions are not included, nor are trading halts during a day.
  • The days are the exchange's own dates; a bar is matched by its UTC date, which is the same date during the regular session of all four exchanges, not for an evening futures session.
  • The FX thin days are a market convention, not a rule; brokers differ on which days they close.

Binance spot bars (binance_bars)

Downloads closed Binance spot bars from M1 to D1 for crypto pairs like BTCUSDT, and writes daily close feeds for pairs a strategy wants to watch.

Kind: Data source. Category: Crypto. Version: 1.0.0. Network: data-api.binance.vision.

Runs in the desktop app only (the engine on your machine).

A download source for crypto pairs. In the Data view, choose Binance spot as the source, type a Binance spot symbol (BTCUSDT, ETHUSDT) and a timeframe from M1 to D1. The plugin pages through Binance's public market data host 1,000 bars at a time, half a second apart, and the engine stores the bars like any other download.

Only closed bars are kept: the bar still forming at the moment of the download is left out, so a backtest never sees a candle that was not finished. Times are UTC, each bar under its open time.

The plugin also writes the daily close of the pairs in Daily close feeds as binance_<pair>_close (BTCUSDT by default), for strategies that look at another market, for example a filter on Bitcoin while trading something else.

Configuration

KeyDefaultWhat it does
cache_days1Days before the close feeds are downloaded again. Within that time a run reads the cached feeds and needs no network.
close_feeds"BTCUSDT"Pairs whose daily close is written as a feed before a run, separated by commas, for example BTCUSDT, ETHUSDT. Empty writes none. The bar download does not use this setting.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    btc = ctx.data("binance_btcusdt_close", bars[-1].ts)
    if btc is not None and btc < 20000:
        ctx.signal("EXIT")

For the bars themselves no code is needed: download them in the Data view with Binance spot as the source and run a backtest on the pair. The close feeds are refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches.

What it provides

  • binance_spot: The download source: closed OHLCV bars in UTC for M1, M5, M15, M30, H1, H4 and D1. Volume is in the base asset (BTC for BTCUSDT).
  • binance_<pair>_close: The daily close of each pair in Daily close feeds, a number, under the day after the UTC day it closes.

Data source and terms

When values are known

A bar is complete at its close, and only closed bars are stored, each under its open time as every bar in the engine. A daily close feed value is stored under the next UTC day, when the day's bar has closed.

Limits

  • Binance refuses some countries with HTTP 451 (unavailable for legal reasons). The download then stops with a message saying so; the plugin cannot work around it.
  • Only pairs trading today can be chosen; pairs Binance delisted are missing, so a test over "all coins" leans towards the survivors.
  • One exchange's prices and volume, not a market wide index. Spot only, no futures and no funding.
  • A pair the engine does not know, such as BTCUSDT, needs its asset class set to crypto or a custom symbol before a run. The data stays on your machine and is for your own use under Binance's terms.

ECB euro reference rates (ecb_fx_reference)

The daily euro reference rates of the European Central Bank for about 30 currencies since 1999, one feed per currency, plus the cross for your pair.

Kind: Data source. Category: Currencies. Version: 1.0.0. Network: data-api.ecb.europa.eu.

Runs in the desktop app only (the engine on your machine).

Every TARGET business day the ECB publishes one reference rate per currency against the euro, around 16:00 CET. It is an official daily fixing, used in contracts, accounting and tax, and a neutral yardstick next to your broker's prices.

Before a run, the plugin downloads the rates from the ECB data portal and writes one feed per currency, named ecb_eur plus the currency code in lower case: ecb_eurusd, ecb_eurjpy, ecb_eurgbp. The value is units of that currency per 1 euro, as the ECB quotes it. When the run's instrument is a currency pair without the euro, such as GBPUSD, it also writes the cross ecb_gbpusd (US dollars per pound), computed day by day from the two euro rates.

Other plugins can rely on these names, for example to convert results to euro.

Configuration

KeyDefaultWhat it does
cache_days1Days before the plugin downloads the rates again. Within that time a run reads the cached feeds and needs no network.
currencies"all"Which currencies to fetch: all (every currency the ECB publishes, about 15 MB) or a list of codes such as USD,JPY,GBP for a smaller download. The two currencies of the run's pair are always added.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    fix = ctx.data("ecb_eurusd", bars[-1].ts)  # US dollars per euro, the last fix known
    if fix is not None and bars[-1].close > fix * 1.01:
        ctx.signal("EXIT")

The feeds are refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches.

What it provides

  • ecb_eur<ccy>: One feed per currency, the code in lower case (ecb_eurusd, ecb_eurjpy, ecb_eurchf): units of that currency per 1 euro, under each day from the day after the fix.
  • ecb_<base><quote>: Only when the run's instrument is a currency pair without the euro (GBPUSD gives ecb_gbpusd): quote currency per 1 unit of the base currency, the ratio of the two euro rates, rounded to 6 significant digits.

Data source and terms

When values are known

The ECB publishes the rates around 16:00 CET on the day they are for, so each rate is stored under the next calendar day and carried forward over weekends and TARGET holidays, for at most 7 days. A bar on Monday reads Friday's fix; a bar during the day of a fix never sees it, not even after 16:00.

Limits

  • A reference rate is a snapshot at about 14:10 CET for information; it is not a price you could have traded at, and it says nothing about the rest of the day.
  • Only currencies the ECB quotes, and only on TARGET business days. A currency the ECB stopped quoting (RUB since March 2022, BGN since Bulgaria joined the euro) ends 7 days after its last fix.
  • Crosses are computed from two euro rates, not quoted by the ECB, so they can differ from market prices in the last digits.
  • The data is downloaded to your machine and stays there; the ECB asks for the source line "Source: ECB statistics" wherever you show it.

US macro releases (FRED) (fred_macro_releases)

CPI, core CPI, core PCE, payrolls, unemployment, jobless claims, industrial production and retail sales from FRED, each under a conservative release date.

Kind: Data source. Category: Macro data. Version: 1.0.0. Network: fred.stlouisfed.org.

Runs in the desktop app only (the engine on your machine).

Inflation, the labour market and activity set the backdrop for rates, the dollar and equity indices. This plugin brings eight US headline series into a backtest as feeds, for regime filters such as "no new longs while CPI inflation is above 4 percent".

Before a run, it downloads each series from FRED and writes the level plus its changes: fred_cpiaucsl (CPI, all items), fred_cpilfesl (core CPI), fred_pcepilfe (core PCE prices), fred_indpro (industrial production) and fred_rsafs (retail sales), each with _yoy and _mom in percent; fred_payems (nonfarm payrolls, thousands) with _yoy and _chg (jobs added, thousands); fred_unrate (unemployment rate) with _chg (percentage points); fred_icsa (initial jobless claims, weekly) with _yoy and _chg (week over week).

FRED dates a value by the period it describes, but it came out weeks later. Every value here is stored under the day after its release, worked out per series from the release dates FRED lists itself, so a strategy never reads a figure before it was published.

Configuration

KeyDefaultWhat it does
cache_days1Days before the plugin downloads a series again. Within that time a run reads the cached feeds and needs no network.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    inflation = ctx.data("fred_cpiaucsl_yoy", bars[-1].ts)  # CPI, percent over a year
    if inflation is not None and inflation > 4.0:
        ctx.signal("EXIT")

The feeds are refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches. To trade the release itself, use the economic calendar (ctx.news_window), not these feeds.

What it provides

  • fred_cpiaucsl, fred_cpilfesl, fred_pcepilfe: CPI, core CPI and core PCE price indices (index levels), each with _yoy and _mom: the change against a year and a month earlier, in percent.
  • fred_indpro, fred_rsafs: Industrial production (index) and retail and food services sales (millions of dollars), each with _yoy and _mom in percent.
  • fred_payems, fred_unrate: Nonfarm payrolls (thousands) with _yoy in percent and _chg, the jobs added in the month (thousands); the unemployment rate (percent) with _chg in percentage points.
  • fred_icsa: Initial jobless claims of a week (persons, seasonally adjusted) with _yoy in percent against 52 weeks earlier and _chg against the week before.

Data source and terms

When values are known

Each value is stored under the day after its release and carried forward until the next release, for at most 70 days. The release date comes from a rule per series that is never earlier than the real release in FRED's release date lists since 1994 (CPI known from the 17th of the next month since the December 2017 index, the 1st of the month after next before that; payrolls and unemployment from the 14th, industrial production and retail sales from the 20th, core PCE from the 9th of the month after next, claims from the Saturday after the week), plus a table of releases that came later, among them the shutdowns of 1995/96, 2013, 2018/19 and 2025. Before 1994 the rules are coarser and later still. A strategy usually sees a figure a few days after it came out, never before.

Limits

  • Values are FRED's latest revisions, not the figures first published: payrolls, retail sales and the seasonally adjusted indices are revised for months and years. A backtest sees today's view of the past at the day of the first release, which flatters any rule that trades on surprises.
  • Not for trading the release: a figure is known from the next day, and the dates are conservative. For the event and the consensus, use the economic calendar (ctx.news_window).
  • A release delayed after this version was written (a new shutdown) is stored under its regular rule and can show up too early; the late release table in the code is where to add it.
  • FRED's terms: downloads are for personal, non-commercial use, and the terms prohibit storing or caching FRED content, while this plugin keeps a local cache of the series on your machine. The series themselves are public domain US government data; commercial users should take them from the primary publishers (BLS, BEA, Census Bureau, Federal Reserve, Department of Labor).
  • With your own FRED API key under Settings, API keys, a strategy can read any FRED series itself with ctx.source("fred"), under FRED's API terms; that route needs no plugin.
  • October 2025 has no CPI and no unemployment rate (not collected during the shutdown); a change against a missing month is left out rather than guessed.

CFTC Commitments of Traders (cftc_cot)

Weekly futures positioning from the CFTC for the market behind your instrument: speculators, leveraged funds, asset managers and a 0 to 100 COT index.

Kind: Data source. Category: Positioning. Version: 1.0.0. Network: publicreporting.cftc.gov.

Runs in the desktop app only (the engine on your machine).

Every week the US Commodity Futures Trading Commission (CFTC) publishes who holds the open futures positions in each market: large speculators, commercial hedgers and, in the newer reports, leveraged funds, asset managers and managed money. Many traders read extremes in speculator positioning as a sign of a crowded trade.

Before a run, the plugin finds the futures market behind the run's instrument (EURUSD reads the CME Euro FX contract, XAUUSD COMEX gold, US500 the E-mini S&P 500), downloads its history since 1993 and writes the feed cot_<market>, for example cot_eur or cot_gold. Each day holds the latest report known that day as an object: net speculator position, its weekly change and share of open interest, net commercials, open interest, net leveraged funds and asset managers (financial futures) or net managed money (commodities), and a COT index: where the net speculator position sits in the range of the last reports, from 0 (lowest) to 100 (highest).

An FX pair reads the contract of each non dollar leg: EURJPY writes cot_eur and cot_jpy. A currency contract is long that currency against the US dollar, so for USDJPY, USDCHF and USDCAD the numbers point the other way: speculators long yen are short USDJPY.

Markets: eur, jpy, gbp, chf, cad, aud, nzd, mxn, usd_index, sp500, nasdaq100, dow, russell2000, nikkei, ust10y, bitcoin, ether, gold, silver, copper, platinum, wti, natgas.

Configuration

KeyDefaultWhat it does
cache_days1Days before the plugin downloads a market again. Within that time a run reads the cached feed and needs no network.
index_weeks156How many reports the COT index looks back over, the current one included. 156 is about three years, 52 one year. The index stays empty until that many reports exist.
markets"auto"Which markets to fetch, separated by commas. auto (the default) is the market behind the run's instrument; add keys to fetch more, for example auto, usd_index. An instrument with no CFTC market gets no feed and a line in the plugin log.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    cot = ctx.data("cot_eur", bars[-1].ts)
    if cot is not None and cot["cot_index"] is not None:
        if cot["cot_index"] > 90:
            ctx.signal("EXIT")

The feed is refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches. A value is None before the first report the plugin has.

What it provides

  • cot_<market>: One feed per market (cot_eur, cot_gold, cot_sp500 and so on). Each day holds an object: report_date, released, open_interest, net_speculators, net_speculators_change, net_speculators_pct_oi, net_commercials, cot_index, and net_leveraged_funds with net_asset_managers (financial futures) or net_managed_money (commodities). Positions are in contracts; a field the report does not carry that week is None.

Data source and terms

When values are known

A report shows positions as of Tuesday and is published on Friday at 15:30 New York time, three business days later, one day later for each federal holiday or government closure in between. Each report is stored under the first weekday after its publication: Friday's report is Monday's value, a report published on a Monday after a holiday week is Tuesday's. Even a crypto strategy sees it from Monday, one rule for every market. The reports delayed by the government shutdowns of 2013, 2018 to 2019 and 2025 use the publication dates the CFTC announced. Values are carried forward for at most 70 days; report_date says how old a value is.

Limits

  • Weekly data: a value is six or more days old when it becomes known (Tuesday positions, known Monday) and stays the same for a week.
  • The publication dates are computed from the CFTC's rule, a list of federal holidays and closures, and a table of the delays the CFTC announced. Where the CFTC gave only a week, the last day of it is used. The table may miss a delay or a closure, so a value can be a day early in a week nobody wrote about.
  • The CFTC corrects reports now and then. The download holds today's corrected figures, not the ones first published.
  • Futures only, no options. Reports before 1993 are left out (the CFTC published them later and less often). Trader groups start in June 2006, bitcoin in 2018 and ether in 2021. Brent, the DAX and other markets outside US exchanges have no CFTC report.

Central bank policy rates (central_bank_rates)

Daily policy rates of the Fed, ECB, BoE, BoJ, SNB, BoC, RBA, RBNZ, Norges Bank and Riksbank, with the last change and the rate gap of your pair.

Kind: Data source. Category: Interest rates. Version: 1.0.0. Network: stats.bis.org.

Runs in the desktop app only (the engine on your machine).

The policy rate is the rate a central bank steers; it drives the carry of a currency pair and is the backdrop of most macro trades. The Bank for International Settlements collects the daily policy rates of central banks in one dataset, WS_CBPOL.

Before a run, the plugin downloads the rates of ten central banks and writes three feeds per currency: cbrate_usd (the rate in percent), cbrate_usd_days (days since the last change became known) and cbrate_usd_dir (1 when that change was a hike, -1 a cut). The currencies are usd (Fed), eur (ECB), gbp (Bank of England), jpy (Bank of Japan), chf (SNB), cad (Bank of Canada), aud (RBA), nzd (RBNZ), nok (Norges Bank) and sek (Riksbank).

When the run's instrument is a pair of two of these currencies, it also writes cbrate_gap_ plus the pair, for example cbrate_gap_audjpy: the base currency's rate minus the quote currency's, in percentage points. Positive means a long position in the pair earns the interest difference, before your broker's costs.

Configuration

KeyDefaultWhat it does
cache_days1Days before the plugin downloads the rates again. Within that time a run reads the cached feeds and needs no network.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    gap = ctx.data("cbrate_gap_audjpy", bars[-1].ts)  # AUD rate minus JPY rate, in points
    if gap is not None and gap < 0:
        ctx.signal("EXIT")  # no carry left for a long AUDJPY

The gap feed exists for the pair of the run (here an AUDJPY run). The feeds are refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches.

What it provides

  • cbrate_<ccy>: The policy rate in percent per year for usd, eur, gbp, jpy, chf, cad, aud, nzd, nok and sek (cbrate_usd, cbrate_eur, ...), three business days after the day it applies.
  • cbrate_<ccy>_days: Calendar days since the last change of that rate became known; starts at the first change in the data.
  • cbrate_<ccy>_dir: The direction of that last change: 1 for a hike, -1 for a cut.
  • cbrate_gap_<base><quote>: Only for a run on a pair of two covered currencies (AUDJPY gives cbrate_gap_audjpy): the base rate minus the quote rate in percentage points, on days both are known.

Data source and terms

When values are known

The BIS dates a rate by the day it applies. Most changes are announced days earlier, but some took effect the morning they were announced (Bank of England on 11 March 2020, Bank of Canada on 4 March 2020), and the early history of some series is a market rate published the next business day. So each value is stored three business days after the day it applies and carried forward for at most 45 days: a change shows up a few days late, never early.

Limits

  • Not for trading the decision itself: a change appears days after the announcement. For the event, use the economic calendar (ctx.news_window).
  • The policy rate is not the swap or rollover your broker charges; the gap shows the direction and size of carry, not what a position would have earned.
  • Before a bank set a fixed policy rate, the BIS lists a market rate for some areas (the US effective federal funds rate in the early decades, for example); there it moves daily and the days and direction feeds mean little.
  • The BIS updates the dataset about weekly and some areas lag behind; a rate changed since the last update shows the old value until the BIS has it. The data stays on your machine; the BIS asks to be named as the source.

US rates curve (us_rates_curve)

US Treasury yields from 3 months to 30 years, the real 10 year yield and the fed funds rate, with curve spreads and breakeven inflation, from the Treasury and the New York Fed.

Kind: Data source. Category: Interest rates. Version: 1.0.0. Network: home.treasury.gov, markets.newyorkfed.org.

Runs in the desktop app only (the engine on your machine).

US rates move the dollar, gold and equity indices. The slope of the curve (10 years minus 2 years) has turned negative before most US recessions, and many traders read gold against the real yield. This plugin brings the curve into a backtest as daily feeds.

Before a run, it downloads the Treasury's daily par yield curve, real yield curve and bill rates, and the effective federal funds rate from the New York Fed, and writes one feed per series, named after the FRED series of the same data: us_dff (effective federal funds rate), us_dtb3 (13 week bill), us_dgs3mo, us_dgs2, us_dgs10, us_dgs30 (par yields), us_dfii10 (10 year real yield, TIPS), and three computed here: us_t10y2y (10 years minus 2 years), us_t10y3m (10 years minus 3 months) and us_t10yie (10 year breakeven inflation, nominal minus real). All in percent.

The data comes from the publishers themselves, both public and keyless.

Configuration

KeyDefaultWhat it does
cache_days1Days before the plugin downloads the current year and the fed funds rate again. Within that time a run reads the cached feeds and needs no network.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    slope = ctx.data("us_t10y2y", bars[-1].ts)  # 10 year minus 2 year yield, in points
    if slope is not None and slope < 0:
        ctx.signal("EXIT")  # stand aside while the curve is inverted

The feeds are refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches.

What it provides

  • us_dgs3mo, us_dgs2, us_dgs10, us_dgs30: Treasury par yields for 3 months, 2, 10 and 30 years in percent, from 1990 (the 30 year has no values from February 2002 to February 2006, when it was not issued).
  • us_dtb3: The 13 week Treasury bill rate on a bank discount basis in percent, from 2002, as the Treasury publishes it.
  • us_dfii10: The 10 year real yield (Treasury real yield curve, from TIPS) in percent, from 2003.
  • us_dff: The effective federal funds rate of the New York Fed in percent, from July 2000.
  • us_t10y2y, us_t10y3m, us_t10yie: Computed on days both legs were published: 10 year minus 2 year yield, 10 year minus 3 month yield, and 10 year breakeven inflation (nominal minus real), in percentage points.

Data source and terms

When values are known

The Treasury publishes a day's yields that evening and the New York Fed a day's EFFR at about 9:00 the next business morning. Each value is stored two US business days after the day it describes (federal holidays, Good Friday and one-off closures skipped) and carried forward over weekends and holidays, for at most 10 days. A bar on Monday reads Thursday's yields; Friday's arrive on Tuesday.

Limits

  • Daily closing values only: an intraday strategy sees rates that are two business days old, never the move of the day.
  • The fed funds rate starts in July 2000 (the New York Fed's own history); the bill rate starts in 2002, the real yield in 2003, the par yields in 1990.
  • us_dtb3 is the Treasury's own 13 week bill rate; FRED's DTB3 comes from the Fed's H.15 and can differ by a few basis points on some days.
  • The first fetch needs about 90 small files, and the Treasury sometimes takes 20 seconds per file. A refresh fetches the newest years first for at most 90 seconds and writes what it has; the older years follow on the next runs, so the early history can be missing at first. Finished years stay in the data cache, and the data stays on your machine.

Crypto Fear & Greed Index (crypto_fear_greed)

alternative.me's daily crypto sentiment number, 0 (extreme fear) to 100 (extreme greed), with its label as feeds.

Kind: Data source. Category: Sentiment. Version: 1.0.0. Network: api.alternative.me.

Runs in the desktop app only (the engine on your machine).

The Crypto Fear & Greed Index by alternative.me condenses volatility, momentum and volume, social media, dominance and search trends around Bitcoin into one number a day. Some traders read extreme fear as a time to look for longs and extreme greed as a time to be careful.

Before a run, the plugin downloads the whole history (daily since February 2018) and writes two feeds: crypto_fear_greed with the number and crypto_fear_greed_label with the class alternative.me gives it: Extreme Fear, Fear, Neutral, Greed or Extreme Greed.

alternative.me asks that wherever the data is shown, the source is named right next to it. The attribution line is in each feed's _meta block; use it if you publish a chart or a report with these values.

Configuration

KeyDefaultWhat it does
cache_days1Days before the plugin downloads the index again. Within that time a run reads the cached feeds and needs no network.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    fear = ctx.data("crypto_fear_greed", bars[-1].ts)
    if fear is not None and fear < 20:
        ctx.signal("LONG")
    elif fear is not None and fear > 80:
        ctx.signal("EXIT")

The feeds are refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches.

What it provides

  • crypto_fear_greed: The index, a whole number from 0 to 100, under the day after the day it describes.
  • crypto_fear_greed_label: The class of the same value as text: Extreme Fear, Fear, Neutral, Greed or Extreme Greed.

Data source and terms

  • alternative.me, Crypto Fear & Greed Index, terms. No API key needed. Attribution: "Source: Crypto Fear & Greed Index by alternative.me". Rate limit: One request per refresh for the whole history. alternative.me asks for no key and names no limit at this volume.

When values are known

alternative.me dates each value with the UTC day it describes and publishes it around the start of that day. The exact time is not documented, so each value is stored under the next UTC day and carried forward for at most 7 days over days the vendor skipped. A bar on Tuesday reads Monday's value.

Limits

  • One value a day: an intraday strategy sees yesterday's number all day.
  • The method behind the index is alternative.me's and may change; the history is what they serve today, not necessarily what was shown on each day.
  • Starts in February 2018. alternative.me's rules: name the source right next to the data wherever you show it, and do not build a service that could be confused with theirs. The data stays on your machine.

VIX close (vix_data)

The daily Cboe VIX close from FRED as a feed, so a strategy can stand aside when volatility is high.

Kind: Data source. Category: Volatility. Version: 1.1.0. Network: fred.stlouisfed.org.

Runs in the desktop app only (the engine on your machine).

The VIX is the market's expectation of 30 day volatility in the S&P 500, read from option prices. Many traders use it as a filter: no new positions while it is above a threshold, or smaller ones.

Before a run, the plugin downloads the series from FRED and writes the feed vix. Set another FRED series id to cache that series under the same feed name instead, for example VXNCLS for the Nasdaq 100 volatility index.

Configuration

KeyDefaultWhat it does
cache_days1Days before the plugin downloads the series again. Within that time a run reads the cached feed and needs no network.
series_id"VIXCLS"The FRED series to download, VIXCLS by default. Whatever it is, it is stored as the feed vix.

Use it in a strategy

pythonbars = ctx.bars(1)
if bars:
    vix = ctx.data("vix", bars[-1].ts)
    if vix is not None and vix > 30:
        ctx.signal("EXIT")

The feed is refreshed before a run whose strategy reads ctx.data, or when "Fetch external data (news and plugin feeds)" is ticked. A run on the server never fetches.

What it provides

  • vix: The close of the series, a number, under each day from the day after the first observation.

Data source and terms

When values are known

A close is published after the market closes, so each value is stored under the next calendar day and carried forward over weekends and holidays, for at most 7 days. A bar on Monday reads Friday's close.

Limits

  • Daily closes only: an intraday strategy sees yesterday's VIX all day.
  • The VIX index data belongs to Cboe; FRED passes it on for personal use. The data stays on your machine.

Indicator

Heikin Ashi (heikin_ashi)

Heikin Ashi candles for a strategy: HA open, high, low and close, the candle color, the run of same colored candles and a no opposite wick flag.

Kind: Indicator. Category: Chart types. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Heikin Ashi smooths candles by averaging. This plugin computes the classic definition (Dan Valcu, Technical Analysis of Stocks & Commodities, 2004) bar by bar: HA close = (open + high + low + close) / 4; HA open = (previous HA open + previous HA close) / 2; HA high = max(high, HA open, HA close); HA low = min(low, HA open, HA close).

The first bar has no previous HA candle, so its HA open is seeded with (open + close) / 2 of that bar, the classic seed. Each later HA open halves the distance to the seed, so after ten bars the seed's share is below 0.1%. The seed is the first bar the indicator sees, so a run that starts on a different day shows slightly different first values.

The chart can already draw Heikin Ashi candles (Chart options); this plugin hands the same kind of values to a strategy. It has no parameters: pass {}.

Use it in a strategy

pythonha = ctx.plugin_indicator("heikin_ashi", "heikin_ashi", {})
if ha is not None and ha.value is not None:
    # Three rising candles in a row, the last without a lower wick.
    if ha.color == 1 and ha.run >= 3 and ha.no_opposite_wick:
        ctx.signal("LONG")
    elif ha.color == -1:
        ctx.signal("EXIT")

The indicator library has this as heikin_ashi: ctx.ind("heikin_ashi") reads the same candles, direction (the color here), run and no_opposite_wick, and the chart, Blocks, Rust and the Rust core have it too. This template stays as an example of a plugin indicator.

What it provides

  • value: The HA close, the same as ha_close. None before the first bar.
  • ha_open: The HA open: the midpoint of the previous HA candle's body.
  • ha_high: The HA high: the highest of the bar's high, the HA open and the HA close.
  • ha_low: The HA low: the lowest of the bar's low, the HA open and the HA close.
  • ha_close: The HA close: the average of the bar's open, high, low and close.
  • color: 1 when the HA close is above the HA open (rising), -1 when below (falling), 0 when they are equal.
  • run: How many candles in a row have this color, this one included. A candle with color 0 has a run of 0 and ends the run before it.
  • no_opposite_wick: True for a rising candle without a lower wick or a falling candle without an upper wick, often read as a strong candle. False otherwise.

Data source and terms

No outside data source.

When values are known

Each value is final at the close of its bar and never changes when later bars arrive.

Limits

  • HA prices are averages, not prices that traded. Orders still fill at the real bid and ask, so a backtest that enters at the HA close would not match its fills.
  • The first values depend on the seed bar, the first bar the indicator sees: a run that starts on another day shows slightly different HA values for its first few bars.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Renko Bricks (renko_bricks)

Renko bricks built from bar closes, fixed or start ATR size: direction, last brick level, new bricks this bar, run length and reversals.

Kind: Indicator. Category: Chart types. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Renko draws a brick each time price has moved a fixed distance and ignores time. This plugin follows the classic rules. Bricks are built from closes only. The brick size never changes during a run: either a price distance you set, or ATR(atr_period) times atr_mult measured once, at the first bar where the ATR is known (Wilder's ATR, the same as ctx.ind("atr")), and then fixed. The first brick is measured from the close of that bar (the anchor), and every brick edge sits on the grid anchor + k times the size.

A brick in the same direction needs a close one size beyond the last brick's close. A reversal needs two sizes: a close one size beyond the last brick's open (the classic two brick reversal). One bar can add several bricks, all in the same direction.

Parameters

ParameterDefaultRangeWhat it does
mode"atr"atr, fixedHow the brick size is set: "atr" measures ATR(atr_period) times atr_mult once at the start, "fixed" takes size.
size0.0The brick size in price units for mode "fixed" (0.0010 is 10 pips on EURUSD). Must be above 0 there; mode "fixed" with 0 is refused and the call returns None.
atr_period141 to 500Bars in Wilder's ATR for mode "atr".
atr_mult1.00.01 to 50.0The brick size as a multiple of that ATR, for mode "atr".

Use it in a strategy

pythonrenko = ctx.plugin_indicator("renko_bricks", "renko", {"mode": "fixed", "size": 0.0010})
if renko is not None and renko.value is not None:
    # Long after two up bricks in a row, out on a reversal down.
    if renko.direction == 1 and renko.run >= 2:
        ctx.signal("LONG")
    elif renko.reversal and renko.direction == -1:
        ctx.signal("EXIT")

The indicator library has this as renko: ctx.ind("renko", mode="atr", atr_length=14, atr_mult=1.0) reads the same bricks (brick_close is the value here), and the chart, Blocks, Rust and the Rust core have it too. This template stays as an example of a plugin indicator.

What it provides

  • value: The close of the last brick (its far edge in its direction). None until the first brick.
  • brick_open: The open of the last brick, one size behind its close. None until the first brick.
  • direction: 1 when the last brick went up, -1 when it went down. None until the first brick.
  • new_bricks: How many bricks this bar added, 0 for none. None while the size is not known yet.
  • run: How many bricks in a row point in the current direction, this bar's bricks included. None until the first brick.
  • reversal: True on the bar whose bricks turned the direction (not on the very first brick). False otherwise.
  • size: The brick size in price units, fixed for the run. None while the ATR warms up.

Data source and terms

No outside data source.

When values are known

Bricks form at the close of the bar whose close reaches them and never change afterwards. With mode "atr" nothing forms before the ATR is known (atr_period + 1 bars); the bar that fixes the size is the anchor and makes no brick itself.

Limits

  • Built from bar closes, not ticks: a move that reverses inside one bar leaves no brick, and on a higher timeframe fewer bricks form than on a tick chart.
  • The ATR size is measured once at the start, so it depends on where the run starts and does not follow later changes in volatility.
  • Brick levels are not prices that traded at that moment. Orders still fill at the real bid and ask of the next bar.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Bollinger Bands (how to write an indicator) (bollinger_bands)

A short streaming indicator to copy from: one bar in, the middle, upper and lower band out.

Kind: Indicator. Category: Examples. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

This plugin exists to be read. It shows the whole shape of an indicator plugin in one file: a class that takes one bar per update, keeps only the window it needs, returns None while it warms up, and exposes extra lines as properties beside value.

The engine already has Bollinger Bands built in (ctx.ind("bb")), drawn on the chart and available in every language. Use that one in a strategy; use this one as the starting point for an indicator of your own.

Parameters

ParameterDefaultRangeWhat it does
period202 to 200Bars in the window the average and the standard deviation are taken over.
mult2.00.5 to 5.0How many standard deviations the upper and lower band sit from the middle.

Use it in a strategy

pythonbands = ctx.plugin_indicator("bollinger_bands", "bollinger", {"period": 20, "mult": 2.0})
if bands is not None and bands.value is not None:
    close = ctx.bars(1)[-1].close
    if close < bands.lower:
        ctx.signal("LONG")

What it provides

  • value: The middle band, the simple moving average of the close. None during warm up.
  • upper: The middle band plus mult population standard deviations.
  • lower: The middle band minus mult population standard deviations.

Data source and terms

No outside data source.

Limits

  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Prior period levels (prior_period_levels)

Yesterday's, last week's or last month's open, high, low and close, the running period's range, and how far price is from them in ATR.

Kind: Indicator. Category: Price levels. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Many traders watch the previous day's high and low, or last week's, as levels where price tends to react. This indicator gives a strategy those levels on every bar: the open, high, low and close of the last finished day, week or month, the high and low of the running one so far, and the distance from the close to the previous high and low in units of a daily ATR, so one rule works on EURUSD and on gold.

Where a day starts is a choice. The default is 17:00 New York, daylight saving included, where forex turns the day: Sunday evening is the start of Monday, so Monday's previous day is Friday. This is the same trading day the built in pivots and VWAP use with their session input on 17:00 New York, so this indicator and the chart agree. The other choices are 00:00 UTC and a custom start on any clock, for an exchange's close (16:00 America/New_York for US stocks, 17:30 Europe/Berlin for Xetra). Weeks start on Monday, months are the trading day's calendar month. Pivot points themselves are in the indicator catalogue (ctx.ind("pivots")), drawn on the chart; use this plugin for the raw levels and the ATR distance.

Parameters

ParameterDefaultRangeWhat it does
period"day"day, week, monthThe period the levels come from. Weeks start on Monday, months are the trading day's month.
day_start"ny"ny, utc, customWhere a day starts: 17:00 New York (as the built in pivots and VWAP), 00:00 UTC, or start in tz.
start"17:00"Custom day start only: a clock time like 16:00. A day starting at or after noon carries the date it ends on, one starting before noon the date it starts on.
tz"America/New_York"Custom day start only. A time zone name (IANA), like Europe/Berlin or America/New_York.
atr_period141 to 200Finished trading days in the daily ATR (Wilder's smoothing).

Use it in a strategy

pythonlv = ctx.plugin_indicator("prior_period_levels", "levels", {"period": "day"})
if lv is not None and lv.dist_high_atr is not None:
    if lv.new_period:
        ctx.signal("EXIT")
    elif lv.dist_high_atr > 0.25:
        ctx.signal("LONG")
    elif lv.dist_low_atr < -0.25:
        ctx.signal("SHORT")

The indicator library has this as prior_levels: ctx.ind("prior_levels", period="day", session="ny") reads the previous and the running open, high, low and close, and the chart, Blocks, Rust and the Rust core have it too. This template stays as an example of a plugin indicator; only it has the daily ATR distances and a custom day start in any time zone.

What it provides

  • prev_open, prev_high, prev_low, prev_close: The last finished period's open, high, low and close; None until the first period in the data has finished.
  • value: The same as prev_close.
  • open, high, low: The running period's open, and its high and low so far, this bar included.
  • new_period: True on the first bar of a new period.
  • atr: Wilder's ATR of finished trading days (same day start), never the running day; None until atr_period + 1 days have finished.
  • dist_high_atr, dist_low_atr: (close - prev_high) / atr and (close - prev_low) / atr: positive above the level, negative below; None while either part is missing.

Data source and terms

No outside data source.

Limits

  • A period counts from its first bar in the data: when the history starts in the middle of a day, that partial day is the first previous day.
  • The levels come from the run's own bars (mid prices). Holidays are not known: a day with a few bars is a day like any other.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Opening range (opening_range)

The high and low of the first minutes after a market opens, its size in daily ATR and where price is now, never before the window has closed.

Kind: Indicator. Category: Sessions and time. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

The opening range is the high and low of the first minutes after a market opens: 30 minutes after 09:30 New York for US stocks and index futures, or after 09:00 Frankfurt for the DAX. Breakout traders buy above it or sell below it, and many skip days where the range is already wide compared with a normal day. This indicator gives a strategy the range, its middle, its size in units of a daily ATR, whether the close is above, inside or below it, and the range of the first hour beside it.

Nothing is reported before the window has closed, so a backtest can never trade on a range that was still forming. The open is a wall clock time in a time zone, so 09:30 New York stays 09:30 New York when the clocks change. The window starts again every local day; after local midnight the outputs are None until the new window closes.

Parameters

ParameterDefaultRangeWhat it does
open"09:30"The opening time on the clock of tz.
tz"America/New_York"A time zone name (IANA), like Europe/Berlin or America/New_York.
minutes301 to 1440The length of the window; it must end by local midnight.
atr_period141 to 200Finished local days in the daily ATR (Wilder's smoothing).

Use it in a strategy

pythonorb = ctx.plugin_indicator("opening_range", "range", {"open": "09:30", "tz": "America/New_York", "minutes": 30})
if orb is not None and orb.complete and orb.size_atr is not None and orb.size_atr < 0.5:
    if orb.position == 1:
        ctx.signal("LONG")
    elif orb.position == -1:
        ctx.signal("SHORT")

The indicator library has this as opening_range: ctx.ind("opening_range", zone="new_york", hour=9, minute=30, minutes=30) reads it, on the clock of UTC, London, New York, Tokyo or Sydney, and the chart, Blocks, Rust and the Rust core have it too. This template stays as an example of a plugin indicator; only it takes any time zone and has the first hour range.

What it provides

  • high, low, mid: The range of the bars that start inside the window, and its middle (also value); None until the window has closed.
  • complete: True from the bar that closes the window until local midnight.
  • size, size_atr: high - low, and that size divided by the daily ATR; size_atr is None until the ATR has warmed up.
  • position: 1 when the bar's close is above the range, -1 below it, 0 inside; None until the window has closed.
  • hour_high, hour_low: The range of the first 60 minutes after the open, by the same rules.
  • atr: Wilder's ATR of finished local days in tz, never today; None until atr_period + 1 days have finished.

Data source and terms

No outside data source.

Limits

  • A bar belongs to the window when it starts inside it. Use a timeframe that lines up with the open and is no longer than the window (M1, M5, M15 or M30 for 09:30); with H1 bars a 09:30 window has no bar and stays None.
  • Holidays and half days are not known: a day without bars in the window has no range, a day with bars has one, whatever the exchange did that day. With round the clock data (forex, crypto) the window is cut from those bars.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Trading sessions (trading_sessions)

Which FX centre is open on each bar, the London and New York overlap, and the high and low of the session you follow.

Kind: Indicator. Category: Sessions and time. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Forex trades around the clock, but not evenly: volume and ranges change with the centres that are open. This indicator tells a strategy, on every bar, whether Sydney, Tokyo, London and New York are open, whether London and New York overlap, and for one session you choose: whether the bar is in it, whether it is its first bar, the minutes since it opened, its high and low so far, and the high and low of the last finished session.

Every session is kept on its own city's clock: Sydney 07:00 to 16:00, Tokyo 09:00 to 18:00 (no daylight saving), London 08:00 to 17:00, New York 08:00 to 17:00. These are common conventions; the FX market has no official session hours. London and New York change their clocks on different Sundays, up to three weeks apart in March and one week apart in late October or early November, and in those weeks the overlap is an hour longer in UTC. Because the times are local, the indicator gets those weeks right, and the Monday Sydney session starts on Sunday evening in UTC, where the FX week opens.

Parameters

ParameterDefaultRangeWhat it does
session"london"sydney, tokyo, london, new_york, customThe session to follow; custom uses open, close and tz below.
open"08:00"Custom session only: the opening time on the clock of tz, like 09:00.
close"17:00"Custom session only: the closing time; at or before open, the session runs past midnight.
tz"Europe/London"Custom session only. A time zone name (IANA), like Europe/Berlin or America/New_York.
weekdays"mon_fri"mon_fri, every_dayThe local days a session may open on: Monday to Friday, or every day for markets that trade at the weekend. Applies to the four FX flags too.

Use it in a strategy

pythonses = ctx.plugin_indicator("trading_sessions", "sessions", {"session": "london"})
bars = ctx.bars(1)
if ses is not None and bars and ses.overlap and ses.prev_high is not None:
    if bars[-1].close > ses.prev_high:
        ctx.signal("LONG")
    elif bars[-1].close < ses.prev_low:
        ctx.signal("SHORT")

The indicator library has this as sessions: ctx.ind("sessions", session="london") reads it in a strategy, and the chart, Blocks, Rust and the Rust core have it too, with the same four sessions on their own clocks. This template stays as an example of a plugin indicator; only it takes custom hours in any time zone.

What it provides

  • value: 1.0 when the bar is in the followed session, else 0.0.
  • in_session: True when the bar's start time is inside the followed session (open included, close excluded).
  • session_open: True on the first bar of a new session (also on the first bar of the data when it starts inside a session; check minutes_since_open).
  • minutes_since_open: Minutes from the session's open to the bar's start time; None outside the session.
  • high, low: The high and low of the running session so far, this bar included; None outside the session.
  • prev_high, prev_low: The high and low of the last finished session; None until one has finished.
  • sydney, tokyo, london, new_york: True while that centre's session is open, on its own clock.
  • overlap: True while London and New York are both open.

Data source and terms

No outside data source.

Limits

  • A bar counts by its start time. A bar that starts before the open but ends after it is not in the session, so pick a timeframe that lines up with the session times (H1 or finer for the four FX sessions).
  • Holidays are not known: a session opens on every weekday that has bars. When the data starts in the middle of a session, its high and low count from the first bar.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Pair correlation and beta (pair_correlation_beta)

Rolling correlation, beta and relative strength of the instrument's daily returns against another market, a yield or any feed you have.

Kind: Indicator. Category: Statistics. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Markets move together for a while and then stop: gold with real yields, the yen with US rates, an index with its sector. This indicator measures that link while a backtest runs. It pairs the instrument's daily close with a date keyed feed, the same kind ctx.data reads (written by a data source plugin such as the FRED rates curve, or by you), and gives the correlation of their daily changes over a rolling window, the instrument's beta to the feed, and its relative strength over the window.

It is careful about time. Days are UTC days, as feeds are keyed, and only finished days count. A feed stores each value under the first day it was known, usually a day or two after the day it describes. By default (align "observed") the indicator reads the feed's own delay rule from its _meta block and pairs each day of the instrument with the feed's value for that same day, but only from the bar where that value was known; so a pair can arrive a day or two late, and it never arrives early. With align "known" each day is paired with the value known that day instead.

When the feed does not exist, or with align observed its delay rule is not one the kit knows, ctx.plugin_indicator returns None.

Parameters

ParameterDefaultRangeWhat it does
feed""Required: the feed's name, as ctx.data reads it.
field""For a feed that holds objects: the key of the number to use. Empty for a feed of numbers.
changes"returns"returns, differencesHow the feed's daily change is measured: returns for prices, differences for yields, rates and indices like the VIX.
window603 to 1000Day pairs in the rolling window.
align"observed"observed, knownobserved pairs each day with the feed's value for that day, from the bar it was known; known pairs it with the value known on that day.

Use it in a strategy

pythonpc = ctx.plugin_indicator("pair_correlation_beta", "correlation",
                          {"feed": "us_dgs10", "changes": "differences", "window": 60})
bars = ctx.bars(1)
# Reading the feed with ctx.data also makes the run fetch it before it starts.
if bars and ctx.data("us_dgs10", bars[-1].ts) is not None:
    if pc is not None and pc.value is not None and abs(pc.value) > 0.6:
        ctx.signal("EXIT")

The feed must exist before the run: install and switch on the plugin that writes it, and read it once with ctx.data in the strategy (or tick "Fetch external data (news and plugin feeds)") so it is fetched first.

What it provides

  • value, correlation: Pearson correlation of the instrument's daily returns and the feed's daily changes over the window, -1 to 1; None until the window is full.
  • beta: cov(instrument, feed) / var(feed) over the window: the instrument's return per unit of feed change.
  • relative_strength: (instrument now / at the window start) / (feed now / at the window start) - 1; only with changes returns, else None.
  • overlap_days: How many day pairs the window holds so far, up to window.

Data source and terms

No outside data source.

Limits

  • Daily changes only, on UTC days: an intraday strategy sees the figures of finished days. A weekly feed (like COT data) gives one change a week, not a daily series.
  • Feeds carry their last value over days without a release. On such a day the feed's change is zero, and holidays the writing plugin skipped for a business day rule are not known here, so around them a pair can take the neighbouring value.
  • Correlation is not causation and changes over time; a correlation from 60 days is a rough estimate.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Seasonality (seasonality)

How the instrument did in past months, weekdays, turns of the month or hours like the current one: mean return, hit rate, count and t-statistic.

Kind: Indicator. Category: Statistics. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Some markets are said to do better in some months, on some weekdays, around the turn of the month or at some hours. This indicator measures that on the run's own data as it goes: for the bucket the current bar is in (this calendar month, this weekday, turn of the month or not, this hour), the number of earlier periods in it, their mean return, the share that went up, and a t-statistic.

Only finished periods count. One observation is the close to close return of one finished hour, day or month; the period the current bar is in has not finished and is left out, so the bar never feeds the number it reads and a backtest cannot see ahead. Each figure therefore grows with the history: early in a run there is little to go on.

Be careful with what it says. A pattern found by looking at twelve months or seven weekdays is often luck, mean, hit_rate and t_stat stay None until a bucket has min_count observations, and the t-statistic assumes independent returns, which market returns rarely are. A t-statistic around 2 from a dozen observations is weak evidence.

Parameters

ParameterDefaultRangeWhat it does
by"weekday"month, weekday, turn_of_month, hourWhat makes periods alike. The turn of the month is the last weekday of a month and the first three weekdays of the next, by the calendar (holidays ignored).
tz"UTC"The clock days, hours and months are counted on. A time zone name (IANA), like Europe/Berlin or America/New_York.
min_count102 to 1000Observations a bucket needs before mean, hit_rate and t_stat appear.

Use it in a strategy

pythonsea = ctx.plugin_indicator("seasonality", "seasonality", {"by": "weekday", "min_count": 20})
if sea is not None and sea.t_stat is not None:
    if sea.t_stat > 2.0 and sea.hit_rate > 0.55:
        ctx.signal("LONG")
    elif sea.t_stat < 0:
        ctx.signal("EXIT")

The indicator library has this as seasonality: ctx.ind("seasonality", by="weekday", zone="utc", min_count=10) reads it on the clock of UTC, London, New York, Tokyo or Sydney, with the turn of the month as bucket 1 and the rest as 0, and the chart, Blocks, Rust and the Rust core have it too. This template stays as an example of a plugin indicator; only it takes any time zone.

What it provides

  • value, mean: The mean close to close return of the finished periods in the current bucket, as a fraction (0.001 is 0.1 %); None below min_count.
  • count: How many finished periods of the current bucket came before this bar; 0 when none.
  • hit_rate: The share of those periods with a return above zero, 0 to 1; None below min_count.
  • t_stat: mean / (sample standard deviation / sqrt(count)); None below min_count or when every return was the same.
  • bucket: The current bucket: month 1 to 12, weekday 0 (Monday) to 6, "turn" or "rest", hour 0 to 23.

Data source and terms

No outside data source.

Limits

  • It needs history: a month bucket gains one observation a year. Load enough data before the part of the run you judge, or results early in the run rest on almost nothing.
  • Returns are close to close between periods that had bars, so the first hour or day after a weekend includes the weekend gap. With forex data on UTC days the few Sunday evening hours form a day of their own.
  • The hour grouping needs bars of an hour or shorter. Nothing here says a pattern will continue; it describes the past of this data only.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Fair Value Gaps (fair_value_gaps)

Three bar fair value gaps with a minimum size in ATR: the nearest open gap on each side, how far each is filled, and an event when one is filled.

Kind: Indicator. Category: Market structure. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

A fair value gap is a price range three bars in a row leave untraded. Bullish: the third bar's low is above the first bar's high, and the gap runs from that high (bottom) to that low (top). Bearish: the third bar's high is below the first bar's low, from that high (bottom) to that low (top). A gap counts only when its height is at least min_atr times the ATR at the third bar: Wilder's ATR over atr_period bars, the engine's own (the same as ctx.ind("atr")). Nothing is detected while the ATR warms up.

Filling starts with the bar after the third, and wicks count. A bullish gap fills as price trades down into it: fill = (top - lowest low since) / (top - bottom). A bearish gap fills as price trades up: fill = (highest high since - bottom) / (top - bottom). When the fill reaches fill_threshold (1.0: price has traded through the whole gap; 0.5: to its middle) the gap counts as filled, is reported on that bar and is no longer tracked. The nearest open bullish gap is the one with the highest top, the nearest bearish one the one with the lowest bottom.

The plugin setting max_open_gaps caps how many open gaps are kept per side; past it the oldest is dropped without a fill event. This plugin follows the public three bar definition and is not a copy of any charting script.

Configuration

KeyDefaultWhat it does
max_open_gaps50How many open gaps are kept per side. When a new one would pass the limit, the oldest open gap on that side is dropped without a fill event.

Parameters

ParameterDefaultRangeWhat it does
min_atr0.250.0 to 10.0The smallest gap that counts, in multiples of the ATR at the gap's third bar. 0 counts every gap.
atr_period141 to 500Bars in Wilder's ATR. Nothing is detected before it is known.
fill_threshold1.00.01 to 1.0The fill at which a gap counts as filled: 1.0 when price has traded through the whole gap, 0.5 at its middle.

Use it in a strategy

pythonfvg = ctx.plugin_indicator("fair_value_gaps", "fvg", {"min_atr": 0.25, "fill_threshold": 0.5})
if fvg is not None and fvg.value is not None:
    close = ctx.bars(1)[-1].close
    for gap in fvg.filled_gaps:
        # Price came back to the middle of a bullish gap and closed above its bottom.
        if gap["side"] == 1 and close > gap["bottom"]:
            ctx.signal("LONG")
        # The same for a bearish gap: out.
        elif gap["side"] == -1 and close < gap["top"]:
            ctx.signal("EXIT")

What it provides

  • value: 1 when a bullish gap formed on this bar, -1 for a bearish one, else 0 (the same as new_gap). None during warm up.
  • gap_top: Top and gap_bottom of the gap formed on this bar. None when none formed.
  • bull_top: Top, bull_bottom and bull_fill (0 to 1) of the nearest open bullish gap. None when there is none.
  • bear_top: Top, bear_bottom and bear_fill (0 to 1) of the nearest open bearish gap. None when there is none.
  • filled_gaps: The gaps this bar filled, as dicts like open_gaps ([] for none): read their levels here, since a filled gap is no longer tracked. None during warm up.
  • bull_filled: How many of them were bullish, and bear_filled how many bearish; 0 for none. None during warm up.
  • open_bull: How many bullish gaps are open, and open_bear how many bearish ones. None during warm up.
  • open_gaps: Every open gap as a dict with side, top, bottom, fill and formed_at (the time of its third bar): bullish first, then bearish, each oldest first. None during warm up.
  • atr: The ATR the minimum size is measured with. None during warm up.

Data source and terms

No outside data source.

When values are known

A gap is known at the close of its third bar; a fill is known at the close of the bar that reaches it. Nothing reported changes with later bars. Warm up: None until the ATR is known (atr_period + 1 bars).

Limits

  • Fills are measured with bar highs and lows: inside one bar the plugin cannot tell whether price touched the gap before or after the bar's other extreme.
  • Gaps depend on the timeframe and on the data's bar times: the same market shows different gaps on M5 and H1, and on data with other session hours.
  • Only open gaps up to the cap per side are tracked; a dropped gap never reports a fill.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Market Structure (market_structure)

Confirmed swing highs and lows without repainting, the trend they form (HH and HL or LH and LL), and the last break of structure and change of character.

Kind: Indicator. Category: Market structure. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

A swing high is a bar whose high is strictly above the highs of the length bars before it and the length bars after it; a swing low is the same with lows, strictly below. Equal highs or lows on either side make no swing. A swing is confirmed only when its length right hand bars have closed, and it is reported on that confirming bar, never earlier, so the indicator never redraws: what it says at a bar uses that bar and the ones before it only. A swing therefore always arrives length bars late.

Each new swing high is labelled HH (above the previous swing high), LH (below) or EH (equal); each swing low HL, LL or EL. The trend (value) is 1 when the last high is HH and the last low is HL, -1 when they are LH and LL, and 0 for any other mix.

A close above the last confirmed swing high, or below the last confirmed swing low, breaks structure; each swing level can be broken once, and wicks do not count. A break in the direction of the current bias, or the very first break, is a break of structure (BOS). A break against the bias is a change of character (CHoCH) and turns the bias. At most one break per bar: a close above the last high and below the last low at once (only possible when that low sits above that high) counts as the break up.

Parameters

ParameterDefaultRangeWhat it does
length31 to 50Bars on each side of a swing. A swing is confirmed, and reported, this many bars after its own bar.

Use it in a strategy

pythonms = ctx.plugin_indicator("market_structure", "structure", {"length": 3})
if ms is not None and ms.bias is not None:
    # A break of structure up while the swings make HH and HL: long.
    if ms.bos == 1 and ms.value == 1:
        ctx.signal("LONG")
    # A change of character down: out.
    elif ms.choch == -1:
        ctx.signal("EXIT")

What it provides

  • value: The trend, the same as trend: 1 (HH and HL), -1 (LH and LL), 0 (mixed). None until there are two swing highs and two swing lows.
  • bias: The direction of the last break: 1 after a break up, -1 after a break down. None until the first break.
  • last_high: The price of the last confirmed swing high, and last_high_time the time of its bar. None until the first one.
  • last_low: The price of the last confirmed swing low, and last_low_time the time of its bar. None until the first one.
  • high_label: "HH", "LH" or "EH" for the last swing high against the one before; low_label is "HL", "LL" or "EL". None until there are two.
  • high_confirmed: True on the bar that confirms a swing high, length bars after it; low_confirmed the same for a swing low. None during warm up.
  • bos: 1 or -1 on the bar of a break of structure up or down, else 0; bos_level keeps the level of the last one. None during warm up.
  • choch: 1 or -1 on the bar of a change of character up or down, else 0; choch_level keeps the level of the last one. None during warm up.

Data source and terms

No outside data source.

When values are known

A swing is known length bars after its own bar, at the close of the confirming bar; breaks are known at the close of the breaking bar. Nothing reported is ever changed by later bars. Warm up: the first 2 x length bars report None.

Limits

  • Swings arrive length bars late by design. A chart tool that marks a swing on its own bar is looking ahead; this one does not.
  • Strict comparisons: a double top with exactly equal highs makes no swing high.
  • BOS and CHoCH have many definitions in trading literature. This one uses closes against the last confirmed swing only; other tools may mark different bars.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only.

Market Structure Toolkit (market_structure_toolkit)

An example that bundles the nineteen catalogue elements of the market structure group (BoS, CHoCH, FVG, order blocks, Fibonacci, OTE, liquidity, kill zones) in one plugin indicator.

Kind: Indicator. Category: Market structure. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

This template is an example and adds nothing of its own. The market structure elements are part of the engine's indicator catalogue: structure (BoS, CHoCH), fvg, ifvg, order_block, breaker_block, fib_retracement, fib_extension, premium_discount, ote, liquidity_sweep, equal_levels, liquidity, bpr, mitigation_block, rejection_block, displacement, killzones, midnight_open and opening_gaps. A strategy reads them one by one with ctx.ind("fvg", ...) or all nineteen at once with ctx.market_structure(...), Blocks offers them in the group Market structure, and the chart draws them when you add them as indicators. Their definitions, warm up and limits are on the indicator pages. That is the real implementation, and the one to use in a strategy.

What the template shows is how a plugin bundles catalogue elements in a plugin indicator. It sorts the parameters to the elements that take them with split from orionquant_engine.indicators.market (the function ctx.market_structure uses), builds each stream with get(id).make(**inputs), passes every bar to each stream in update(bar), and hands on each element's outputs as a read only reading, the way ctx.ind does. The parameters, their defaults and their bounds are read from the catalogue as well. The template finds no swing, measures no gap and computes nothing from prices: every value is the catalogue stream's own, so it is exactly what this reads at the same bar with the same inputs:

pythonms = ctx.market_structure(length=3)
if ms.trend == 1 and ms.premium_discount.zone == -1:
    ctx.signal("LONG")

Start here when a plugin of your own needs a catalogue indicator inside it, for example a filter of your own on top of the order blocks: build the stream the same way and keep your logic next to it.

Parameters

ParameterDefaultRangeWhat it does
length31 to 50Swing bars on each side, for every element built on swings: structure, the order, breaker, mitigation and rejection blocks, both Fibonacci elements, premium_discount, ote, liquidity, liquidity_sweep and equal_levels. A swing is confirmed this many bars after its extreme.
break_on"close"close, wickWhat breaks a swing in structure and the order, breaker and mitigation blocks: a close beyond it, or with wick a high or low beyond it.
atr_length141 to 500Bars in Wilder's ATR that sizes the gaps of fvg, ifvg, bpr and displacement, the body of a displacement and the tolerance of equal_levels.
min_size0.250.0 to 10.0The smallest gap that counts in fvg, ifvg, bpr and displacement, in multiples of the ATR at its third candle. 0 counts every gap.
fill"full"full, halfWhen a fair value gap counts as filled: full when price has passed through the whole gap, half at its middle (consequent encroachment).
max_zones201 to 500Zones or levels kept per side in every element that keeps them (the gaps, blocks, ranges and liquidity levels); past it the oldest goes without an event.
zone"wick"wick, bodyWhat an order, breaker or mitigation block spans: wick for the full candle, body for its body only.
tolerance0.10.0 to 2.0How far apart two swing highs (or lows) may be and still count as equal in equal_levels, in multiples of the ATR.
min_wick0.50.0 to 1.0The smallest wick a rejection block counts, as a share of its candle's range.
min_body1.00.0 to 10.0The body a displacement candle needs: more than this many ATRs.
days"mon_fri"mon_fri, every_dayWhich days killzones and midnight_open count: mon_fri for Monday to Friday, every_day for markets that trade at the weekend.

Use it in a strategy

pythontk = ctx.plugin_indicator("market_structure_toolkit", "toolkit", {"length": 3})
if tk is not None and tk.value is not None:
    # Trend up and the close inside the OTE zone of an up leg: long.
    if tk.value == 1 and tk.ote.leg == 1 and tk.ote.inside == 1:
        ctx.signal("LONG")
    # A change of character down: out.
    elif tk.structure.choch == -1:
        ctx.signal("EXIT")

The same strategy without the plugin reads ms = ctx.market_structure(length=3) and then ms.trend, ms.ote.leg, ms.ote.inside and ms.choch, with the same values at every bar. That form also runs in Blocks, Rust and C# and reaches the exports; the plugin form runs in Python strategies only.

What it provides

  • value: The trend of the structure element: 1 up, -1 down, 0 before the first break. None while it warms up.
  • structure: The reading of structure: trend, bos, choch, swing_high, swing_low, bos_level and choch_level.
  • fvg: The reading of fvg: bull_top, bull_bottom, bear_top, bear_bottom of the nearest open gaps, bull_new, bear_new, bull_filled and bear_filled.
  • ifvg: The reading of ifvg: the same zone lines as fvg, with bull_broken and bear_broken.
  • order_block: The reading of order_block: the zone lines of the nearest blocks, the new flags, bull_broken and bear_broken.
  • breaker_block: The reading of breaker_block: the same lines as order_block, for broken blocks kept as zones of the other side.
  • fib: The reading of fib_retracement: level_0, level_236, level_382, level_500, level_618, level_786, level_1000 and leg.
  • fib_extension: The reading of fib_extension: level_1272, level_1618, level_2000, level_2618 and leg.
  • premium_discount: The reading of premium_discount: equilibrium, range_high, range_low and zone (1 premium, -1 discount).
  • ote: The reading of ote: top, bottom, inside and leg of the 0.62 to 0.79 retracement zone.
  • liquidity_sweep: The reading of liquidity_sweep: up and down on the bar of a sweep, up_level and down_level the last swept prices.
  • equal_levels: The reading of equal_levels: eqh and eql, the nearest pools of equal highs and lows, with their new and taken flags.
  • liquidity: The reading of liquidity: buy_side and sell_side, the nearest untaken swing high and low, with buy_taken and sell_taken.
  • bpr: The reading of bpr: the zone lines of the nearest balanced price ranges, the new flags, bull_broken and bear_broken.
  • mitigation_block: The reading of mitigation_block: the same lines as order_block, for the swing candle a failure swing broke.
  • rejection_block: The reading of rejection_block: the same lines as order_block, for the long wicks at swing highs and lows.
  • displacement: The reading of displacement: up, down, and high, low and body_atr of the last displacement candle.
  • killzones: The reading of killzones: asia, london_open, new_york_open and london_close, each with its high and low.
  • midnight_open: The reading of midnight_open: open, position (1 above, -1 below) and first_bar.
  • opening_gaps: The reading of opening_gaps: top, bottom and filled of the new week gap (nwog) and the new day gap (ndog).

Data source and terms

No outside data source.

When values are known

Every value is known when the catalogue element knows it, at the close of the bar, exactly as ctx.ind gives it: a swing length bars after its extreme, a gap on its third candle. Nothing reported changes with later bars. Each reading is a snapshot: keeping one past its bar keeps the values it had. Warm up is each element's own; an output is None until its element has something to show.

Limits

  • An example only: it adds nothing to the catalogue elements, and in a strategy ctx.market_structure or ctx.ind is shorter and does the same.
  • One set of parameters for all nineteen elements; for a different length per element, read each one with ctx.ind.
  • The definitions belong to the catalogue: a change there reaches this plugin without a new version of it.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only. The catalogue elements themselves are.

Strategy template

London breakout (london_breakout)

Trades the break of the Asian range in the London session: buffer, stop at the other side, target in R, out by the New York afternoon.

Kind: Strategy template. Category: Breakout. Version: 1.1.0. Network: none.

Runs in the desktop app only (the engine on your machine).

European currency pairs often trade in a narrow range through the Asian hours and move when London opens. This template takes the high and the low from 00:00 to 07:00 on the London clock, and from 07:00 rests a buy stop 2 pips above the high and a sell stop 2 pips below the low, one cancelling the other, so there is at most one trade a day. The stop is the other side's entry level, the target a multiple of the stop distance (1R by default). Orders that have not filled by 12:00 London are cancelled, and an open position is closed at 16:00 New York.

The range and the last entry are on the London clock, the exit on the New York clock. The two cities change to summer time on different Sundays, so for a few weeks a year the gap between them is four hours instead of five. The template turns each time into UTC for every day, so 07:00 London and 16:00 New York stay where they belong through those weeks.

All four times are run parameters, written HHMM (700 is 07:00), so the backtest form and the optimiser set them and they go along into an export for MetaTrader 5, cTrader, TradingView or Rust. Times that are not clock times, or a range that does not end after it starts and before the last entry, place nothing. Optional filters skip a day whose range is smaller or larger than a number of pips.

Parameters

ParameterDefaultRangeWhat it does
bufferPips2.00.0 to 100.0Pips beyond the Asian high and low the stop orders rest at.
targetR1.00.0 to 10.0The target as a multiple of the stop distance. 0 sets no target; the position is closed at the New York time.
minRangePips0.00.0 to 1000.0Skip a day whose Asian range is smaller than this many pips. 0 is off.
maxRangePips0.00.0 to 1000.0Skip a day whose Asian range is larger than this many pips. 0 is off.
riskPercent1.00.0 to 10.0Per cent of the balance lost if the stop is hit. 0 trades the run's lot size.
allowShort10 to 11 trades breaks in both directions, 0 long only.
rangeStart00 to 2359Start of the Asian range, HHMM on the London clock.
rangeEnd7000 to 2359End of the Asian range, HHMM on the London clock, later the same day. The two orders rest from here.
lastEntry12000 to 2359From this time, HHMM on the London clock, orders that have not filled are cancelled. An open position stays.
exitNewYork16000 to 2359When an open position is closed, HHMM on the New York clock. The exit is sent on the close of the bar that ends at this time and fills at the next bar's open.

Use it in a strategy

python# The entry rule in your own strategy, on the close of the 06:45 London M15 bar
# (28 bars of 15 minutes from 00:00), with a 2 pip buffer:
bars = ctx.bars(28)
high = max(b.high for b in bars)
low = min(b.low for b in bars)
buffer = 2 * ctx.pip()
dist = (high - low + 2 * buffer) / ctx.pip()
ctx.order("LONG", stop=high + buffer, sl_pips=dist, tp_pips=dist, oco="london", risk_percent=1.0)
ctx.order("SHORT", stop=low - buffer, sl_pips=dist, tp_pips=dist, oco="london", risk_percent=1.0)

To run the whole template, install it, switch it on and pick London breakout under Plugins in the strategy list. Run it on M5, M15 or M30. In a strategy of your own, finding the 06:45 London bar through the clock changes is the part to write yourself; the template writes the UK and US clock rules out as arithmetic, which is what lets it export.

What it provides

  • london_breakout/london: The strategy, with its parameters: entry buffer in pips, target in R, the range size filters in pips, the risk per trade and the short side.

Data source and terms

No outside data source.

When values are known

The range is known on the close of its last bar (06:45 to 07:00 London on M15), and the two orders rest from the next bar on. Every decision uses that bar and the ones before it.

Limits

  • Built for currency pairs that are active in London (EURUSD, GBPUSD, EURGBP and the like). Pips are the instrument's own price step, so the buffer and the filters mean something else on an index or a coin.
  • The range lies within one London day; a range that starts the evening before (for example 22:00 to 07:00) needs a change in the code.
  • Spreads are wider in the Asian hours and around the London open; on bars with bid and ask the fills pay that, on bars without them the run's spread assumption does.
  • Bank holidays and thin days are not known to the template. A news release at the London open can fill a stop order well past its price.
  • False breaks are common: price leaves the range, takes the order and returns. No result is promised.

Opening range breakout (opening_range_breakout)

Trades the break of the first minutes after a market opens: one trade a day, stop at the other side, target in R, flat before the close.

Kind: Strategy template. Category: Breakout. Version: 1.1.0. Network: none.

Runs in the desktop app only (the engine on your machine).

The opening range is the high and the low of the first minutes after a market opens, 30 minutes after 09:30 New York by default. Once the range is complete, the strategy rests a buy stop at its high and a sell stop at its low, one cancelling the other, so there is at most one trade a day. The stop sits the width of the range away from the fill (the other side), the target a multiple of that distance (2R by default), and on the session's second to last bar every open order is cancelled and the position closed, so the exit fills at the open of the last bar.

The session is three run parameters on the market's own clock: the time zone as a number (0 New York, 1 Chicago, 2 London, 3 Berlin, 4 Zurich, 5 Tokyo, 6 Hong Kong, 7 Sydney, 8 UTC) and the open and the close written HHMM (930 is 09:30). New York is zone 0 from 930 to 1600, Frankfurt (Xetra) zone 3 from 900 to 1730, London zone 2 from 800 to 1630. The backtest form and the optimiser set them, and they go along into an export for MetaTrader 5, cTrader, TradingView or Rust. The template turns the wall clock time into UTC for every day, so the range starts at the right bar in summer and in winter, including the weeks when America and Europe have changed their clocks and the other has not. Times that are not clock times, or a close at or before the open, place nothing.

An optional filter skips days whose range is unusually small or large compared with the daily ATR, computed over whole days from the bars of the run.

Parameters

ParameterDefaultRangeWhat it does
rangeMinutes301 to 240Minutes after the open that make the range. A whole number of bars.
stopRange1.00.1 to 3.0The stop's distance from the fill as a share of the range (with the buffer on both sides). 1.0 is the other side.
targetR2.00.0 to 10.0The target as a multiple of the stop distance. 0 sets no target; the position is closed before the close.
bufferPips0.00.0 to 100.0Pips beyond the range high and low the stop orders rest at.
minRangeAtr0.00.0 to 5.0Skip a day whose range is smaller than this many daily ATRs. 0 is off.
maxRangeAtr0.00.0 to 5.0Skip a day whose range is larger than this many daily ATRs. 0 is off.
atrDays142 to 100Whole days in the daily ATR the two filters compare with. Used only when a filter is on.
riskPercent1.00.0 to 10.0Per cent of the balance lost if the stop is hit. 0 trades the run's lot size.
allowShort10 to 11 trades breaks in both directions, 0 long only.
zone00 to 8The market's time zone: 0 New York, 1 Chicago, 2 London, 3 Berlin, 4 Zurich, 5 Tokyo, 6 Hong Kong, 7 Sydney, 8 UTC. The open and the close are read on this clock, and a day is a calendar day on it.
sessionOpen9300 to 2359When the session opens, HHMM on the market's clock. The opening range starts here: 930 for New York, 900 for Frankfurt.
sessionClose16000 to 2359When the session closes, HHMM on the market's clock, later the same day. The position is closed at the open of the last bar before it.

Use it in a strategy

python# The entry rule in your own strategy, on the close of the bar that completes the range
# (six M5 bars for a 30 minute range):
bars = ctx.bars(6)
high = max(b.high for b in bars)
low = min(b.low for b in bars)
dist = (high - low) / ctx.pip()
ctx.order("LONG", stop=high, sl_pips=dist, tp_pips=2 * dist, oco="orb", risk_percent=1.0)
ctx.order("SHORT", stop=low, sl_pips=dist, tp_pips=2 * dist, oco="orb", risk_percent=1.0)

To run the whole template, install it, switch it on, pick Opening range breakout under Plugins in the strategy list and set the session in its parameters. Run it on M1, M5, M15 or M30 bars that start on the open. In a strategy of your own, finding the bar that completes the range on the market's clock is the part to write yourself; the template writes each zone's daylight saving rules out as arithmetic, which is what lets it export.

What it provides

  • opening_range_breakout/orb: The strategy, with its parameters: range minutes, stop as a share of the range, target in R, entry buffer, the daily ATR filter, the risk per trade, the short side, and the session (time zone, open, close).

Data source and terms

No outside data source.

When values are known

The range is known on the close of its last bar, and the two orders rest from the next bar on. The daily ATR for the filter covers the days before the current one only.

Limits

  • Session times are the ones you set. Holidays and early closes are not known to the template: on a short day the exit comes at the usual time, after the market has closed.
  • The daylight saving rules are written out from the first year each zone has kept them: New York and Chicago 1976, London 1972, Berlin 1980, Zurich 1981, Tokyo 1952, Hong Kong 1980, Sydney 1996. The backtest reads the time zone database on earlier days. An export for MetaTrader 5, cTrader, TradingView or Rust has the rules only, so it places nothing on a day before the zone's first year, and its daily ATR counts from there.
  • The bars have to start on the open and divide the range. On H1 bars and a 09:30 open there is no bar at 09:30, so nothing trades.
  • An export reads the bar's time to the minute. On bars shorter than a minute (S5) it places nothing; the backtest trades them.
  • On CFD and FX data the market trades before the open, so the open is a time of day rather than a first print. The range is built from the run's bars, which may differ from the exchange's own opening range.
  • A narrow range with risk sizing means a large position. The engine refuses an entry the margin cannot carry, and a fast open can fill a stop order well past its price.
  • Breakouts of the opening range often fail and reverse; a day can take the stop and then run in the original direction. No result is promised.

RSI trend pullback (rsi_trend_pullback)

Buys short, sharp dips while price is above its 200 bar SMA and sells rallies below it, out when RSI(2) recovers or price crosses the 5 bar SMA.

Kind: Strategy template. Category: Mean reversion. Version: 1.1.0. Network: none.

Runs in the desktop app only (the engine on your machine).

A mean reversion template inside a trend filter, after the RSI(2) pullback idea Larry Connors and Cesar Alvarez published. The 200 bar SMA decides the direction: above it only longs, below it only shorts. A 2 bar RSI at or below 10 marks a short, sharp dip in an uptrend, and the long is entered at the next bar's open; above 90 marks a rally in a downtrend for a short. The trade ends when the RSI recovers above 70 (below 30 for a short) or the close crosses back over the 5 bar SMA, whichever comes first.

The published rules have no stop. This template adds two guards you can widen or switch off: a stop 3 ATR from the entry, which also sizes the position by risk, and an exit after 10 bars held. The published rules are written for daily bars of stocks and indices; on other markets and timeframes every length counts bars.

It exports to MetaTrader 5, cTrader and Rust as it runs here. It keeps its own record of the trade, the fill on the ask or the bid and the stop on the other side, so the expert advisor and the cBot follow the bid and ask of the quotes they see. TradingView's bars carry one price and no bid or ask, so there is no export to it.

Parameters

ParameterDefaultRangeWhat it does
trendLength20010 to 500Bars in the trend SMA: longs only above it, shorts only below it.
rsiLength22 to 30Bars in the RSI. 2 in the published rules.
entryLevel10.01.0 to 49.0A long enters when the RSI is at or below this, a short when it is at or above 100 minus this.
exitLevel70.051.0 to 99.0A long exits when the RSI rises above this, a short when it falls below 100 minus this.
exitSma50 to 50Bars in the exit SMA: a long exits on a close above it, a short on a close below it. 0 is off.
atrLength142 to 100Bars in the ATR the stop is measured in.
stopAtr3.00.5 to 20.0The stop's distance from the entry, in ATR.
maxBars100 to 200Close the position after it has been held this many bars, counting the bar it opened on. 0 is off.
riskPercent1.00.0 to 10.0Per cent of the balance lost if the stop is hit. 0 trades the run's lot size.
allowShort10 to 11 also sells rallies in a downtrend, 0 long only.

Use it in a strategy

python# The entry rule in your own strategy:
trend = ctx.ind("sma", length=200)
rsi = ctx.ind("rsi", length=2)
atr = ctx.ind("atr", length=14)
close = ctx.bars(1)[-1].close
if trend is not None and rsi is not None and atr is not None:
    if close > trend and rsi <= 10:
        ctx.signal("LONG", sl_pips=3 * atr / ctx.pip(), risk_percent=1.0)

To run the whole template, install it, switch it on and pick RSI trend pullback under Plugins in the strategy list. The rules were published for daily bars.

What it provides

  • rsi_trend_pullback/rsi_pullback: The strategy, with its parameters: trend SMA, RSI length, entry and exit levels, exit SMA, ATR stop, maximum bars held, the risk per trade and the short side.

Data source and terms

No outside data source.

When values are known

Every decision is made on a bar's close from that bar and the ones before it, and fills at the next bar's open. The stop is checked from the bar after the entry.

Limits

  • Buying dips means buying into falling prices. In a trend that breaks, the next bars can fall further before the exit; the stop limits that, at the price of being hit on deep dips the published rules would have held through.
  • Trades are short and frequent, so costs matter: spread, commission and the gap between a close and the next open. A test without realistic costs says little.
  • The published research used daily bars of US stocks and indices. Results on other markets or timeframes are not covered by it.
  • The template keeps its own record from the bars, as it did before a strategy could read the engine's position, so it trades as it always has. Without bid and ask on the bars the engine adds a spread to the fill the record cannot see, so on a bar that only just reaches the stop the two can differ; the record then waits for its own exit before the next entry.
  • No result is promised.

Power of Three (power_of_three)

ICT's Power of Three from the market structure blocks: the Asian range, a London sweep of it, and the trade the other way until the London close.

Kind: Strategy template. Category: Market structure. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Power of Three is ICT's picture of a trading day in three phases: accumulation, when price builds a range; manipulation, a false move out of that range that takes the stops beyond it; and distribution, the real move the other way. This template builds each phase from the market structure group of the indicator catalogue:

  • Accumulation is the Asian range: the high and low of the Asia kill zone, 20:00 to 24:00 New York, from killzones.
  • Manipulation is a sweep of that range in the London open kill zone, 02:00 to 05:00 New York: a bar trades below the Asian low and closes back above it (above the high and back below, for a short). With useMidnightOpen the long also needs its close below the midnight open from midnight_open, buying at a discount to the true day open.
  • Distribution is the trade, opened at the close of that bar, at most one a day. The stop sits a buffer beyond London's extreme so far, the target at a multiple of the stop distance or, with targetR 0, at the other side of the Asian range. Whatever is still open is closed when the London close kill zone ends, 12:00 New York.

The kill zones follow New York's clock through its daylight saving changes. This template has not been measured on market data and makes no claim about results.

Parameters

ParameterDefaultRangeWhat it does
stopBufferPips2.00 to 100Pips beyond London's low (high, for a short) so far where the stop sits.
targetR2.00 to 10The target as a multiple of the stop distance; 0 aims at the other side of the Asian range.
useMidnightOpen10 to 11 buys only below the midnight open and sells only above it, 0 ignores it.
riskPercent1.00 to 10Per cent of the balance lost if the stop is hit. 0 trades the run's lot size.
allowShort10 to 11 trades both directions, 0 long only.

Use it in a strategy

python# The manipulation in your own strategy: a London bar that sweeps the Asian low
# and closes back inside, below the true day open.
k = ctx.ind("killzones")
m = ctx.ind("midnight_open")
bar = ctx.bars(1)[-1]
if k.london_open == 1 and k.asia_low is not None and m.open is not None:
    if bar.low < k.asia_low < bar.close < m.open:
        ctx.signal("LONG", sl_pips=20, tp_pips=40)

To run the whole template, install it, switch it on and pick Power of Three under Plugins in the strategy list. Run it on M5, M15 or M30 bars.

What it provides

  • power_of_three/power_of_three: The strategy, with its parameters: the stop buffer, the target, the midnight open filter, the risk per trade and the short side.

Data source and terms

No outside data source.

When values are known

The Asian range is complete at 24:00 New York, hours before London opens; a sweep is known at the close of its bar, and the trade opens at the next bar's open. No decision uses a later bar.

Limits

  • A template to start from, not a tested edge: it has not been measured on market data, so no result is promised. Backtest it on your markets before you trust it.
  • The sweep is judged on one bar, beyond the range and back inside at its close; a sweep spread over several bars needs a change in the code.
  • Days without an Asia session in the data use the last Asian range there is.
  • Bank holidays and news are not known to the template. Spreads are wider in the Asian hours.

Silver Bullet (silver_bullet)

ICT's Silver Bullet from the market structure blocks: in a kill zone a liquidity sweep, then a limit order in the fair value gap that follows.

Kind: Strategy template. Category: Market structure. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

The Silver Bullet is an ICT setup for one hour of the trading day: price first runs the stops beyond a recent swing, then turns and leaves a fair value gap, and the trade is taken when price comes back into that gap. This template builds it from the market structure group of the indicator catalogue, each rule an element a strategy reads with ctx.ind:

  • killzones is the window. One kill zone on the New York clock is traded; the default, the London close from 10:00 to 12:00, holds ICT's AM Silver Bullet hour, 10:00 to 11:00.
  • liquidity_sweep is the setup. A wick below a swing low that closes back above it takes the sell side liquidity and makes the window bullish; a sweep of a swing high makes it bearish.
  • fvg is the trigger. The first fair value gap in the direction of the bias, formed after the sweep, gets a buy limit at its top (a sell limit at the bottom of a bearish gap).
  • liquidity is the target when targetR is 0: the nearest swing high no price has traded above yet, for a long.

The stop sits a buffer beyond the most extreme price since the sweep. At most one order per window; an order still unfilled when the window ends is cancelled, and a position runs to its stop or target.

The same rules in a strategy of your own read the same streams:

pythonk = ctx.ind("killzones")
sweep = ctx.ind("liquidity_sweep", length=3)
gap = ctx.ind("fvg", min_size=0.25)

This template has not been measured on market data and makes no claim about results.

Parameters

ParameterDefaultRangeWhat it does
killzone30 to 3The kill zone traded: 0 Asia (20:00 to 24:00), 1 London open (02:00 to 05:00), 2 New York open (07:00 to 10:00), 3 London close (10:00 to 12:00), all New York time.
swingBars31 to 20Bars on each side of a swing for the sweep and the liquidity target; a swing counts this many bars after its extreme.
minGapAtr0.250 to 5The smallest fair value gap that triggers, in multiples of the ATR.
stopBufferPips1.00 to 100Pips beyond the most extreme price since the sweep where the stop sits.
targetR2.00 to 10The target as a multiple of the stop distance; 0 aims at the nearest untaken liquidity instead.
riskPercent1.00 to 10Per cent of the balance lost if the stop is hit. 0 trades the run's lot size.
allowShort10 to 11 trades both directions, 0 long only.

Use it in a strategy

python# The trigger in your own strategy: after a sell side sweep in the window,
# a bullish gap gets a buy limit at its top.
k = ctx.ind("killzones")
sweep = ctx.ind("liquidity_sweep", length=3)
gap = ctx.ind("fvg", min_size=0.25)
if k.london_close == 1 and sweep.down == 1:
    self.bias = 1
elif k.london_close == 1 and getattr(self, "bias", 0) == 1 and gap.bull_new == 1:
    bars = ctx.bars(3)
    ctx.order("LONG", limit=bars[-1].low, sl_pips=20, tp_pips=40)
    self.bias = 0

To run the whole template, install it, switch it on and pick Silver Bullet under Plugins in the strategy list. Run it on M1, M5 or M15 bars of a market that trades in New York hours.

What it provides

  • silver_bullet/silver_bullet: The strategy, with its parameters: the kill zone, the swing bars, the smallest gap, the stop buffer, the target, the risk per trade and the short side.

Data source and terms

No outside data source.

When values are known

Every element is known at the close of its bar: a sweep needs its swing confirmed swingBars bars after the extreme, a gap shows on its third candle. The order rests from the next bar on, so no decision uses a later bar.

Limits

  • A template to start from, not a tested edge: it has not been measured on market data, so no result is promised. Backtest it on your markets before you trust it.
  • The kill zones of the catalogue are wider than ICT's one hour windows; for the 10:00 to 11:00 hour alone, change the window test in the code.
  • A limit order fills only when price returns into the gap; on a fast day it never does and the window passes without a trade.
  • Bank holidays and news are not known to the template. Spreads widen around the New York open.

Turtle breakout (turtle_breakout)

The Turtle Traders' published rules: channel breakouts, a stop at 2N, units sized by risk and the System 1 skip-after-winner filter.

Kind: Strategy template. Category: Trend following. Version: 1.1.0. Network: none.

Runs in the desktop app only (the engine on your machine).

A trend following template built from the rules Curtis Faith published in "The Original Turtle Trading Rules" and in "Way of the Turtle". It enters when price trades through the highest high or the lowest low of the last 20 days (System 1) or 55 days (System 2), and leaves when price trades through the opposite channel of the last 10 or 20 days. N is the 20 day average true range with Wilder's smoothing. The stop sits 2N from the entry price, and a unit is sized so that a move of 1N changes the balance by 1 per cent, which puts 2 per cent at risk at the stop.

The defaults are System 1, with its filter on: a breakout is skipped when the last breakout, taken or not, would have closed at a profit. A skipped breakout is still entered at the 55 day breakout (the failsafe), so a long trend is not missed. For System 2 set the entry to 55 days, the exit to 20 days and the filter off.

Entries are resting stop orders at the channel, placed on each bar's close for the next bar, so a breakout fills where the market crossed the channel (or at the open when it gapped through). The code is short and commented: read it, change it, and keep it as your own strategy.

It exports to MetaTrader 5, cTrader and Rust as it runs here. It keeps its own record of the trade, its stop orders filled on the ask or the bid and its stop on the other side, so the expert advisor and the cBot follow the bid and ask of the quotes they see. TradingView's bars carry one price and no bid or ask, so there is no export to it.

Parameters

ParameterDefaultRangeWhat it does
entryDays202 to 300Days in the entry channel: a breakout of the highest high or lowest low of this many days enters. 20 for System 1, 55 for System 2.
exitDays102 to 300Days in the exit channel: a break of the opposite side of this many days closes the position. 10 for System 1, 20 for System 2.
atrDays202 to 100Length of N, the average true range with Wilder's smoothing.
stopN2.00.5 to 10.0The stop's distance from the entry, in N.
riskPerN1.00.0 to 5.0Per cent of the balance a move of 1N is worth; the stop then risks this times the stop distance. 0 trades the run's lot size.
skipAfterWinner10 to 11 skips a breakout when the last one, taken or not, would have closed at a profit (System 1). 0 takes every breakout (System 2).
failsafeDays550 to 300Days in the channel a skipped breakout is still entered at. 0, or no longer than the entry channel, turns it off.
allowShort10 to 11 trades breakouts in both directions, 0 long only.

Use it in a strategy

python# The core rule in your own strategy: a buy stop at the 20 day high for the next bar,
# with the stop 2N away and the size set by risk.
n = ctx.ind("atr", length=20)
high = ctx.ind("dc", length=20).upper
if n is not None and high is not None:
    ctx.order("LONG", stop=high, sl_pips=2 * n / ctx.pip(), bars=1, risk_percent=2.0)

To run the whole template, install it, switch it on and pick Turtle breakout under Plugins in the strategy list. Run it on D1: every length counts bars.

What it provides

  • turtle_breakout/turtle: The strategy, with its parameters: entry and exit channel days, the ATR length for N, the stop in N, the risk per N, the System 1 filter, the failsafe channel and the short side.

Data source and terms

No outside data source.

When values are known

Every decision is made on a bar's close from that bar and the ones before it. The entry orders rest during the next bar only; the channel exit is sent on the close of the bar that broke the channel and fills at the next bar's open.

Limits

  • One unit per breakout. The Turtles added a unit every half N in their favour, up to 4 units, and moved every stop up with the last one. The engine holds one position per instrument, so this template cannot add to a position.
  • One instrument per run. The Turtles traded a portfolio of futures with limits per market, per group of correlated markets and in total; none of those limits is modelled here.
  • The channel exit fills at the next bar's open, not at the channel price, because the engine has no resting exit order. On a fast day that open can be well past the channel.
  • The size is measured on the balance when the order is placed. The Turtles cut their trading size after a drawdown (a 10 per cent drawdown cut it by 20 per cent); this template does not.
  • An exit and an entry the other way on the same bar: the entry waits for the next new breakout. While a position is open no opposite order rests.
  • The template keeps its own record of the position from the bars, by the rules the engine fills by, as it did before a strategy could read the engine's position, so it trades as it always has. When the engine refuses an entry (not enough margin), the record still counts it until its stop or exit.
  • Long trends have made these rules famous, and long sideways periods are where they lose: many small losing breakouts in a row are normal for this kind of system. No result is promised.

Exporter

Webhook summary (webhook_summary)

Posts a run's headline figures to a webhook of your own, as generic JSON, a Discord message or a Slack message. HTTPS only, summary only by default.

Kind: Exporter. Category: Integrations. Version: 1.0.0. Network: a host you enter (setting webhookUrl).

Runs in the desktop app only (the engine on your machine).

After a run, the exporter sends net P&L, return, maximum drawdown, the number of trades, the win rate and the profit factor, with the instrument, timeframe and period, to a webhook you set up: your own service or an automation tool (generic JSON), a Discord channel or a Slack channel. The trades go along only when you switch them on.

Nothing leaves your machine by accident. You enter the webhook's host as a setting of its own, so you see where the summary goes, and the URL has to be on exactly that host and use HTTPS. While Send is off, which is the default, an export sends nothing: it shows the payload, the host it would go to and whether the settings are complete. Switch Send on once that looks right. The webhook URL is a secret, so neither the preview nor the plugin's log shows more of it than the host.

The generic JSON says "format": "orionquant-webhook-summary", "formatVersion": 1 and "hypothetical": true, with the blocks run and summary, and trades (at most 500, the rest counted in tradesOmitted) when switched on. Discord and Slack get a short text message; trades that do not fit their length limit are counted at the end.

Where to get a webhook URL: Discord, in a channel's settings under Integrations, Webhooks (https://docs.discord.com/developers/resources/webhook); Slack, with an app that has incoming webhooks (https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks). Their terms apply to what you post: Discord's developer terms and Slack's API terms. Slack allows about one message per second per webhook; each export sends one.

Configuration

KeyDefaultWhat it does
format"generic"generic: a JSON object for your own service. discord: a Discord webhook message (mentions switched off). slack: a Slack incoming webhook message.
host""The one host this plugin may send to, like discord.com or hooks.slack.com. The webhook URL must be on it.
includeTradesfalseSend the trades too (time, side, symbol, prices, lots, net, exit reason). Off sends the summary only.
label"OrionQuant backtest"The title of the message, so you can tell runs apart in a channel.
sendfalseOff: an export only shows what it would send and where. On: an export posts it.
webhookUrl""The webhook's full https:// URL from Discord, Slack or your service. Treat it like a password: whoever has it can post to your channel.

Use it in a strategy

Not called from a strategy: after a run, choose it in the result's export menu. Exports are part of the plans that include CSV export.

What it provides

  • POST <webhook URL>: One HTTPS POST with a JSON body in the chosen format, when Send is on. The export answers with the host and the HTTP status.
  • preview: While Send is off: the payload, the host it would go to, and whether the settings are ready, as text. Nothing is sent.

Data source and terms

No outside data source.

Limits

  • It sends to the host of the URL you enter, which can be any HTTPS host; the host setting has to name the same host, so you see it before anything is sent.
  • The webhook URL is kept in the plugin's settings on this machine, in plain text like every plugin setting.
  • One attempt per export, no retry and no redirect: a webhook that is down or moved means the export fails and says why.
  • The figures are those of a hypothetical backtest; every message says so.

CSV report (csv_report_exporter)

Writes a finished backtest as one CSV file: the headline statistics, then every trade.

Kind: Exporter. Category: Reports. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

An exporter takes a finished result and turns it into a file. This one writes a short header (instrument, timeframe, seed), the statistics as name and value rows, and the trade log with one column per trade field.

It is also the smallest example of an exporter plugin: one function that receives the result as plain data and returns the path it wrote.

Configuration

KeyDefaultWhat it does
fileName"backtest_report"The start of the file name; the run's seed is added, and the file ends in .csv.

Use it in a strategy

Not called from a strategy: after a run, choose it in the result's export menu. Exports are part of the plans that include CSV export.

What it provides

  • exports/backtest_report_<seed>.csv: A CSV file in the exports folder of the data cache.

Data source and terms

No outside data source.

Limits

  • Plain CSV with a comma; a spreadsheet set to a German locale may need the import dialog.

Notebook JSON export (notebook_json_export)

Writes a whole run as one versioned, documented JSON file plus a trades CSV and a JSON Schema, ready for pandas or any notebook.

Kind: Exporter. Category: Reports. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

For your own analysis in Jupyter, pandas or any other tool: the run as one JSON file with what ran (instrument, timeframe, period, seed, currency, engine), its configuration, the statistics, the equity curve, every trade with every field the engine recorded, and the monthly returns. The trades also come as a CSV, and the file's JSON Schema comes along, so the export explains itself wherever it is copied.

The file says "format": "orionquant-notebook" and "formatVersion": 1. Version 1 only ever gains fields; a change of meaning would be version 2, so code written against version 1 keeps working. Keys are camelCase as in the engine's result, times are ISO 8601 in UTC, money is in run.resultCurrency.

In pandas:

pythonimport json
import pandas as pd

with open("notebook_EURUSD_42.json", encoding="utf-8") as fh:
    nb = json.load(fh)
trades = pd.DataFrame(nb["trades"])
equity = pd.DataFrame(nb["equity"]).assign(ts=lambda d: pd.to_datetime(d["ts"])).set_index("ts")
trades_csv = pd.read_csv("notebook_EURUSD_42_trades.csv", parse_dates=["entryTs", "exitTs"])

Configuration

KeyDefaultWhat it does
equity"all"all: one equity point per bar. daily: the last point of each UTC day, which keeps the file small for a long run on a short timeframe.
fileName"notebook"The start of the file names; the instrument and the run's seed are added.

Use it in a strategy

Not called from a strategy: after a run, choose it in the result's export menu. Exports are part of the plans that include CSV export. The pandas lines above read the files it writes.

What it provides

  • notebook_<instrument>_<seed>.json: The run: run, config, stats, costsUsed, equitySampling, equity, trades and monthlyReturns, as the schema describes.
  • notebook_<instrument>_<seed>_trades.csv: One row per trade, columns named as in the JSON (id, symbol, direction, entryTs, exitTs, prices, lots, P&L, costs, exitReason, mae, mfe and more).
  • notebook_json_export.schema.json: The JSON Schema (draft 2020-12) of the JSON file, with a description of every field that needs one.

Data source and terms

No outside data source.

Limits

  • The strategy's source code is left out of the configuration (strategy.code and strategy.files), so a shared file does not share your strategy.
  • The price bars and indicator series are not included; a long run on a short timeframe still gives a large file, which equity daily makes smaller.
  • A number that is not finite (an infinite profit factor, say) is written as null, because JSON has no Infinity.

Realised P&L by year (realised_pnl_by_year)

Sums a run's closed trades per calendar year and instrument: gross gains, losses, commission, swap and net, as CSV and Markdown, in EUR when ECB rates are cached.

Kind: Exporter. Category: Reports. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

Not tax advice: a backtest is hypothetical, and this exporter only adds up its trades. Ask a tax adviser how real trading results are to be declared.

A trade counts in the year it was closed, on the clock of the time zone you set, so a trade closed at 23:30 UTC on 31 December counts in the next year in Berlin. Per year and instrument the report lists the number of trades (won and lost), the gross gains (trades that made money before costs), the gross losses, the commission paid, the swap (a credit is positive) and the net result: net = gross gains + gross losses - commission + swap, which is the sum of the trades' own net figures.

The amounts are in the result's currency, which is the instrument's quote currency (USD for EURUSD); the engine converts nothing. With the ECB reference rates plugin (ecb_fx_reference) switched on and its data fetched, the report adds every amount in EUR: each trade at the euro reference rate known on the day it closed, read from the feed ecb_eur plus the currency (ecb_eurusd holds USD per 1 EUR). Without that feed, or when it does not cover every closing day, the report stays in the result's currency and says why in its first lines.

Configuration

KeyDefaultWhat it does
convertToEurtrueAdd every amount in EUR with the ECB reference rates, when their feed is on this machine. Off keeps the report in the result's currency.
fileName"realised_pnl"The start of both file names; the instrument and the run's seed are added, the files end in .csv and .md.
timeZone"Europe/Berlin"The time zone whose midnight separates the years, as an IANA name like Europe/Berlin or UTC.

Use it in a strategy

Not called from a strategy: after a run, choose it in the result's export menu. Exports are part of the plans that include CSV export. For EUR, switch on ecb_fx_reference and run once with "Fetch external data (news and plugin feeds)" ticked, so its feed is on this machine.

What it provides

  • realised_pnl_<instrument>_<seed>.csv: One row per year and instrument. Two lines starting with # come first (the disclaimer and the currency basis); pandas reads it with comment="#". The _eur columns are empty when nothing was converted.
  • realised_pnl_<instrument>_<seed>.md: The same as a Markdown report: the disclaimer, the currency basis, a table per year and a table per year and instrument.

Data source and terms

No outside data source.

Limits

  • Not tax advice and not a tax report. It does not know your country's rules, loss offsetting, allowances or the tax categories of CFDs, futures or forex.
  • The trades are those of a hypothetical backtest. A trade the engine closed at the end of the data counts in the year the data ended.
  • EUR only through the ECB reference rates, one a day: a trade uses the last rate published before the day it closed, which on a Monday is Friday's.

Trade journal CSV (trade_journal_csv)

Writes every trade of a run as one CSV row for your trading journal, with local times, session, pips, R and empty columns for notes.

Kind: Exporter. Category: Reports. Version: 1.0.0. Network: none.

Runs in the desktop app only (the engine on your machine).

A journal wants more per trade than the engine's trade log: the time on your own clock next to UTC, the weekday and the FX session the trade was opened in, the move in pips and in R, and room for your own setup name, notes and grade. This exporter writes exactly that, one row per trade, in a file a spreadsheet opens directly.

Columns: trade_id, symbol, side, lots, entry and exit time in UTC and in your time zone, entry_weekday, entry_session (Sydney, Tokyo, London, New York, joined with + where they overlap), entry, exit, stop and target price, price_move, pips, r_multiple, gross_pnl, commission, swap, net_pnl, currency, duration_minutes, bars_held, exit_reason, mae, mfe, and the empty setup, notes and grade.

R is the price move divided by the distance from the entry to the stop the trade was opened with. MAE and MFE are the worst and best open profit on the way, in money, as the engine recorded them. Every figure comes from the result as it is: what the result does not carry stays empty instead of being guessed.

Configuration

KeyDefaultWhat it does
fileName"trade_journal"The start of the file name; the instrument and the run's seed are added, and the file ends in .csv.
layout"comma"comma: comma separated with a decimal point, for English spreadsheets and pandas. semicolon: semicolon separated with a decimal comma, which is what a spreadsheet set to German expects.
timeZone"Europe/Berlin"Your time zone as an IANA name, like Europe/Berlin, America/New_York or UTC. The local time and weekday columns use it, daylight saving time included.

Use it in a strategy

Not called from a strategy: after a run, choose it in the result's export menu. Exports are part of the plans that include CSV export.

What it provides

  • trade_journal_<instrument>_<seed>.csv: One CSV file, UTF-8 with a byte order mark so umlauts in your notes survive Excel, one row per trade under a header row.

Data source and terms

No outside data source.

Limits

  • Pips only for currency pairs and custom symbols that state a pip size; for an index, a metal or a share the pips column stays empty and price_move holds the move.
  • No R for a trade opened without a stop. MAE and MFE are empty on a result from before the engine recorded them.
  • Money columns are in the result's currency, the instrument's quote currency; the engine does not convert to the account currency.
  • The sessions are common conventions on each centre's own clock (London 08:00 to 17:00 and so on), not official hours; the FX market has none.
OrionQuant