Deploy Hurst/VPIN directional strategy to live + paper

Live node:
  - Added Hurst VPIN to STRATEGIES (BTC, 0.00024 size, 00)
  - Feed BTC price into dollar-bar Hurst/VPIN every 5 ticks
  - Signal: BUY/SELL when H>0.55 + VPIN>0.25 + direction bias

Paper trader:
  - Added Kalman Pairs, Avellaneda-Stoikov, Hurst VPIN strategies
  - All 00 allocation, matching live node asset distribution
  - Hurst/VPIN signal from BTC mid-price dollar bars

Strategy file: hurst_vpin_live.py (lightweight price-tick mode)
This commit is contained in:
ramseshk
2026-08-06 06:51:51 +00:00
parent a8ed3cafe0
commit cf376f2995
3 changed files with 216 additions and 34 deletions
+154
View File
@@ -0,0 +1,154 @@
"""
Hurst/VPIN integration module — provides compact signal generators
for live trading, paper trading, and backtesting.
Live: feeds price tick stream into Hurst dollar bars.
Paper/Backtest: feeds real trade data.
"""
import math, time, numpy as np
from collections import deque
# ═══════════════════════════════════════════════════════════
# 1. Hurst Exponent — R/S on log returns
# ═══════════════════════════════════════════════════════════
def _hurst_rs(returns: list) -> float:
"""R/S estimate from log returns. Returns 0.200.80."""
n = len(returns)
if n < 32:
return 0.50
max_lag = min(n // 2, 64)
lags = []; rs = []
for lag in range(4, max_lag):
segs = n // lag
if segs < 2: continue
vals = []
for s in range(segs):
seg = returns[s*lag:(s+1)*lag]
mean = np.mean(seg)
dev = np.cumsum(seg - mean)
r = float(np.max(dev) - np.min(dev))
sd = float(np.std(seg, ddof=1))
if sd > 1e-12:
vals.append(r / sd)
if vals:
lags.append(np.log(lag))
rs.append(np.log(np.mean(vals)))
if len(lags) < 4:
return 0.50
slope = float(np.polyfit(lags, rs, 1)[0])
return max(0.20, min(0.80, slope))
# ═══════════════════════════════════════════════════════════
# 2. Dollar Bar Builder (notional-based)
# ═══════════════════════════════════════════════════════════
class DollarBar:
def __init__(self, threshold: float = 10000.0):
self.threshold = threshold
self.vol = 0.0
self.buy_vol = 0.0
self.sell_vol = 0.0
self.close = 0.0
def add(self, price: float, notional: float, is_buy: bool):
self.vol += notional
if is_buy:
self.buy_vol += notional
else:
self.sell_vol += notional
self.close = price
@property
def ready(self) -> bool:
return self.vol >= self.threshold
def emit(self) -> dict:
total = self.buy_vol + self.sell_vol
data = {
"close": self.close,
"vpin": abs(self.buy_vol - self.sell_vol) / total if total > 1 else 0.0,
"direction": (self.buy_vol - self.sell_vol) / total if total > 1 else 0.0,
}
self.vol = 0.0; self.buy_vol = 0.0; self.sell_vol = 0.0
return data
# ═══════════════════════════════════════════════════════════
# 3. Hurst/VPIN Signal (price-tick mode for live trading)
# ═══════════════════════════════════════════════════════════
class HurstVPINLive:
"""Lightweight Hurst/VPIN for live price tick stream.
Uses notional bars ($10K) from mid-price changes.
Each tick adds notional ≈ price * |Δprice| * 100 as volume proxy.
"""
def __init__(self, threshold: float = 10000.0,
hurst_window: int = 128,
vpin_window: int = 50,
hurst_entry: float = 0.55,
vpin_threshold: float = 0.25):
self.threshold = threshold
self.vpin_window = vpin_window
self.hurst_entry = hurst_entry
self.vpin_threshold = vpin_threshold
self.bar = DollarBar(threshold)
self.vpin_buf = deque(maxlen=vpin_window)
self.vpin_dir_buf = deque(maxlen=vpin_window)
self.returns = deque(maxlen=hurst_window)
self.last_close = 0.0
self.last_price = 0.0
def feed_price(self, price: float):
"""Feed a mid-price tick. Returns signal dict or None."""
if self.last_price <= 0:
self.last_price = price
return None
delta = price - self.last_price
is_buy = delta > 0
notional = price * abs(delta) * 100 # volume proxy
self.last_price = price
self.bar.add(price, notional, is_buy)
if not self.bar.ready:
return None
bar_data = self.bar.emit()
# VPIN
self.vpin_buf.append(bar_data["vpin"])
self.vpin_dir_buf.append(bar_data["direction"])
vpin = float(np.mean(self.vpin_buf)) if len(self.vpin_buf) >= self.vpin_window else 0.0
direction = float(np.mean(self.vpin_dir_buf)) if len(self.vpin_dir_buf) >= self.vpin_window else 0.0
# Hurst
if self.last_close > 0:
self.returns.append(math.log(bar_data["close"] / self.last_close))
self.last_close = bar_data["close"]
hurst = _hurst_rs(list(self.returns)) if len(self.returns) >= 64 else 0.50
# Signal
trending = hurst >= self.hurst_entry
high_vpin = vpin >= self.vpin_threshold
if trending and high_vpin:
if direction > 0.02:
return {"signal": "BUY", "hurst": round(hurst, 3), "vpin": round(vpin, 3), "direction": round(direction, 3)}
elif direction < -0.02:
return {"signal": "SELL", "hurst": round(hurst, 3), "vpin": round(vpin, 3), "direction": round(direction, 3)}
return None
# ═══════════════════════════════════════════════════════════
# 4. Hurst/VPIN for backtest (full trade data)
# ═══════════════════════════════════════════════════════════
from strategies.hurst_vpin import run_hurst_vpin, HurstVPINSignal
# Expose for easy import
def hurst_vpin_backtest(trades, capital=100.0, size=0.00024):
return run_hurst_vpin(trades, starting_capital=capital, size=size)