Triangular Cycle Scanner
Evaluates both directions of a three-pair cycle against live bid/ask and reports the edge net of a per-leg fee assumption, returning a multi-leg plan only when the net edge clears your floor.
Evaluates both directions of a three-pair cycle against live bid/ask and reports the edge net of a per-leg fee assumption, returning a multi-leg plan only when the net edge clears your floor.
Triangular arbitrage is a cost calculation, not a pattern. The gross spread almost always looks positive; the question is whether it survives three sets of taker fees plus the bid/ask spread you actually cross. Quote both sides and subtract nine tenths of the visible edge and that is roughly the shape of the problem. This package computes both directions of the cycle - USDT to BTC to ETH to USDT and USDT to ETH to BTC to USDT - because the profitable side flips, and most implementations only ever check one. It returns multi with an explicit leg list only when the net edge clears your floor. Crucially, it also states the atomicity limitation in the output: three sequential legs are not three atomic legs. WHEN TO USE THIS • An agent must decide whether a visible cross-rate gap is worth executing. • You want a cost-aware arb check that reports gross and net separately. • You need to show why "the spread was 0.3%" is not an arb, net of three fees. Not viable on venues where you are a taker for all three legs. Also not viable if any leg has thin depth: the quoted size will not fill at the quoted price. WHAT THIS PACKAGE DELIBERATELY DOES NOT CONTAIN This is a framework release, not the tuned production build. Specifically: • No order size. amount_usdt is absent - the package reports an edge, it does not size a position, and depth is not modelled. • No fee table per venue and tier. You supply one per-leg fee assumption. • No atomic execution. See the limits below; this is a real hazard, not a footnote. • No tuned spread floor or fee assumption. • No default thresholds in code. scripts/triangle_scan.py raises PARAM_INCOMPLETE rather than falling back to a number. A silent default would make you believe you are running a calibrated system when you are running someone else's parameters. Every decision boundary is an input. That is the whole contract. DECISION CONTRACT • action: Condition • multi: Net edge of the better direction clears min_spread; legs lists the three legs • flat: Neither direction clears the floor; gross_edge and net_edge say by how much Response carries direction, gross_edge, net_edge, fee_total and legs. net_edge already has the per-leg fee applied three times. OUTPUT SCHEMA { "action": "multi", "direction": "usdt-btc-eth-usdt", "gross_edge": 0.00512, "fee_total": 0.0036, "net_edge": 0.00152, "legs": [ {"symbol": "BTC/USDT", "side": "buy", "ratio": 1.0}, {"symbol": "ETH/BTC", "side": "buy", "ratio": 1.0}, {"symbol": "ETH/USDT", "side": "sell", "ratio": 1.0} ], "atomic": false, "reason": "net edge 0.152% clears the 0.100% floor" } action is the only field you must branch on. reason is meant for logs and for an agent to quote back to a human — do not parse it. RUN IT python3 scripts/triangle_scan.py \ --params references/params-example.json \ --symbol BTC/USDT \ --interval 1m references/params-example.json holds round, obviously-illustrative numbers. They exist so the command runs on the first try, not because they are good. Feeding position state in: python3 scripts/triangle_scan.py --params my.json --state '{"avg_entry": 61250.5, "layers": 2}' CALIBRATING THE THRESHOLDS 1. fee_rate - per leg, per side. Look up your actual tier; do not use a generic 0.1%. If you hold an exchange token or have a rebate, this single number decides whether the strategy exists at all. 2. min_spread - the net floor. It must exceed your execution shortfall: the gap between the quoted price and your realised fill on each leg. Measure that from your own fills, then set the floor well above it. 3. pairs - must form a closed cycle returning to the quote currency. Three pairs that do not close the loop produce a meaningless edge. Verify with a paper run of a few hundred scans: count how many opportunities the scanner reports and compare with how many your executor could actually have filled. The ratio is your real hit rate and it is usually far below one. KNOWN LIMITS • Three sequential legs are not atomic. A later leg can fail after an earlier one fills, leaving a directional position you did not intend. The output states atomic: false for exactly this reason; reconciliation is the executor's job. • Bid/ask quotes are snapshots. By the time leg two executes, the leg-three quote has usually moved. • Liquidity is not modelled. A 0.15% edge on a book that is 5,000 USDT deep is not an edge at size. • Cross-rate arithmetic assumes all three pairs trade on the same venue with the same fee tier and no withdrawal or transfer step between them. DISCLAIMER This is a decision framework for research and education. It is not investment advice, not a profit claim, and not a backtest result. Crypto derivatives can lose more than the deposit. You are responsible for the parameters you choose and for every order your executor places.
ipfs://bafkreidj64vujbi2kph2yascl5oclz53kpaniiq7q27tolnekxtib5akiu
The primary-document hash is the SHA-256 of the plaintext primary Markdown file. The content-commitment hash covers the verified SkillRoot structure, or plaintext ZIP for older Skills; it is not the hash of the downloaded file.
Opaque identifiers and fingerprints are hidden from the primary experience but remain available for exact verification.
0x180bbac541a1ad619c83f7d53fca00600d1554c429377563a6ced5dad3b94c3f0xa619ba461eb4bc6fd92273749f2cf95f81945b416faee0cac61cb8ce61eb3329