SniperGold_ML/engine/verify/vinvariants.py

94 lines
3.1 KiB
Python

"""Independent invariant checks (spec 18.3).
All checks are implemented against the specification text; they never call
producer validation functions. Each returns (ok, details).
"""
from ..util import sha256_bytes
def check_monotonic_stream(records):
"""ts_ms must be non-decreasing across the merged stream."""
prev = None
bad = 0
first_bad = None
for rec in records:
ts = rec[0]
if prev is not None and ts < prev:
bad += 1
if first_bad is None:
first_bad = (prev, ts)
prev = ts
return bad == 0, {"regressions": bad, "first": first_bad}
def check_ohlc_validity(bar_rows):
"""low <= open,close <= high; counts >= 1; avg = sum // count."""
bad = 0
for row in bar_rows:
(bar_idx, _pid, _st, _en, o, h, l, c, n, _v, _smn, _smx, ss,
avg, _final, _f, _l) = row
if not (l <= o <= h and l <= c <= h and n >= 1):
bad += 1
if avg != (ss // n):
bad += 1
return bad == 0, {"invalid": bad}
def check_bar_uniqueness(bar_rows):
"""(TF period) uniqueness is implied per stream; bar_idx must be strictly
increasing; period_id strictly increasing within one stream."""
seen = {}
bad = 0
for row in bar_rows:
bar_idx, period_id = row[0], row[1]
if seen.get(period_id):
bad += 1
seen[period_id] = True
return bad == 0, {"duplicates": bad}
def check_chunk_counts_sum(per_chunk_counts, total):
return sum(per_chunk_counts) == total, {"sum": sum(per_chunk_counts),
"total": total}
def check_carry_continuity(carry, first_tick):
"""Resume continuity: first ts must be >= carried last_ts; carried
period must not be re-emitted."""
if carry is None:
return True, {}
if first_tick[0] < carry["last_ts_ms"]:
return False, {"first_ts": first_tick[0], "carried_last": carry["last_ts_ms"]}
return True, {}
def independent_serialize_ticks(records):
"""Independent canonical tick serialization (spec 8.5).
``records`` is an iterable of (ts_ms, bid_u, ask_u, vol) tuples in
rec_ord order."""
from .vaggregate import v_tick_serialize
return b"".join(v_tick_serialize(r[0], r[1], r[2], r[3]) for r in records)
def independent_ticks_content_id(records):
return sha256_bytes(independent_serialize_ticks(records))
def independent_bars_content_id(rows):
from .vaggregate import v_bar_serialize
return sha256_bytes(b"".join(v_bar_serialize(r) for r in rows))
def independent_preservation_content_id(pairs):
"""Independent source-preservation content id (spec 8.6).
``pairs`` is an iterable of (src_line, last_u) integers in rec_ord
order, extracted independently from the sidecar; the verifier re-derives
the identity from its own row extraction with the fixed canonical
serialization ``src_line|last_u\n`` (no floats)."""
parts = []
for src_line, last_u in pairs:
parts.append(("%d|%d" % (src_line, last_u)).encode("ascii") + b"\n")
return sha256_bytes(b"".join(parts))