auto: hourly snapshot 2026-06-08 06:34
This commit is contained in:
+203
-95
@@ -83,39 +83,36 @@ DEFAULT_DAYS_GLOB = "20260415*"
|
||||
# fixed physical scales — never refit, never z-score
|
||||
# (the whole point of this injector)
|
||||
MAP_SCALE = {
|
||||
# funding_rate from HL is per-hour. Annualised bps = rate * 8760 * 10000.
|
||||
# Calibrated 2026-06-07 on 720 hourly rows from April BTC HL fundingHistory.
|
||||
# Real distribution: median |val|=620bps, q90=1265bps, q99=2320bps, max=3835bps.
|
||||
# 3000bps annualised → 1.0 dipole-leg strength.
|
||||
# Second rebalance 2026-06-07: previous scale (1000bps/strength, cap 2.5)
|
||||
# left funding firing as a near-constant DC offset at strength ~1.3 every
|
||||
# minute, dominating the stress tensor. Tripling the scale shrinks each
|
||||
# dipole leg to ~0.4 and lets minute-to-minute variance show through.
|
||||
"funding_bps_per_strength": 3000.0,
|
||||
"funding_strength_cap": 2.5,
|
||||
# ---- LEVELS (4-hour rolling means) ----
|
||||
# Calibrated 2026-06-08 on full April BTC distributions:
|
||||
# per_strength = |val| q90 -> strength 1.0 (so q99 -> ~1.7-2.0, cap takes it).
|
||||
# Mean-reverting tensions (bs_ratio, activity, cvd) compress heavily under
|
||||
# the 4hr mean (10-20× vs per-minute std). Persistent ones (funding) compress
|
||||
# only ~1.5×. Scales below reflect the post-aggregation distributions.
|
||||
"funding_level_per_strength": 1180.0,
|
||||
"funding_level_strength_cap": 2.1,
|
||||
|
||||
# bs_ratio_signed ranges natively ±0.5 (since ratio is 0..1, minus 0.5).
|
||||
# Scale ×2 so full ±0.5 → ±1.0 strength.
|
||||
# Cap ±3.0 (rebalance 2026-06-07): single-blob encoding was mute (ρ<0.15)
|
||||
# because a single density blob doesn't couple to global telemetry channels.
|
||||
# Second rebalance 2026-06-07: bs_ratio is now a Y-AXIS DIPOLE (mechanically
|
||||
# equivalent to funding's x-axis dipole, rotated 90°). Couples cleanly to
|
||||
# stress_yy and vel_mean on equal footing with funding.
|
||||
"bs_ratio_per_strength": 0.5,
|
||||
"bs_ratio_strength_cap": 3.0,
|
||||
"bs_ratio_level_per_strength": 0.060,
|
||||
"bs_ratio_level_strength_cap": 1.8,
|
||||
|
||||
# activity_excess = (trade_count / rolling_500min_mean) - 1.
|
||||
# Centred at 0. -1 = zero activity. +2 = 3× baseline. +5 = 6× baseline.
|
||||
# Scale: 3.0 of excess → 1.0 strength. Cap at 5.
|
||||
"activity_per_strength": 3.0,
|
||||
"activity_strength_cap": 5.0,
|
||||
"activity_level_per_strength": 0.58,
|
||||
"activity_level_strength_cap": 2.2,
|
||||
|
||||
# cvd_divergence = sum(signed_flow_usd, 60min) / (|price_change_60min_bps| + eps)
|
||||
# Units: USD per bps. April BTC: median |val|=$566k, 90th=$2.5M, 99th=$13M, max=$100M.
|
||||
# Tanh scale 2e6 keeps the bulk (q50 strength 0.28, q90 0.85) in the responsive
|
||||
# part of the curve, while ~8% of extreme minutes saturate near ±1.0.
|
||||
# Calibrated 2026-06-07 on 43k April minutes.
|
||||
"cvd_div_tanh_scale": 2e6,
|
||||
# cvd level: tanh keeps it bounded; scale = q90 → q90 maps to tanh(1)=0.76.
|
||||
"cvd_level_tanh_scale": 1.24e6,
|
||||
|
||||
# ---- VELOCITIES (4hr level diff) ----
|
||||
# Calibrated same way: per_strength = |vel| q90.
|
||||
"funding_vel_per_strength": 900.0,
|
||||
"funding_vel_strength_cap": 1.8,
|
||||
|
||||
"bs_ratio_vel_per_strength": 0.078,
|
||||
"bs_ratio_vel_strength_cap": 1.7,
|
||||
|
||||
"activity_vel_per_strength": 0.87,
|
||||
"activity_vel_strength_cap": 1.8,
|
||||
|
||||
"cvd_vel_tanh_scale": 1.78e6,
|
||||
}
|
||||
|
||||
# ROLLING WINDOWS (computed per source minute, past-only by shift)
|
||||
@@ -123,6 +120,14 @@ ROLL_ACTIVITY_WIN = 500 # 500-min rolling baseline for activity_excess
|
||||
ROLL_CVD_WIN = 60 # 60-min summed CVD and price change
|
||||
EPS_BPS = 0.5 # epsilon to prevent /0 in cvd_div denominator
|
||||
|
||||
# 4-hour aggregation window for LEVELS and VELOCITIES.
|
||||
# Levels = rolling mean over last 4hr (past-only shift).
|
||||
# Velocities = level(t) - level(t - LEVEL_WIN_MIN). The signal we found dominant
|
||||
# was asymmetry_d240 (β=+4.77). Pre-aggregating to the same timescale tests
|
||||
# whether the lattice's value is temporal smoothing (4hr-input matches it) or
|
||||
# non-linear coupling (4hr-input fails, only minute-level + lattice memory works).
|
||||
LEVEL_WIN_MIN = 240
|
||||
|
||||
# Lattice spatial geometry (lattice is 1024×1024, center 512,512)
|
||||
CENTER = (512.0, 512.0)
|
||||
DIPOLE_OFF = 64.0 # dipole leg offset from center (funding x, bs_ratio y)
|
||||
@@ -134,6 +139,16 @@ SIGMA = 32.0 # gaussian sigma of each injection blob
|
||||
# 4 legs is the minimum for a clean dipole-quadrupole rotation. 6 is smoother.
|
||||
VORTICITY_N_LEGS = 6
|
||||
|
||||
# --- VELOCITY (Δlevel over 4hr) spatial geometry — distinct from levels ---
|
||||
# Levels and velocities of the SAME tension must occupy distinct spatial
|
||||
# signatures so the lattice can't conflate them. Velocities use a different
|
||||
# radius / scale than their level counterparts.
|
||||
DIPOLE_OFF_VEL = 192.0 # funding/bs_ratio velocity dipoles wider than level dipoles
|
||||
SIGMA_VEL = 16.0 # tighter sigma for velocity injections (sharper, less DC)
|
||||
RING_R_ACT_VEL = 320.0 # activity-velocity annular ring (same-sign legs = pure breathing)
|
||||
RING_R_CVD_VEL = 96.0 # cvd-velocity counter-rotating ring (half the cvd-level radius)
|
||||
ACT_VEL_N_LEGS = 4 # activity-velocity ring leg count
|
||||
|
||||
# Timing
|
||||
PER_MINUTE_MS = 100 # 10× realtime (proven in throughput probe)
|
||||
WAIT_AFTER_INJECT_MS = 80 # extra room for the 4 force-patterns to settle
|
||||
@@ -268,11 +283,17 @@ def fetch_funding_history(coin: str, start_min: int, end_min: int,
|
||||
|
||||
|
||||
def compute_tensions(df: pd.DataFrame, funding: pd.DataFrame) -> pd.DataFrame:
|
||||
"""Per minute, compute the four native tensions in physical units."""
|
||||
"""Per minute, compute the four native tensions AND their 4-hour level
|
||||
aggregates plus 4-hour velocities. Eight tensions total.
|
||||
|
||||
Naming:
|
||||
<name> = per-minute native tension (kept for reference & analyzer)
|
||||
<name>_level = 4-hour rolling mean (past-only via shift(1))
|
||||
<name>_vel = level(t) - level(t - LEVEL_WIN_MIN)
|
||||
"""
|
||||
out = df.copy()
|
||||
|
||||
# --- funding: forward-fill the hourly value onto each minute ---
|
||||
# join via the floor-of-hour for each minute
|
||||
out["minute_hour_floor"] = (out["minute"] // 60) * 60
|
||||
out = out.merge(funding.rename(columns={"minute_hour": "minute_hour_floor"}),
|
||||
on="minute_hour_floor", how="left")
|
||||
@@ -283,23 +304,29 @@ def compute_tensions(df: pd.DataFrame, funding: pd.DataFrame) -> pd.DataFrame:
|
||||
out["bs_ratio_signed"] = (out["taker_buy_usd"] / denom) - 0.5 # -0.5..+0.5
|
||||
|
||||
# --- activity_excess: trade_count / rolling-500min-mean - 1 ---
|
||||
# past-only via shift(1)
|
||||
rolling_mean = out["trade_count"].astype(float).shift(1).rolling(
|
||||
window=ROLL_ACTIVITY_WIN, min_periods=50
|
||||
).mean()
|
||||
out["activity_excess"] = (out["trade_count"].astype(float) / rolling_mean) - 1.0
|
||||
|
||||
# --- cvd_divergence: 60min summed signed_flow / |60min bps price change| ---
|
||||
# past-only: window ends at minute t-1, then we ascribe to minute t.
|
||||
sflow = out["signed_flow_usd"].astype(float)
|
||||
cvd_60 = sflow.shift(1).rolling(window=ROLL_CVD_WIN, min_periods=10).sum()
|
||||
px = out["mid_price"].astype(float)
|
||||
# |price_change_60min| in bps, looking back from t-1 -> t-1-60
|
||||
px_now = px.shift(1)
|
||||
px_then = px.shift(1 + ROLL_CVD_WIN)
|
||||
pct_chg_bps = ((px_now - px_then) / px_then * 10000.0).abs()
|
||||
out["cvd_divergence"] = cvd_60 / (pct_chg_bps + EPS_BPS)
|
||||
|
||||
# --- 4-HOUR LEVELS (rolling means, past-only via shift(1)) ---
|
||||
for col in ["funding_bps", "bs_ratio_signed", "activity_excess", "cvd_divergence"]:
|
||||
out[f"{col}_level"] = (out[col].astype(float).shift(1)
|
||||
.rolling(LEVEL_WIN_MIN, min_periods=60).mean())
|
||||
|
||||
# --- 4-HOUR VELOCITIES (level(t) - level(t - LEVEL_WIN_MIN)) ---
|
||||
for col in ["funding_bps", "bs_ratio_signed", "activity_excess", "cvd_divergence"]:
|
||||
out[f"{col}_vel"] = out[f"{col}_level"] - out[f"{col}_level"].shift(LEVEL_WIN_MIN)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@@ -313,70 +340,115 @@ def _send_inject(pub: zmq.Socket, x: float, y: float,
|
||||
pub.send_string(json.dumps(payload))
|
||||
|
||||
|
||||
def encode_funding_dipole(pub: zmq.Socket, funding_bps: float) -> None:
|
||||
"""Funding → x-axis dipole. Positive funding (longs paying) pushes +x,
|
||||
negative funding (shorts paying) pushes -x."""
|
||||
if not np.isfinite(funding_bps):
|
||||
return
|
||||
strength = funding_bps / MAP_SCALE["funding_bps_per_strength"]
|
||||
strength = float(np.clip(strength, -MAP_SCALE["funding_strength_cap"],
|
||||
+MAP_SCALE["funding_strength_cap"]))
|
||||
if abs(strength) < 1e-6:
|
||||
return
|
||||
# ---- LEVEL ENCODERS (re-use original geometries, fed with 4hr-mean values) ----
|
||||
def encode_funding_level(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr-mean funding → x-axis dipole at ±DIPOLE_OFF."""
|
||||
if not np.isfinite(val): return
|
||||
s = float(np.clip(val / MAP_SCALE["funding_level_per_strength"],
|
||||
-MAP_SCALE["funding_level_strength_cap"],
|
||||
+MAP_SCALE["funding_level_strength_cap"]))
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
_send_inject(pub, cx - DIPOLE_OFF, cy, -strength)
|
||||
_send_inject(pub, cx + DIPOLE_OFF, cy, +strength)
|
||||
_send_inject(pub, cx - DIPOLE_OFF, cy, -s)
|
||||
_send_inject(pub, cx + DIPOLE_OFF, cy, +s)
|
||||
|
||||
|
||||
def encode_bsratio_velocity(pub: zmq.Socket, bs_ratio_signed: float) -> None:
|
||||
"""bs_ratio_signed → Y-AXIS dipole (mechanically equivalent to funding's
|
||||
x-axis dipole, rotated 90°). Positive = buy pressure pushes +y leg positive,
|
||||
-y leg negative. Couples to global stress_yy / vel_mean.
|
||||
Second rebalance 2026-06-07: was a single offset blob that didn't couple
|
||||
to any global telemetry channel."""
|
||||
if not np.isfinite(bs_ratio_signed):
|
||||
return
|
||||
strength = bs_ratio_signed / MAP_SCALE["bs_ratio_per_strength"]
|
||||
strength = float(np.clip(strength, -MAP_SCALE["bs_ratio_strength_cap"],
|
||||
+MAP_SCALE["bs_ratio_strength_cap"]))
|
||||
if abs(strength) < 1e-6:
|
||||
return
|
||||
def encode_bsratio_level(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr-mean bs_ratio → y-axis dipole at ±DIPOLE_OFF."""
|
||||
if not np.isfinite(val): return
|
||||
s = float(np.clip(val / MAP_SCALE["bs_ratio_level_per_strength"],
|
||||
-MAP_SCALE["bs_ratio_level_strength_cap"],
|
||||
+MAP_SCALE["bs_ratio_level_strength_cap"]))
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
_send_inject(pub, cx, cy - DIPOLE_OFF, -strength)
|
||||
_send_inject(pub, cx, cy + DIPOLE_OFF, +strength)
|
||||
_send_inject(pub, cx, cy - DIPOLE_OFF, -s)
|
||||
_send_inject(pub, cx, cy + DIPOLE_OFF, +s)
|
||||
|
||||
|
||||
def encode_activity_isotropic(pub: zmq.Socket, activity_excess: float) -> None:
|
||||
"""activity_excess → isotropic center blob. Excess is always relative to
|
||||
own baseline so -1 (zero activity) is a meaningful 'cold' state. We allow
|
||||
NEGATIVE strength too — quiet minutes pull energy out of the field."""
|
||||
if not np.isfinite(activity_excess):
|
||||
return
|
||||
strength = activity_excess / MAP_SCALE["activity_per_strength"]
|
||||
strength = float(np.clip(strength, -MAP_SCALE["activity_strength_cap"] / 3.0,
|
||||
+MAP_SCALE["activity_strength_cap"]))
|
||||
if abs(strength) < 1e-6:
|
||||
return
|
||||
def encode_activity_level(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr-mean activity → isotropic center blob."""
|
||||
if not np.isfinite(val): return
|
||||
s = float(np.clip(val / MAP_SCALE["activity_level_per_strength"],
|
||||
-MAP_SCALE["activity_level_strength_cap"],
|
||||
+MAP_SCALE["activity_level_strength_cap"]))
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
_send_inject(pub, cx, cy, strength)
|
||||
_send_inject(pub, cx, cy, s)
|
||||
|
||||
|
||||
def encode_cvd_vorticity(pub: zmq.Socket, cvd_divergence: float) -> None:
|
||||
"""cvd_divergence → vorticity ring around center. N legs alternating signs.
|
||||
Direction of rotation = sign of divergence. Tanh keeps magnitude bounded."""
|
||||
if not np.isfinite(cvd_divergence):
|
||||
return
|
||||
strength = math.tanh(cvd_divergence / MAP_SCALE["cvd_div_tanh_scale"])
|
||||
if abs(strength) < 1e-6:
|
||||
return
|
||||
def encode_cvd_level(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr-mean cvd_div → 6-leg alternating-sign ring at R=192."""
|
||||
if not np.isfinite(val): return
|
||||
s = math.tanh(val / MAP_SCALE["cvd_level_tanh_scale"])
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
sigma = SIGMA * 0.75 # tighter sigma so legs don't overlap excessively
|
||||
sigma = SIGMA * 0.75
|
||||
for k in range(VORTICITY_N_LEGS):
|
||||
theta = 2 * math.pi * k / VORTICITY_N_LEGS
|
||||
x = cx + RING_R * math.cos(theta)
|
||||
y = cy + RING_R * math.sin(theta)
|
||||
s = strength if (k % 2 == 0) else -strength
|
||||
_send_inject(pub, x, y, s, sigma)
|
||||
sign = s if (k % 2 == 0) else -s
|
||||
_send_inject(pub, x, y, sign, sigma)
|
||||
|
||||
|
||||
# ---- VELOCITY ENCODERS (new, geometrically distinct from level encoders) ----
|
||||
def encode_funding_vel(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr funding velocity → x-axis dipole at ±DIPOLE_OFF_VEL=192, sigma=16.
|
||||
Wider separation and tighter sigma than the level dipole → distinct signature."""
|
||||
if not np.isfinite(val): return
|
||||
s = float(np.clip(val / MAP_SCALE["funding_vel_per_strength"],
|
||||
-MAP_SCALE["funding_vel_strength_cap"],
|
||||
+MAP_SCALE["funding_vel_strength_cap"]))
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
_send_inject(pub, cx - DIPOLE_OFF_VEL, cy, -s, SIGMA_VEL)
|
||||
_send_inject(pub, cx + DIPOLE_OFF_VEL, cy, +s, SIGMA_VEL)
|
||||
|
||||
|
||||
def encode_bsratio_vel(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr bs_ratio velocity → y-axis dipole at ±DIPOLE_OFF_VEL=192, sigma=16."""
|
||||
if not np.isfinite(val): return
|
||||
s = float(np.clip(val / MAP_SCALE["bs_ratio_vel_per_strength"],
|
||||
-MAP_SCALE["bs_ratio_vel_strength_cap"],
|
||||
+MAP_SCALE["bs_ratio_vel_strength_cap"]))
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
_send_inject(pub, cx, cy - DIPOLE_OFF_VEL, -s, SIGMA_VEL)
|
||||
_send_inject(pub, cx, cy + DIPOLE_OFF_VEL, +s, SIGMA_VEL)
|
||||
|
||||
|
||||
def encode_activity_vel(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr activity velocity → 4-leg same-sign annular ring at R=320.
|
||||
Same-sign legs = pure 'breathing' mode (expansion vs contraction),
|
||||
geometrically distinct from any dipole or alternating ring."""
|
||||
if not np.isfinite(val): return
|
||||
s = float(np.clip(val / MAP_SCALE["activity_vel_per_strength"],
|
||||
-MAP_SCALE["activity_vel_strength_cap"],
|
||||
+MAP_SCALE["activity_vel_strength_cap"]))
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
for k in range(ACT_VEL_N_LEGS):
|
||||
theta = 2 * math.pi * k / ACT_VEL_N_LEGS
|
||||
x = cx + RING_R_ACT_VEL * math.cos(theta)
|
||||
y = cy + RING_R_ACT_VEL * math.sin(theta)
|
||||
_send_inject(pub, x, y, s, SIGMA_VEL)
|
||||
|
||||
|
||||
def encode_cvd_vel(pub: zmq.Socket, val: float) -> None:
|
||||
"""4hr cvd_div velocity → 6-leg alternating-sign ring at R=96.
|
||||
Half the level-ring radius → inner counter-rotating vorticity. Sign-flipped
|
||||
relative to level so rotation opposes the level ring's curl when both fire.
|
||||
"""
|
||||
if not np.isfinite(val): return
|
||||
s = math.tanh(val / MAP_SCALE["cvd_vel_tanh_scale"])
|
||||
if abs(s) < 1e-6: return
|
||||
cx, cy = CENTER
|
||||
for k in range(VORTICITY_N_LEGS):
|
||||
theta = 2 * math.pi * k / VORTICITY_N_LEGS + (math.pi / VORTICITY_N_LEGS) # rotated 30°
|
||||
x = cx + RING_R_CVD_VEL * math.cos(theta)
|
||||
y = cy + RING_R_CVD_VEL * math.sin(theta)
|
||||
sign = -s if (k % 2 == 0) else s # opposite rotation to level ring
|
||||
_send_inject(pub, x, y, sign, SIGMA_VEL)
|
||||
|
||||
|
||||
# ───────────────────────── per-arm runner ─────────────────────────
|
||||
@@ -388,10 +460,21 @@ def snapshot_row(tel: LatestTel, minute: int, arm: str,
|
||||
"arm": arm,
|
||||
"snap_wall": snap_wall,
|
||||
"snap_age_ms": (time.time() - snap_wall) * 1000 if snap_wall else None,
|
||||
# per-minute (kept for reference / analyzer continuity)
|
||||
"funding_bps": tensions.get("funding_bps"),
|
||||
"bs_ratio_signed": tensions.get("bs_ratio_signed"),
|
||||
"activity_excess": tensions.get("activity_excess"),
|
||||
"cvd_divergence": tensions.get("cvd_divergence"),
|
||||
# 4-hour levels (what arm T actually injects)
|
||||
"funding_bps_level": tensions.get("funding_bps_level"),
|
||||
"bs_ratio_signed_level": tensions.get("bs_ratio_signed_level"),
|
||||
"activity_excess_level": tensions.get("activity_excess_level"),
|
||||
"cvd_divergence_level": tensions.get("cvd_divergence_level"),
|
||||
# 4-hour velocities (what arm T actually injects)
|
||||
"funding_bps_vel": tensions.get("funding_bps_vel"),
|
||||
"bs_ratio_signed_vel": tensions.get("bs_ratio_signed_vel"),
|
||||
"activity_excess_vel": tensions.get("activity_excess_vel"),
|
||||
"cvd_divergence_vel": tensions.get("cvd_divergence_vel"),
|
||||
}
|
||||
if snap is None:
|
||||
for ch in CHANNELS:
|
||||
@@ -408,7 +491,7 @@ def run_arm(tel: LatestTel, pub: zmq.Socket | None, arm: str,
|
||||
if arm == "A":
|
||||
log(" arm A: NO INJECTIONS — sampling state at cadence only")
|
||||
elif arm == "T":
|
||||
log(" arm T: 4 NATIVE TENSION INJECTIONS per minute")
|
||||
log(" arm T: 8 TENSION INJECTIONS per minute (4 levels + 4 velocities, 4hr window)")
|
||||
|
||||
records: list[dict] = []
|
||||
t_arm_start = time.time()
|
||||
@@ -421,17 +504,34 @@ def run_arm(tel: LatestTel, pub: zmq.Socket | None, arm: str,
|
||||
t_tick = time.time()
|
||||
row = df.iloc[i]
|
||||
tensions = {
|
||||
# per-minute (reference)
|
||||
"funding_bps": row.get("funding_bps"),
|
||||
"bs_ratio_signed": row.get("bs_ratio_signed"),
|
||||
"activity_excess": row.get("activity_excess"),
|
||||
"cvd_divergence": row.get("cvd_divergence"),
|
||||
# 4-hour levels (injected)
|
||||
"funding_bps_level": row.get("funding_bps_level"),
|
||||
"bs_ratio_signed_level": row.get("bs_ratio_signed_level"),
|
||||
"activity_excess_level": row.get("activity_excess_level"),
|
||||
"cvd_divergence_level": row.get("cvd_divergence_level"),
|
||||
# 4-hour velocities (injected)
|
||||
"funding_bps_vel": row.get("funding_bps_vel"),
|
||||
"bs_ratio_signed_vel": row.get("bs_ratio_signed_vel"),
|
||||
"activity_excess_vel": row.get("activity_excess_vel"),
|
||||
"cvd_divergence_vel": row.get("cvd_divergence_vel"),
|
||||
}
|
||||
|
||||
if arm == "T" and pub is not None:
|
||||
encode_funding_dipole (pub, tensions["funding_bps"])
|
||||
encode_bsratio_velocity (pub, tensions["bs_ratio_signed"])
|
||||
encode_activity_isotropic(pub, tensions["activity_excess"])
|
||||
encode_cvd_vorticity (pub, tensions["cvd_divergence"])
|
||||
# 4 LEVELS — existing spatial geometries, fed with 4hr means
|
||||
encode_funding_level (pub, tensions["funding_bps_level"])
|
||||
encode_bsratio_level (pub, tensions["bs_ratio_signed_level"])
|
||||
encode_activity_level(pub, tensions["activity_excess_level"])
|
||||
encode_cvd_level (pub, tensions["cvd_divergence_level"])
|
||||
# 4 VELOCITIES — new geometries, fed with 4hr deltas
|
||||
encode_funding_vel (pub, tensions["funding_bps_vel"])
|
||||
encode_bsratio_vel (pub, tensions["bs_ratio_signed_vel"])
|
||||
encode_activity_vel(pub, tensions["activity_excess_vel"])
|
||||
encode_cvd_vel (pub, tensions["cvd_divergence_vel"])
|
||||
time.sleep(wait_inject_s)
|
||||
else:
|
||||
time.sleep(wait_inject_s)
|
||||
@@ -501,11 +601,15 @@ def main():
|
||||
|
||||
# tensions
|
||||
df = compute_tensions(df, funding)
|
||||
log("tensions computed:")
|
||||
for col in ["funding_bps", "bs_ratio_signed", "activity_excess", "cvd_divergence"]:
|
||||
log("tensions computed (per-minute + 4hr levels + 4hr velocities):")
|
||||
for col in ["funding_bps", "bs_ratio_signed", "activity_excess", "cvd_divergence",
|
||||
"funding_bps_level", "bs_ratio_signed_level",
|
||||
"activity_excess_level", "cvd_divergence_level",
|
||||
"funding_bps_vel", "bs_ratio_signed_vel",
|
||||
"activity_excess_vel", "cvd_divergence_vel"]:
|
||||
s = df[col]
|
||||
finite = int(np.isfinite(s.astype(float)).sum())
|
||||
log(f" {col:<22} n_finite={finite:>5}/{len(s)} "
|
||||
log(f" {col:<26} n_finite={finite:>5}/{len(s)} "
|
||||
f"mean={s.mean():+.4f} std={s.std():+.4f} "
|
||||
f"min={s.min():+.4f} max={s.max():+.4f}")
|
||||
|
||||
@@ -519,9 +623,12 @@ def main():
|
||||
"minute_range": [int(df.minute.min()), int(df.minute.max())],
|
||||
"map_scale": MAP_SCALE,
|
||||
"spatial": {
|
||||
"center": list(CENTER), "dipole_off": DIPOLE_OFF,
|
||||
"center": list(CENTER),
|
||||
"dipole_off": DIPOLE_OFF, "dipole_off_vel": DIPOLE_OFF_VEL,
|
||||
"ring_r": RING_R,
|
||||
"sigma": SIGMA, "vorticity_n_legs": VORTICITY_N_LEGS,
|
||||
"ring_r_act_vel": RING_R_ACT_VEL, "ring_r_cvd_vel": RING_R_CVD_VEL,
|
||||
"sigma": SIGMA, "sigma_vel": SIGMA_VEL,
|
||||
"vorticity_n_legs": VORTICITY_N_LEGS, "act_vel_n_legs": ACT_VEL_N_LEGS,
|
||||
},
|
||||
"timing": {
|
||||
"per_minute_ms": PER_MINUTE_MS,
|
||||
@@ -532,6 +639,7 @@ def main():
|
||||
"activity_baseline_min": ROLL_ACTIVITY_WIN,
|
||||
"cvd_window_min": ROLL_CVD_WIN,
|
||||
"eps_bps": EPS_BPS,
|
||||
"level_win_min": LEVEL_WIN_MIN,
|
||||
},
|
||||
"arms": args.arms.split(","),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user