Files
resonance-engine/inject_v1_blind.py
T
2026-06-06 16:34:29 +07:00

186 lines
5.8 KiB
Python

"""inject_v1_blind.py — blind variable test
Sends 6 inject_density pulses to the lattice (ZMQ 5557), one per
independent BTC trade variable, normalized by day stats.
Does NOT talk to Fractonaut. Fractonaut observes the lattice on its
own cadence and reports whatever it notices. After all 6 pulses are
sent, a human can ask Fractonaut for a debrief separately.
Layout: all 6 at (x=512, y=512, sigma=32). Only `strength` differs.
Pacing: one pulse every 180s wall clock.
Output: prints + writes D:/Resonance_Engine/inject_v1_blind_log.jsonl
"""
from __future__ import annotations
import glob, json, random, time
from pathlib import Path
import pyarrow.parquet as pq
import pandas as pd
import numpy as np
DATA_DAY = r"D:\PaperTrader\research\hl_data\minutes\20260601"
DATA_DAY_WSL = "/mnt/d/PaperTrader/research/hl_data/minutes/20260601"
COIN = "BTC"
ZMQ_ADDR = "tcp://127.0.0.1:5557"
LOG_PATH = Path(r"D:\Resonance_Engine\inject_v1_blind_log.jsonl")
LOG_PATH_WSL = Path("/mnt/d/Resonance_Engine/inject_v1_blind_log.jsonl")
# Fixed injection geometry. Only `strength` varies between calls.
INJECT_X = 512.0
INJECT_Y = 512.0
INJECT_SIG = 32.0
SPACING_S = 180 # 3 minutes between pulses
# Independent variable set (drop redundant buy/sell - kept signed_flow)
VARS = [
"mid_price",
"signed_flow_usd",
"trade_count",
"large_print_cnt",
"wallet_entropy",
"vwap_drift",
]
def load_btc_day() -> pd.DataFrame:
import sys
base = DATA_DAY_WSL if sys.platform.startswith("linux") else DATA_DAY
files = glob.glob(str(Path(base) / "*.parquet"))
dfs = [pq.read_table(f).to_pandas() for f in files]
df = pd.concat(dfs).sort_values("minute").reset_index(drop=True)
return df[df.coin == COIN].reset_index(drop=True)
def pick_normal_minute(df: pd.DataFrame) -> pd.Series:
"""Pick the minute with the LARGEST summed |z| across all variables.
This is an extreme minute - the lattice should see something on every
pulse, not just on the two big ones. Honest about what we're doing:
showing the lattice the strongest input we can build from one real
minute of BTC trading.
"""
z_sum = np.zeros(len(df))
for v in VARS:
s = df[v]
sd = float(s.std())
if sd == 0:
continue
z = (s - float(s.mean())) / sd
z_sum += z.abs().values
idx = int(np.argmax(z_sum))
return df.iloc[idx]
def compute_strengths(df: pd.DataFrame, row: pd.Series) -> dict:
"""For each variable, z-score against the full-day distribution, then
clip and scale into the lattice strength regime.
strength = clip(z, -3, +3) / 3 * 0.3
so a ±3-sigma value gives |strength|=0.3 (our known calibration band).
A median value gives |strength|≈0.
"""
out = {}
for v in VARS:
s = df[v]
mu = float(s.mean())
sd = float(s.std())
if sd == 0:
z = 0.0
else:
z = (float(row[v]) - mu) / sd
z_clip = max(-3.0, min(3.0, z))
strength = (z_clip / 3.0) * 0.3
out[v] = {
"raw": float(row[v]),
"day_mean": mu,
"day_std": sd,
"z": z,
"z_clip": z_clip,
"strength": strength,
}
return out
def main():
print(f"[INJECT] loading {COIN} from {DATA_DAY}")
df = load_btc_day()
print(f"[INJECT] {len(df)} rows (minutes {df.minute.min()}..{df.minute.max()})")
row = pick_normal_minute(df)
print(f"[INJECT] chose minute={row.minute} (typical-ish row)")
print(f" mid_price={row.mid_price:.2f} "
f"signed_flow={row.signed_flow_usd:.0f} "
f"trades={row.trade_count} vwap_drift={row.vwap_drift:.5f}")
print()
strengths = compute_strengths(df, row)
print("[INJECT] computed strengths:")
for v in VARS:
s = strengths[v]
print(f" {v:18s} raw={s['raw']:>15.4f} z={s['z']:>+7.2f} -> strength={s['strength']:>+7.4f}")
print()
# Random order so Fractonaut can't anchor on alphabetical/code order
order = VARS.copy()
random.shuffle(order)
print(f"[INJECT] random injection order: {order}")
print()
# ZMQ PUB to daemon
import zmq
ctx = zmq.Context()
sock = ctx.socket(zmq.PUB)
sock.connect(ZMQ_ADDR)
time.sleep(0.6) # slow-joiner
# Open log (append; survives restarts)
import sys
log_path = LOG_PATH_WSL if sys.platform.startswith("linux") else LOG_PATH
log_path.parent.mkdir(parents=True, exist_ok=True)
for i, var in enumerate(order):
s = strengths[var]
cmd = {
"cmd": "inject_density",
"x": INJECT_X,
"y": INJECT_Y,
"sigma": INJECT_SIG,
"strength": s["strength"],
}
wall = time.time()
sock.send_string(json.dumps(cmd))
record = {
"wall_ts": wall,
"wall_iso": time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(wall)),
"sequence_i": i,
"variable": var,
"raw_value": s["raw"],
"z": s["z"],
"z_clip": s["z_clip"],
"strength": s["strength"],
"x": INJECT_X,
"y": INJECT_Y,
"sigma": INJECT_SIG,
"minute": int(row.minute),
"coin": COIN,
}
with log_path.open("a", encoding="utf-8") as fh:
fh.write(json.dumps(record) + "\n")
print(f" [{i+1}/6] {time.strftime('%H:%M:%S')} {var:18s} "
f"strength={s['strength']:>+7.4f} -> SENT")
if i < len(order) - 1:
time.sleep(SPACING_S)
sock.close()
ctx.term()
print()
print(f"[INJECT] all 6 sent. Log appended at {log_path}")
print(f"[INJECT] Fractonaut should auto-observe each in its own ~70s window.")
if __name__ == "__main__":
main()