Skip to content
← All examples

Example 50

Sweep fade managed

a managed order-flow trade, written to use what a complex strategy needs: an entry that carries its own stop and target, a part of the

Category
Host: needs the terminal's data and engine
Permissions
orders.submit
Before you run it
a chart in hand on the live market or a replay, whose instrument the live feed serves (aggregated prints and closed bars)
What it does
asks the terminal to run the strategy on that market; once running it places orders on the account the person chose.
How to run it
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).
Language
>=0.1.2, <0.2.0
Host
>=0.1.9, <0.2.0
Needs
features >=0.1.4, <0.2.0trade >=0.1.0, <0.2.0

Source

// Purpose: a managed order-flow trade, written to use what a complex strategy needs: an entry that carries its own stop and target, a part of the
// position taken off on the way, the stop brought to break-even once it has, a time limit, and a rest between trades.
//
//   SETUP    the bar that just closed went THROUGH the edge of the last `lookback` bars' range and closed back inside it, with the aggressors
//            pushing into the edge (buying into a high that did not hold, selling into a low that did not): a failed break.
//   ENTRY    against the break, at the market. The stop rests `pad` dollars beyond the bar's extreme; the distance from the price to it is the
//            trade's risk, R, remembered from the entry (`at_entry`).
//   MANAGE   at `first_r` R in favour, half of the position is closed and the stop is moved to the entry price;
//            the rest runs to the target, `final_r` R, or is closed after `max_minutes`.
//   REST     no new trade for `rest_minutes` after one closes.
//
// HONEST NOTE ON ITS EDGE: IT HAS NONE THAT WAS FOUND. This setup was studied on the twenty days of BTCUSDT prints recorded on this machine.
// As written here (the 8-hour range, the stop 5 dollars past the bar), over those twenty days it would have taken 251 trades and made +0.5 basis
// points a trade before fees and LOST 10 a trade after them (taker 5 bp and 1 bp of slippage, maker 2 bp): its risk is a few dollars, and a
// round trip costs more than the move it catches. A variant with a wide stop made +13 bp a trade net on the first twelve days and lost 23 on the
// eight kept aside. It is here to show how a managed trade is written, and to be studied and changed -- not to be trusted 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 (aggregated prints and closed bars)
// 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.1.2, <0.2.0";
host "ttt" ">=0.1.9, <0.2.0";
requires "features" ">=0.1.4, <0.2.0";
requires "trade" ">=0.1.0, <0.2.0";

strategy sweep_fade(lookback: Int, pad: Decimal, first_r: Decimal, final_r: Decimal, max_minutes: Decimal, rest_minutes: Decimal, size: Quantity) version "1.0.0" {
    // The last closed bar, and the range of the `lookback` bars before it.
    let px = features.price(features.Source.AggTrades);
    let close = features.bar_close();
    let high = features.bar_high();
    let low = features.bar_low();
    let range_high = features.highest_high(lookback, 1);
    let range_low = features.lowest_low(lookback, 1);
    let delta = features.bar_delta(features.Source.AggTrades);

    // True on the one step where a new bar has closed -- a bar is looked at once.
    let new_bar = close != prev(close);
    let failed_up = high > range_high && close < range_high && delta > 0;
    let failed_down = low < range_low && close > range_low && delta < 0;
    let rested = features.flat_minutes() >= rest_minutes;

    // Where the stop goes, and the risk that makes.
    let short_stop = high + pad;
    let long_stop = low - pad;

    // The open trade: its side, its price, and the risk it was taken with -- held from the entry, so that it is still the same after the stop
    // that defined it has been moved.
    let held = features.position_sign();
    let entry = features.entry_price();
    let short_risk = at_entry(short_stop - px);
    let long_risk = at_entry(px - long_stop);
    let whole = features.position_fraction() > 0.75;
    let aged = features.position_minutes() >= max_minutes;

    // Out of time.
    rule long_timed_out when exposure_is(Exposure.Open) && held > 0 && aged
        => order(Side.Sell, Intent.Close, OrderType.Market, Sizing.Fixed(size));
    rule short_timed_out when exposure_is(Exposure.Open) && held < 0 && aged
        => order(Side.Buy, Intent.Close, OrderType.Market, Sizing.Fixed(size));

    // The first target: half off, once.
    rule long_first when exposure_is(Exposure.Open) && held > 0 && whole && px >= entry + first_r * long_risk
        => order(Side.Sell, Intent.Reduce, OrderType.Market, Sizing.PercentOfPosition(percent(50)));
    rule short_first when exposure_is(Exposure.Open) && held < 0 && whole && px <= entry - first_r * short_risk
        => order(Side.Buy, Intent.Reduce, OrderType.Market, Sizing.PercentOfPosition(percent(50)));

    // What is left cannot lose: the stop to the entry price, once.
    rule long_safe when exposure_is(Exposure.Open) && held > 0 && !whole && features.stop_price() < entry
        => protect(Protect.StopLoss(entry));
    rule short_safe when exposure_is(Exposure.Open) && held < 0 && !whole && features.stop_price() > entry
        => protect(Protect.StopLoss(entry));

    // The entries, each with its stop beyond the bar's extreme and its target `final_r` risks away.
    rule fade_high when exposure_is(Exposure.Flat) && new_bar && failed_up && rested
        => order(Side.Sell, Intent.Open, OrderType.Market, Sizing.Fixed(size), Protect.Bracket(px - final_r * (short_stop - px), short_stop));
    rule fade_low when exposure_is(Exposure.Flat) && new_bar && failed_down && rested
        => order(Side.Buy, Intent.Open, OrderType.Market, Sizing.Fixed(size), Protect.Bracket(px + final_r * (px - long_stop), long_stop));
    flatten_on_stop;
}

fn main() -> Result<Unit, Error> {
    // The 8-hour range; the stop 5 dollars past the bar; half off at 1 R; the rest to 3 R or four hours; half an hour between trades.
    let run = trade.run(sweep_fade(480, 5.0, 1.0, 3.0, 240.0, 30.0, qty(0.02)))?;
    print(run.name + " on " + run.target + ": " + run.state);
    return Ok(());
}