Skip to content
bmosha00Public

About

Volatility-adjusted Bitcoin position sizing with a pure, replayable risk engine. Vol-targeting under liquidity and Basel caps, asymmetric rebalancing bands, and L2 depth collection. Compute only.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

btcvol

Volatility-adjusted Bitcoin position sizing and rebalancing.

Phase 1: compute only. The engine produces a daily target exposure and a binding-constraint tag. There is no execution code in this repository and none should be added before the roadmap's Phase 4.

E_target = max(0, min( RB/(z·σ_h)·D , E_liq , E_reg ))

Risk budget over risk-per-unit, capped by liquidity and by regulation. Everything else is a modifier.


Quickstart

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

python tests/test_engine.py     # 29 checks — sizing, vol, bands
python tests/test_book.py       # 16 checks — normalisation, impact
python tools/check_purity.py    # engine/ must stay side-effect free

Start the recorder before anything else. Depth snapshots, your own fills and custody test results cannot be backfilled or purchased; the historical klines can wait.

# Verify the endpoint reaches you at all — see "Geo-blocking" below
curl -s -o /dev/null -w "%{http_code}\n" \
  "https://dapi.binance.com/dapi/v1/depth?symbol=BTCUSD_PERP&limit=5"

python -m recorders.depth --venue binance_cm --symbol BTCUSD_PERP \
       --limit 20 --interval 10 --root data

python -m backfill.binance_bulk --kind klines  --market cm --interval 1d --start 2019-01-01
python -m backfill.binance_bulk --kind funding --market cm --start 2019-01-01

Deploy the recorder with deploy/btcvol-depth.service, not tmux.


Layout

engine/           PURE — no I/O, no clock, no randomness. CI-enforced.
  sizing.py       E_target + binding-constraint tag + input hash
  volatility.py   sigma = max(EWMA, implied*haircut)  |  z as a SEPARATE output
  rebalance.py    asymmetric bands, cost gate, contract rounding
  book.py         contract->USD/BTC normalisation, depth-to-impact, E_liq
recorders/
  depth.py        L2 snapshots -> JSONL, gzipped at UTC rollover
  journal.py      fills, custody tests, hash-chained audit log
backfill/
  binance_bulk.py bulk CSV archives (not REST pagination)
tools/
  check_purity.py CI guard on the replay-harness assumption
deploy/           systemd unit

The traps this encodes

Each is a test, not a comment. They exist because each one silently produced a plausible-looking wrong number first.

Guard What it prevents
sigma_daily > 0 enforced dividing by a z-score — undefined at z=0, sign-flips below
sigma_daily <= 0.25 an annualised figure passed as daily (~478% ann.)
e_liq is a min() term liquidity as a divisor, where a deeper book shrinks the position
z is a separate return field keeps the trigger permanently out of the denominator
normalize() refuses unknown specs 642x depth overstatement — see below
sweep_cost_bps returns filled extrapolating past the last visible level
round_to_contract truncates a sub-contract hedge silently rounding to a real order
day-rollover gzip multi-member gzip, ~2x storage penalty on append

The 642x one is worth reading twice

Binance coin-margined quantities are in contracts, not BTC. One BTCUSD_PERP contract is $100 notional. From a live snapshot at mid $64,178.9, a top bid of 9922:

correct : 9,922 contracts = $992,200   = 15.46 BTC
naive   : 9,922 BTC                    = $636,742,646
error   : 642x   (the factor is mid/100)

At that magnitude E_liq never binds, which silently disables the one cap that stops you holding more than you can exit. normalize() raises rather than defaulting to a unit for any venue/symbol it has no spec for.

Spread is not liquidity

The same snapshot shows a 0.016 bps spread — and 96.5% of visible bid depth sitting at the touch. Level 2 is $14,500. A $2M sell exhausts the book. E_liq must come from depth-to-impact against recorded history at a stressed percentile, never from spread and never from a single live read.


Geo-blocking

Binance returns HTTP 451 to US IPs, and Binance.US is a separate entity with no coin-margined perpetuals. The recorder host must be in a non-US region, and provider IP ranges get flagged independently of geography — so run the curl check on the box, not just on your laptop. A Frankfurt VPS is not automatically a working VPS.

000 from curl means no HTTP response at all: usually a VPN kill switch or stale DNS, not a block.


Storage

Measured, not estimated — 20 levels, 10s interval, one venue:

Item Size
Depth archive, gzipped 840 MB/year
Working file (today, uncompressed) 9.6 MB peak
Klines 1d + funding, 7-year backfill ~1 MB one-off
SQLite journal ~12 MB/year
Year 1 total 0.86 GB

Over-collecting is the real cost: 100 levels -> 3.9 GB/yr, 1000 levels -> 41.7 GB/yr, 1000 levels @ 1s -> 417 GB/yr. Stay at 20. Rate-limit weight at 10s is 12/min against a 2,400 budget, so there is no throughput argument for going faster either.

Data is gitignored. It belongs in object storage with a daily sync, not in this repository.


Security posture

This repository needs no credentials. Depth, klines and funding are all public unauthenticated endpoints. The collector box holds no secrets and should stay that way permanently — when execution is eventually added, it runs elsewhere. If .env ever has something in it, ask why first.


Not built yet (deliberate)

  • Margin & funding module with symmetric +/- shocks. A short hedge against custodied spot bleeds cash on a rally while the offsetting gain sits behind a multi-day withdrawal path. Downside-only stress misses it.
  • Jurisdiction rule-versioning for the regulatory cap — the Basel cryptoasset standard is under review and national timelines diverge, so the cap needs versioned rules with effective dates, not a config constant.
  • Incremental-VaR challenger to replace the floored diversification multiplier once a portfolio factor feed exists.
  • Impact model calibration — blocked on the depth and fill history the recorders are gathering now. That dependency is the whole reason they run first.

About

Volatility-adjusted Bitcoin position sizing with a pure, replayable risk engine. Vol-targeting under liquidity and Basel caps, asymmetric rebalancing bands, and L2 depth collection. Compute only.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages