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

예제 38

Iceberg

the iceberg: a band of the book that is hit and fills back up again and again. Follows the largest resting bid and the largest

분류
Workspace: 차트와 패널을 읽거나 변경
권한
market.readworkspace.readworkspace.write
실행하기 전에
a terminal connected to a data engine that builds the book of the chart's symbol
하는 일
DRAWS a level named "iceberg" on the active chart for a band refilled three times or more (run again: replaced).
실행 방법
grant market.read, workspace.read and workspace.write, Run.
언어
>=0.1.1, <0.2.0
호스트
>=0.1.7, <0.2.0
필요한 것
book >=0.1.0, <0.2.0workspace >=0.1.0, <0.2.0plot >=0.1.0, <0.2.0event >=0.1.0, <0.2.0

소스

// Purpose: the iceberg: a band of the book that is hit and fills back up again and again. Follows the largest resting bid and the largest
// resting ask over the last five minutes, reading by reading, and counts how many times each was cut to under a third and came back to over
// four fifths. **A refill is counted only between two readings that both exist**: a stretch the book was not recorded through is not a
// band being hit, and the count starts again after it.
// Category: workspace
// Permissions: market.read, workspace.read, workspace.write
// Preconditions: a terminal connected to a data engine that builds the book of the chart's symbol
// Side effects: DRAWS a level named "iceberg" on the active chart for a band refilled three times or more (run again: replaced).
// Run: grant market.read, workspace.read and workspace.write, Run.

lang ">=0.1.1, <0.2.0";
host "ttt" ">=0.1.7, <0.2.0";
requires "book" ">=0.1.0, <0.2.0";
requires "workspace" ">=0.1.0, <0.2.0";
requires "plot" ">=0.1.0, <0.2.0";
requires "event" ">=0.1.0, <0.2.0";

const OWNER: String = "iceberg";

// The size resting in the band at `at`, or None when the reading is unknown.
fn resting(side: List<book.Level>, known: Bool, at: Price) -> Option<Float> {
    if !known {
        return None;
    }
    let mut size: Float = 0.0;
    for level in side {
        if level.price == at {
            size = to_float(level.size.value());
        }
    }
    return Some(size);
}

// How many times the band at `at` was cut and came back, over the readings.
fn refills(columns: List<book.Snapshot>, bids: Bool, at: Price, full: Float) -> Int {
    let mut count = 0;
    let mut cut = false;
    let mut before: Option<Float> = None;
    for snapshot in columns {
        let side = if bids { snapshot.bids } else { snapshot.asks };
        let here = resting(side, snapshot.known, at);
        match here {
            Some(size) => {
                if size <= full * 0.33 {
                    cut = true;
                }
                if cut && size >= full * 0.8 {
                    match before {
                        Some(_) => {
                            count = count + 1;
                        }
                        None => {}
                    }
                    cut = false;
                }
                before = Some(size);
            }
            None => {
                // A reading is missing: what was happening across it is not known, so the sequence starts again.
                before = None;
                cut = false;
            }
        }
    }
    return count;
}

// The largest band of one side over the readings, with its size.
fn largest(columns: List<book.Snapshot>, bids: Bool) -> Option<book.Level> {
    let mut best: Option<book.Level> = None;
    let mut best_size: Float = 0.0;
    for snapshot in columns {
        if snapshot.known {
            let side = if bids { snapshot.bids } else { snapshot.asks };
            for level in side {
                let size = to_float(level.size.value());
                if size > best_size {
                    best_size = size;
                    best = Some(level);
                }
            }
        }
    }
    return best;
}

fn main() -> Result<Unit, Error> {
    let chart = workspace.active_chart().expect("no chart is open");
    let now = event.current().time;
    let history = book.history(chart.symbol, now - 5min, now, 5s, 30)?;
    plot.clear(chart.chart, OWNER)?;
    if history.missing > 0 {
        print(to_string(history.missing) + " of " + to_string(history.columns.len()) + " readings of the book were not recorded; a refill is never counted across them");
    }
    let mut found = 0;
    for bids in [true, false] {
        match largest(history.columns, bids) {
            Some(level) => {
                let full = to_float(level.size.value());
                let times = refills(history.columns, bids, level.price, full);
                let side = if bids { "bid" } else { "ask" };
                print(side + " band " + to_string(level.price) + " (" + to_string(full) + " at most): refilled " + to_string(times) + " time(s)");
                if times >= 3 {
                    plot.level(chart.chart, OWNER, level.price, "iceberg " + side + " x" + to_string(times), "purple")?;
                    found = found + 1;
                }
            }
            None => print("no verdict on the " + (if bids { "bid" } else { "ask" }) + " side: the book was not recorded in the last five minutes"),
        }
    }
    if found == 0 {
        print(chart.symbol + ": no iceberg");
    }
    return Ok(());
}