본문으로 건너뛰기
← 모든 예제

예제 45

Orderflow strategy

an order-flow strategy, described in T0 and run by TTT's engine. It buys when sellers were absorbed -- heavy one-sided selling that did not

분류
Host: 터미널의 데이터와 엔진 필요
권한
market.readbacktest.run
실행하기 전에
at least one published BTCUSDT dataset on this machine (Datasets window)
하는 일
starts two backtest jobs (they appear in the Jobs panel); writes their runs under the terminal's runs folder.
실행 방법
grant market.read and backtest.run in the T0 Lang window, Run. "Stop job" stops only the backtest; "Stop program" stops both.
언어
>=0.1.1, <0.2.0
호스트
>=0.1.1, <0.2.0
필요한 것
market >=0.1.1, <0.2.0features >=0.1.0, <0.2.0backtest >=0.1.1, <0.2.0

소스

// Purpose: an order-flow strategy, described in T0 and run by TTT's engine. It buys when sellers were absorbed -- heavy one-sided selling that did not
// move price (`inferred_absorption_sell_side`: INFERRED from the tape, never observed) -- while price is under the session VWAP, and sells when price
// crosses back up through the VWAP. It is run twice on the newest published BTCUSDT dataset, with a loose and a strict notion of "heavy", and the two
// reports are printed side by side: the point of a backtest is the comparison, and a rule that only works at one threshold is a curve fit.
// `min_volume` is in units of the instrument, like an order's size: 1.0 and 5.0 below are one and five bitcoin sold into the bar.
// Category: host
// Permissions: market.read, backtest.run
// Preconditions: at least one published BTCUSDT dataset on this machine (Datasets window)
// Side effects: starts two backtest jobs (they appear in the Jobs panel); writes their runs under the terminal's runs folder.
// Run: grant market.read and backtest.run in the T0 Lang window, Run. "Stop job" stops only the backtest; "Stop program" stops both.

lang ">=0.1.1, <0.2.0";
host "ttt" ">=0.1.1, <0.2.0";
requires "market" ">=0.1.1, <0.2.0";
requires "features" ">=0.1.0, <0.2.0";
requires "backtest" ">=0.1.1, <0.2.0";

strategy vwap_absorption(min_volume: Decimal, imbalance_bps: Decimal, size: Quantity) version "1.0.0" {
    let px = features.price(features.Source.AggTrades);
    let vw = features.vwap(features.Source.AggTrades);
    let absorbed = features.inferred_absorption_sell_side(features.Source.AggTrades, min_volume, imbalance_bps);
    rule enter when absorbed && px < vw && exposure_is(Exposure.Flat)
        => order(Side.Buy, Intent.Open, OrderType.Market, Sizing.Fixed(size));
    rule exit when cross_above(px, vw) && exposure_is(Exposure.Open)
        => order(Side.Sell, Intent.Close, OrderType.Market, Sizing.Fixed(size));
    flatten_on_stop;
}

fn run(ds: market.Dataset, min_volume: Decimal, imbalance_bps: Decimal) -> Result<Unit, Error> {
    let job = backtest.submit(backtest.Spec {
        strategy: vwap_absorption(min_volume, imbalance_bps, qty(0.01)),
        dataset: ds,
        start: None,
        end: None,
        starting_balance: money(10000, USDT),
        execution: backtest.Execution {
            maker_fee: bps(2),
            taker_fee: bps(5),
            slippage: backtest.Slippage.Fraction(bps(1)),
            latency: 50ms,
            market_fill: backtest.MarketFill.NextPrint,
            limit_fill: backtest.LimitFill.TradeThrough,
        },
    })?;
    let report = backtest.wait(job)?;
    print("heavy from " + to_string(min_volume) + " at " + to_string(imbalance_bps) + " bps: " + to_string(report.status) + ", " + to_string(report.trades) + " trades, realized " + to_string(report.realized_pnl) + ", fees " + to_string(report.fees_paid));
    return Ok(());
}

fn main() -> Result<Unit, Error> {
    let ds = market.datasets("BTCUSDT")?.first().expect("no published BTCUSDT dataset on this machine");
    run(ds, 1.0, 6000.0)?;
    run(ds, 5.0, 8000.0)?;
    return Ok(());
}