idea119-regflow-verifiable-pre/regflow/__init__.py

93 lines
3.5 KiB
Python

"""Minimal RegFlow implementation for tests.
Provides a tiny DSL compiler and a per-trade proof generator sufficient
for the unit tests in tests/test_core.py.
"""
from typing import Dict, List, Any
def _parse_line_to_rule(tokens: List[str]) -> Dict[str, Any]:
# Expected formats:
# constraint max_position <venue> <instrument> <limit>
# constraint min_cash <venue> <limit>
if len(tokens) < 2:
raise ValueError("Invalid constraint line: need at least type")
rtype = tokens[1]
if rtype == "max_position":
# tokens: ["constraint", "max_position", venue, instrument, limit]
if len(tokens) != 5:
raise ValueError("Invalid max_position constraint: need venue, instrument, limit")
venue, instrument, limit = tokens[2], tokens[3], int(tokens[4])
return {"type": "max_position", "venue": venue, "instrument": instrument, "limit": limit}
elif rtype == "min_cash":
# tokens: ["constraint", "min_cash", venue, limit]
if len(tokens) != 4:
raise ValueError("Invalid min_cash constraint: need venue, limit")
venue, limit = tokens[2], int(tokens[3])
return {"type": "min_cash", "venue": venue, "limit": limit}
else:
# Unknown constraint type; store generically
return {"type": rtype, "raw": tokens[2:]}
def compile_dsl(dsl: str) -> Dict[str, Any]:
"""Compile a tiny DSL into a canonical IR representation.
The DSL supports lines like:
constraint max_position venue AAPL 1000
constraint min_cash venue 5000
Returns a dict with a single key 'rules' containing a list of rule dicts.
"""
rules: List[Dict[str, Any]] = []
for raw_line in dsl.strip().splitlines():
line = raw_line.strip()
if not line or line.startswith("#"): # skip empty or comments
continue
tokens = line.split()
if not tokens:
continue
if tokens[0] != "constraint":
# ignore non-constraint lines for tests
continue
rule = _parse_line_to_rule(tokens)
rules.append(rule)
return {"rules": rules}
def _evaluate_rule(rule: Dict[str, Any], trade: Dict[str, Any]) -> Dict[str, Any]:
venue = trade.get("venue")
if rule.get("venue") is not None and venue != rule["venue"]:
return {"ok": False, "rule": rule, "actual": None}
if rule["type"] == "max_position":
actual = trade.get("qty")
ok = actual is not None and actual <= rule["limit"]
return {"ok": ok, "rule": rule, "actual": actual}
if rule["type"] == "min_cash":
actual = trade.get("cash")
ok = actual is not None and actual >= rule["limit"]
return {"ok": ok, "rule": rule, "actual": actual}
# Fallback: unknown rule type treated as OK (not used by tests)
return {"ok": True, "rule": rule, "actual": None}
def generate_proof(ir: Dict[str, Any], trade: Dict[str, Any]) -> Dict[str, Any]:
"""Generate a simple per-trade proof against the IR.
Returns a dict with:
- valid: bool
- summary: str
- details: list of {ok: bool, rule: dict, actual: Any}
"""
rules = ir.get("rules", [])
details = []
all_ok = True
for rule in rules:
result = _evaluate_rule(rule, trade)
details.append(result)
if not result["ok"]:
all_ok = False
summary = "all applicable constraints satisfied" if all_ok else "some applicable constraints violated"
return {"valid": all_ok, "summary": summary, "details": details}