Documentation

Talios

Strategy API reference

Every function: orders, positions, series, indicators, FVGs, news() and near_news().

The complete reference of the talios package: everything your strategy can read and call. Two imports cover it all.

from talios import Strategy, Long, Short
from talios.indicators import ema, sma, rsi, atr, highest, lowest, crossover, crossunder, fvg, killzone, session, news, near_news

Strategy

NameWhat it is
setup(self)Optional. Once per pair, before the first bar.
on_bar(self, bar)Required. At each bar's close. Without it, the run stops with "define on_bar(self, bar) in your Strategy".
self.dataThe pair's price series (see Series).
self.pairThe pair being run, e.g. "EUR/USD".
self.timeframeThe backtest timeframe, e.g. "1H".
self.minutesThe bar length in minutes (60 on 1H).
self.positionThe open Position, or None.
self.orderThe pending Order, or None.
self.tradesThe pair's closed trades so far (a list of Trade).
cost_r = 0.0Class attribute: R taken off every trade to account for spread and commission (0 or more). Without it, trades are costless.
ParametersAny other class attribute holding a number, string or boolean (up to 50).

Talios creates one fresh instance of your class per pair, so state kept on self never leaks from one pair to the next.

Series and Bar

NameWhat it is
self.data.open / .high / .low / .close / .volumeLists of the same length, oldest first.
self.data.timeEach bar's open time, in epoch seconds UTC.
self.data.killzone / .sessionOne label per bar (or None), handy to precompute filters in setup.
len(self.data), self.data[i]The number of bars, and the Bar at index i (negative indexes and slices work).
bar.iThe bar's index in the series.
bar.time / bar.dtIts open time, in epoch seconds UTC / as a UTC datetime.
bar.localIts open time on the project's timezone, summer and winter time included: bar.local.hour == 9 is 9:00 your time all year. local(t) in talios.indicators does the same for any time.
bar.open / .high / .low / .close / .volumeIts prices and volume.
bar.pair / .killzone / .sessionIts pair, killzone and session.

Never read the future

The whole series is in self.data so indicators can be computed once in setup. Inside on_bar, only read indexes up to bar.i: anything above is look-ahead, and makes results that can't be traded.

Orders and positions

One position at a time per pair, and at most one pending order. Long and Short are the strings "Long" and "Short".

self.enter(direction, stop, target=None, rr=None, price=None, expiry=None, note=None)

ArgumentMeaning
directionLong or Short.
stopRequired. The stop loss price.
target / rrThe take profit, as a price (target) or as a multiple of the risk measured from the fill (rr). Give one or neither, never both. With neither, the trade ends on its stop, on exit() or at the end of the data.
priceNone: a market order, filled at the next bar's open. A price: an order working from the next bar. For a Long, at or below the signal bar's close it is a limit (fills when a low reaches it), above it a stop entry (fills when a high reaches it); the reverse for a Short. It fills at its price, or at the open when a bar opens past it.
expiryHow many bars a price order stays working (a whole number, 1 or more). None: until it fills, is cancelled or replaced.
noteA text up to 200 characters, carried to the trade.

Returns True when the order is placed. It is refused, with a note in the console and False, when a position is already open, when the stop or the target is on the wrong side of the entry (the order's price, or this bar's close for a market order), or when rr is not above 0. A new enter replaces a pending order. An order whose fill lands past its stop or target is cancelled: the setup is gone.

CallWhat it does
self.cancel()Drops the pending order.
self.exit(note=None)Closes the position at this bar's close. False when there is none.
self.move_stop(price)Moves the open position's stop (break-even, trailing). R stays measured on the initial stop.
self.move_target(price)Moves the target; move_target(None) removes it.
Orders in practice
# Market order: filled at the next bar's open, stop at a price, target at 2R
self.enter(Long, stop=bar.low, rr=2)

# Limit order at 1.0850, valid for 5 bars, fixed target price
self.enter(Long, stop=1.0820, target=1.0920, price=1.0850, expiry=5)

# Break-even once the trade is 1R in profit
pos = self.position
if pos and pos.r(bar.close) >= 1:
    self.move_stop(pos.entry)

# Time exit
if pos and pos.bars(bar) >= 24:
    self.exit("time")

Position, Order and Trade

