Skip to content
← All examples

Example 39

Gamma flip cross

the gamma flip: the strike where the options market's net gamma changes sign. Above it dealers damp moves, below it they feed

Category
Host: needs the terminal's data and engine
Permissions
market.readworkspace.readworkspace.write
Before you run it
a terminal connected to a backend that reads the options chain; a BTC or ETH chart
What it does
sounds the alert chime when the flip was crossed. Reads only.
How to run it
grant market.read, workspace.read and workspace.write, Run. Attach it to "Every N seconds" (300 s) to keep watch.
Language
>=0.1.1, <0.2.0
Host
>=0.1.7, <0.2.0
Needs
market >=0.1.3, <0.2.0options >=0.1.0, <0.2.0workspace >=0.1.0, <0.2.0alert >=0.1.0, <0.2.0event >=0.1.0, <0.2.0

Source

// Purpose: the gamma flip: the strike where the options market's net gamma changes sign. Above it dealers damp moves, below it they feed
// them. Reports which side of the flip the last close is on, how far, how much the market moved in the last hour -- and raises an alert when
// the close crossed it in the last fifteen minutes. **Needs the flip at both ends of the window**: when the options chain was not read at
// either (or has no flip), it says so and reports no crossing, because a flip carried over from an old chain is a level that is not there.
// Category: host
// Permissions: market.read, workspace.read, workspace.write
// Preconditions: a terminal connected to a backend that reads the options chain; a BTC or ETH chart
// Side effects: sounds the alert chime when the flip was crossed. Reads only.
// Run: grant market.read, workspace.read and workspace.write, Run. Attach it to "Every N seconds" (300 s) to keep watch.

lang ">=0.1.1, <0.2.0";
host "ttt" ">=0.1.7, <0.2.0";
requires "market" ">=0.1.3, <0.2.0";
requires "options" ">=0.1.0, <0.2.0";
requires "workspace" ">=0.1.0, <0.2.0";
requires "alert" ">=0.1.0, <0.2.0";
requires "event" ">=0.1.0, <0.2.0";
import std.text;
import std.ind;
import std.stats;

fn main() -> Result<Unit, Error> {
    let chart = workspace.active_chart().expect("no chart is open");
    let asset = if text.starts_with(chart.symbol, "ETH") { "ETH" } else { "BTC" };
    let now = event.current().time;
    let candles = market.chart_candles(chart.symbol, "5m", 13)?;
    if candles.len() < 4 {
        print("no reading: fewer than four closed five-minute bars");
        return Ok(());
    }
    let last = candles.last().expect("a bar");
    let before = candles.get(candles.len() - 4).expect("a bar");

    let gamma_now = options.gamma_at(asset, now)?;
    match gamma_now.unknown {
        Some(why) => {
            print("no reading: " + why);
            return Ok(());
        }
        None => {}
    }
    if gamma_now.flip.is_none() {
        print(asset + ": the chain has no flip -- net gamma does not change sign within the listed strikes");
        return Ok(());
    }
    let flip = gamma_now.flip.expect("checked above");
    // The flip is in USD (the options' strikes) and the candles in USDT: the language never converts between them, so the comparison is of
    // the numbers, which takes USDT as worth one USD. Said here because it is an assumption, and a depeg is when it fails.
    let level = flip.amount();
    let above = last.close.amount() > level;
    let distance = to_float(last.close.amount() / level - 1.0) * 100.0;
    print(chart.symbol + " closed at " + to_string(last.close) + ", " + to_string(distance) + " % " + (if above { "above" } else { "below" }) + " the flip at " + to_string(flip) + (if above { " (dealers damp moves)" } else { " (dealers feed moves)" }));

    // Realized volatility of the last twelve bars (or the bars there are), per bar.
    let mut closes: List<Float> = [];
    for c in candles {
        closes.push(to_float(c.close.amount()));
    }
    match stats.stdev(ind.known(ind.log_returns(closes))) {
        Some(vol) => print("realized volatility of the last " + to_string(candles.len()) + " bars: " + to_string(vol * 100.0) + " % per bar"),
        None => print("realized volatility: too few bars"),
    }

    // The flip fifteen minutes ago, from the chain the capture read then: unknown stays unknown.
    let gamma_then = options.gamma_at(asset, now - 15min)?;
    match gamma_then.unknown {
        Some(why) => {
            print("no crossing verdict: no chain fifteen minutes ago (" + why + ")");
            return Ok(());
        }
        None => {}
    }
    if gamma_then.flip.is_none() {
        print("no crossing verdict: the chain fifteen minutes ago had no flip");
        return Ok(());
    }
    let flip_then = gamma_then.flip.expect("checked above");
    let above_then = before.close.amount() > flip_then.amount();
    if above != above_then {
        alert.notify("Gamma flip crossed", chart.symbol + " went " + (if above { "above" } else { "below" }) + " the gamma flip (" + to_string(flip) + ")")?;
        print("CROSSED the flip in the last fifteen minutes: " + (if above_then { "above" } else { "below" }) + " -> " + (if above { "above" } else { "below" }));
    } else {
        print("no crossing in the last fifteen minutes");
    }
    return Ok(());
}