Ir al contenido
← Todos los ejemplos

Ejemplo 51

Intraday momentum

Bitcoin intraday time-series momentum, as published -- Shen, Urquhart and Wang, "Bitcoin intraday time-series momentum",

Categoría
Host: necesita los datos y el motor de la terminal
Permisos
orders.submit
Antes de ejecutarlo
BTCUSDT one-minute candles (klines), with at least a day and a half of them before the first trade
Qué hace
asks the terminal to run the strategy; once running it places orders on the account the person chose.
Cómo ejecutarlo
Insert strategy in the Datasets window (backtest over consecutive days), or Add script on a replay or the live market.
Lenguaje
>=0.1.2, <0.2.0
Host
>=0.1.10, <0.2.0
Necesita
features >=0.1.5, <0.2.0trade >=0.1.0, <0.2.0

Código

// Purpose: Bitcoin intraday time-series momentum, as published -- Shen, Urquhart and Wang, "Bitcoin intraday time-series momentum",
// Financial Review 57(2), 2022, pp. 319-344 (doi 10.1111/fire.12290); accepted version free at the University of Reading,
// https://centaur.reading.ac.uk/100181/ . Their combined timing rule, eq. (8).
//
// The paper's trading day, for BTC/USD, in US Eastern time (EST, taken here as UTC-5 all year): it opens at 9:00 EST, when the
// volume spikes (Bitstamp's open in their Table 1), and closes at 17:00 EST, when CME's Bitcoin futures stop for an hour. In UTC:
//
//   r_onfh  the "overnight + first half-hour" return: the price at 9:30 EST (14:30 UTC) over the previous day's close at 17:00 EST
//           (22:00 UTC the day before) -- eq. (1);
//   r_slh   the second-to-last half-hour: the price at 16:30 EST (21:30 UTC) over the one at 16:00 EST (21:00 UTC) -- eq. (2).
//
//   ENTRY   at 16:30 EST (21:30 UTC): LONG when r_onfh > 0 and r_slh < 0, SHORT when r_onfh <= 0 and r_slh >= 0, nothing otherwise.
//   EXIT    at the close, 17:00 EST (22:00 UTC). One trade a day at most, held half an hour.
//
// Every price is the close of a one-minute bar that has already closed (`close_ago`): at 21:30 UTC the last closed bar is 21:29's,
// 21:00's price is 30 bars back, 14:30's 420, and yesterday's 22:00 is 1410 -- nothing is read that a live market would not have
// had at that moment. The first day of a backtest has no previous close, and trades nothing.
//
// What the paper reports, before costs, 2013-2020, annualized: 16.7% return, Sharpe 1.72, 58% of trades right (Table 5). Its own
// break-even cost is 10 bps a trade (Table 8): at a taker fee of 5.5 bps a side the rule does not pay without leverage -- the
// authors say so. Results on later data are not theirs.
// Category: host
// Permissions: orders.submit
// Preconditions: BTCUSDT one-minute candles (klines), with at least a day and a half of them before the first trade
// Side effects: asks the terminal to run the strategy; once running it places orders on the account the person chose.
// Run: Insert strategy in the Datasets window (backtest over consecutive days), or Add script on a replay or the live market.

lang ">=0.1.2, <0.2.0";
host "ttt" ">=0.1.10, <0.2.0";
requires "features" ">=0.1.5, <0.2.0";
requires "trade" ">=0.1.0, <0.2.0";

strategy intraday_momentum(size: Quantity) version "1.0.0" {
    let minute = features.minute_of_day();

    // The prices the two returns are made of, as of the decision at 21:30 UTC.
    let now = features.close_ago(0);               // 21:30 UTC, 16:30 EST
    let half_hour_ago = features.close_ago(30);    // 21:00 UTC, 16:00 EST
    let first_half_hour = features.close_ago(420); // 14:30 UTC, 9:30 EST
    let last_close = features.close_ago(1410);     // 22:00 UTC the day before, 17:00 EST

    let r_onfh_up = first_half_hour > last_close;
    let r_slh_down = now < half_hour_ago;

    let decision = minute == 1290;                 // the minute that begins at 21:30 UTC
    let closing = minute >= 1320 || minute < 1290; // from 22:00 UTC on

    rule go_long when exposure_is(Exposure.Flat) && decision && r_onfh_up && r_slh_down
        => order(Side.Buy, Intent.Open, OrderType.Market, Sizing.Fixed(size));
    rule go_short when exposure_is(Exposure.Flat) && decision && !r_onfh_up && !r_slh_down
        => order(Side.Sell, Intent.Open, OrderType.Market, Sizing.Fixed(size));

    rule close_long when exposure_is(Exposure.Open) && features.position_sign() > 0 && closing
        => order(Side.Sell, Intent.Close, OrderType.Market, Sizing.Fixed(size));
    rule close_short when exposure_is(Exposure.Open) && features.position_sign() < 0 && closing
        => order(Side.Buy, Intent.Close, OrderType.Market, Sizing.Fixed(size));
    flatten_on_stop;
}

fn main() -> Result<Unit, Error> {
    let run = trade.run(intraday_momentum(qty(0.01)))?;
    print(run.name + " on " + run.target + ": " + run.state);
    return Ok(());
}