← 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(()); }