ObjectFields
Position (self.position).direction, .entry (fill price), .stop, .target, .initial_stop, .entry_i, .entry_time, .risk (the price distance of 1R), .note, .r(price) (R at that price, before cost_r), .bars(bar) (bars held, 0 on the fill bar).
Order (self.order).direction, .price, .stop, .target, .rr, .placed_i, .expiry, .note, .kind ("market", "limit" or "stop").
Trade (self.trades).direction, .entry, .exit, .r, .entry_i, .exit_i, .entry_time, .exit_time, .initial_stop, .stop, .target, .reason ("stop", "target", "exit" or "end"), .open, .note, .mae (how far it went against you, in R).

talios.indicators

Every indicator only uses bars up to the one it is computed for, and returns a list as long as its input, with None while it warms up. A list argument can also be the series itself (its closes are used).

FunctionReturns
ema(values, n)Exponential moving average (starts from the average of the first n values).
sma(values, n)Simple moving average.
rsi(values, n=14)Wilder's RSI, 0 to 100.
atr(data, n=14)Wilder's average true range. Takes the series (self.data), not a list.
highest(values, n), lowest(values, n)Rolling max / min over the last n values, the current one included.
crossover(a, b, i), crossunder(a, b, i)True when list a crosses above / below b (a list or a number) at index i (or at a Bar).
fvg(data, min_size=0.0, max_age=None)The series' fair value gaps, exactly as on the Afterburner chart (see below).
killzone(bar)The killzone the bar opens in, from your own chart Killzone settings (Paris time; by default "Asia" 02:00–06:00, "London" 08:00–11:00, "NY" 13:00–16:00), or None.
session(bar)The journal's session of the bar's open, in UTC: "Asia" (00–07), "London" (07–12), "Overlap" (12–16), "New York" (16–21) or "Off Hours".
news(pair=None, impact="high")The economic calendar releases of the backtest period that move the pair's currencies, sorted by time (see below).
near_news(bar, minutes=30, impact="high", pair=None)True when a release of that impact falls within minutes before or after the bar's open (edges included).

Fair value gaps

A gap is bullish when bar i's low is above bar i−2's high, bearish when bar i's high is below bar i−2's low. min_size is a minimum height in price; max_age drops an untouched gap after that many bars. fvg() returns a Gaps object:

  • gaps.formed_at(i): the gaps completed at bar i.
  • gaps.active(i): the gaps formed before i and not touched yet, newest last.
  • gaps.first_touch(bar): the oldest active gap this bar trades back into (then marked touched), or None. A gap counts once.
  • A Gap has .bullish, .bearish, .top, .bottom, .height, .mid, .formed (index of its third bar), .time and .touched (index or None).

News functions

Talios strategies read the same economic calendar as the News indicator of your charts (13 countries, since 2020). impact is "high", "medium", "low" or a list of them. A release counts for a pair when it moves one of its currencies: gold and the US indices follow USD, GER40 follows EUR, and so on.

  • news() returns a list of releases, each with .time (epoch seconds UTC), .dt, .title, .currency, .country and .impact. It builds a new list each call: call it once in setup and keep it.
  • near_news() answers the usual question directly: is this bar too close to a release?
strategy.py: a trend strategy that sits out the news
from talios import Strategy, Long, Short
from talios.indicators import ema, near_news, news


class TrendNoNews(Strategy):
    """EMA 50 / 200 trend, sitting out 30 min around high-impact news."""

    news_window = 30    # minutes before and after a release

    def setup(self):
        self.fast = ema(self.data.close, 50)
        self.slow = ema(self.data.close, 200)
        self.releases = news(impact="high")   # once, in setup
        print(len(self.releases), "high-impact releases for", self.pair)

    def on_bar(self, bar):
        i = bar.i
        if self.position or self.slow[i] is None:
            return
        if near_news(bar, minutes=self.news_window, impact="high"):
            return
        if self.fast[i] > self.slow[i] and bar.close > self.fast[i]:
            self.enter(Long, stop=bar.low, rr=2)
        elif self.fast[i] < self.slow[i] and bar.close < self.fast[i]:
            self.enter(Short, stop=bar.high, rr=2)

Also importable

Bar, Series, Position, Order, Trade from talios; Gap, Gaps, NewsEvent from talios.indicators. Useful for type hints in your own helper files.