Ir al contenido
← Todos los ejemplos

Ejemplo 77

Alma cross

a trend strategy on the Arnaud Legoux Moving Average (Arnaud Legoux and Dimitrios Kouzis-Loukas, 2009): it buys when a bar closes above the ALMA and sells when a bar closes below it,

Categoría
Host: necesita los datos y el motor de la terminal
Permisos
orders.submit
Antes de ejecutarlo
a chart in hand on the live market or a replay whose instrument the live feed serves (closed bars), nine closed bars of history to start the line
Qué hace
asks the terminal to run the strategy on that market; once running it places orders on the account the person chose.
Cómo ejecutarlo
Add script on the Market Replay window or the title bar's script icon (replay or real time); Insert strategy in the Datasets window (backtest).
Lenguaje
>=0.2.0, <0.3.0
Host
>=0.1.17, <0.2.0
Necesita
features >=0.1.8, <0.2.0trade >=0.1.0, <0.2.0

Código

// Purpose: a trend strategy on the Arnaud Legoux Moving Average (Arnaud Legoux and Dimitrios Kouzis-Loukas, 2009): it buys when a bar closes above the ALMA and sells when a bar closes below it,
// so it is always on the side the last close chose -- long above the line, short under it. The line is the same one the indicator draws (76_alma.tzi) with the authors' values: 9 bars, offset 0.85,
// sigma 6, i.e. a Gaussian over the nine last closes whose peak sits on the second newest. A strategy's signals can only be sums and products of what the engine gives it, so the line is written out:
// each close (`features.close_ago(k)`, 0 the last closed bar) times its weight, the nine weights summing to 1. They are `exp(-(i - m)^2 / (2 s^2))` for i = 0..8 (0 the oldest) with
// m = offset * (window - 1) = 6.8 and s = window / sigma = 1.5, divided by their sum; to try another window, offset or sigma, compute the weights again the same way and write one `close_ago` per
// weight (76_alma.tzi takes those three as its configuration).
//
//   LONG    a bar closes above the ALMA (the close goes up through it): a market buy, with a target 0.3 % above the entry and a stop 0.3 % below it -- one risk for one reward, 1:1.
//   SHORT   a bar closes under the ALMA: a market sell, target 0.3 % below, stop 0.3 % above.
//   EXIT    the target or the stop, whichever the price reaches first (they are orders the host leaves on the position, not conditions the strategy watches); or a bar closing on the other side of the
//           line, which closes the position at the market and, the book being flat, takes the other side at once -- stop and reverse, with its own 0.3 % pair. A position closed by its target or stop waits for the
//           next cross: it does not buy again in the same trend.
//   `distance_pct` is that 0.3 (a percentage of the entry price, the same distance for the target and the stop); change it in `main` to size the pair.
//
// WHY THE CLOSE OF A BAR AND NOT THE PRICE NOW: the price crosses a moving line many times inside one bar when it is close to it, and each crossing would be a trade; a bar's close is one reading, never
// repainted, and a backtest, a replay and the live market read it the same way. The cost is that the signal comes when the bar closes, not when the price touched the line.
// HONEST NOTE: written to the specification and checked that it compiles and hands over a strategy; its edge has not been measured. A moving-average cross loses in ranges (it buys the top of a swing
// and sells the bottom) and pays in trends, and the fees of a reversal each time are not small on a short timeframe: backtest it on the instrument and timeframe before running it with money.
// Category: host
// Permissions: orders.submit
// Preconditions: a chart in hand on the live market or a replay whose instrument the live feed serves (closed bars), nine closed bars of history to start the line
// Side effects: asks the terminal to run the strategy on that market; once running it places orders on the account the person chose.
// Run: Add script on the Market Replay window or the title bar's script icon (replay or real time); Insert strategy in the Datasets window (backtest).

lang ">=0.2.0, <0.3.0";
host "ttt" ">=0.1.17, <0.2.0";
requires "features" ">=0.1.8, <0.2.0";
requires "trade" ">=0.1.0, <0.2.0";

strategy alma_cross(distance_pct: Decimal, size: Quantity) version "1.1.0" {
    // The last closed bar, and the ALMA(9, 0.85, 6) of the closes up to it. Unknown until nine bars are known, and a rule that reads an unknown does not fire.
    let bar = features.bar_close();
    let alma =
        0.0000104644 * features.close_ago(8) +
        0.0001720830 * features.close_ago(7) +
        0.0018144406 * features.close_ago(6) +
        0.0122667003 * features.close_ago(5) +
        0.0531732242 * features.close_ago(4) +
        0.1477877594 * features.close_ago(3) +
        0.2633687034 * features.close_ago(2) +
        0.3009331953 * features.close_ago(1) +
        0.2204734295 * features.close_ago(0);

    let px = features.price(features.Source.AggTrades);
    let held = features.position_sign();
    // The target and the stop, the same distance either side of the price the entry is decided at.
    let above = px * (1 + distance_pct / 100);
    let below = px * (1 - distance_pct / 100);

    // Which side to take once flat after a position was closed by the line: 1 long, -1 short, 0 none. Set when a bar closes on the other side of the line, cleared when the entry is made.
    var side = 0;
    let to_long = 1;
    let to_short = 0 - 1;
    let none = 0;

    // A bar closed on the other side of the line: the position is closed, and the other side is the next entry.
    rule exit_long when exposure_is(Exposure.Open) && held > 0 && cross_below(bar, alma)
        => [order(Side.Sell, Intent.Close, OrderType.Market, Sizing.Fixed(size)), set(side, to_short)];
    rule exit_short when exposure_is(Exposure.Open) && held < 0 && cross_above(bar, alma)
        => [order(Side.Buy, Intent.Close, OrderType.Market, Sizing.Fixed(size)), set(side, to_long)];

    // Flat on a cross: take that side.
    rule enter_long when exposure_is(Exposure.Flat) && cross_above(bar, alma)
        => order(Side.Buy, Intent.Open, OrderType.Market, Sizing.Fixed(size), Protect.Bracket(above, below));
    rule enter_short when exposure_is(Exposure.Flat) && cross_below(bar, alma)
        => order(Side.Sell, Intent.Open, OrderType.Market, Sizing.Fixed(size), Protect.Bracket(below, above));

    // Flat after the line closed a position: the side the cross chose, while the last bar is still on it.
    rule reverse_long when exposure_is(Exposure.Flat) && side > 0 && bar > alma
        => [order(Side.Buy, Intent.Open, OrderType.Market, Sizing.Fixed(size), Protect.Bracket(above, below)), set(side, none)];
    rule reverse_short when exposure_is(Exposure.Flat) && side < 0 && bar < alma
        => [order(Side.Sell, Intent.Open, OrderType.Market, Sizing.Fixed(size), Protect.Bracket(below, above)), set(side, none)];
    flatten_on_stop;
}

fn main() -> Result<Unit, Error> {
    // A target and a stop 0.3 % from the entry; a hundredth of a coin.
    let run = trade.run(alma_cross(0.3, qty(0.01)))?;
    print(run.name + " on " + run.target + ": " + run.state);
    return Ok(());
}