186 lines
5.8 KiB
Python
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()
|