build(agent): molt-z#db0ec5 iteration
This commit is contained in:
parent
87c397abee
commit
f5b9e4ed8c
|
|
@ -0,0 +1,21 @@
|
||||||
|
node_modules/
|
||||||
|
.npmrc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
__tests__/
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
tmp/
|
||||||
|
.tmp/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
*.egg-info/
|
||||||
|
.pytest_cache/
|
||||||
|
READY_TO_PUBLISH
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
# SignalVault SWARM Agent Guidance
|
||||||
|
|
||||||
|
- Architecture: Python MVP with Graph primitives (SignalNode, Edge, Scenario, HedgePlan), Graph-of-Contracts registry, deterministic replay engine, and privacy primitives stubs.
|
||||||
|
- Tech stack: Python 3.11+, dataclasses, in-memory registry, minimal cryptographic placeholders (no real crypto implemented here), toy adapters for price feed and venue.
|
||||||
|
- Testing: pytest-based unit tests; test.sh runs pytest and builds the package.
|
||||||
|
- Running tests: ./test.sh
|
||||||
|
- Building: python3 -m build
|
||||||
|
- Contribution rules: keep changes minimal and focused; add tests for new functionality; update AGENTS.md with new modules and tests.
|
||||||
|
|
||||||
|
Guiding principles:
|
||||||
|
- Start with a small, solid MVP; iterate via 1 feature per sprint.
|
||||||
|
- Ensure deterministic replay semantics for offline reproducibility.
|
||||||
|
- Provide adapters to demonstrate interoperability with external feeds and venues.
|
||||||
25
README.md
25
README.md
|
|
@ -1,3 +1,24 @@
|
||||||
# signalvault-verifiable-privacy-preservin
|
# SignalVault Verifiable Privacy-Preserving Signal Repository (MVP)
|
||||||
|
|
||||||
A portable, graph-backed signal store for finance that focuses on verifiable provenance, offline replay, and privacy-preserving sharing of market signals across venues. SignalVault stores signals as canonical graph primitives (SignalNode, Edge, Scena
|
This project implements a portable, graph-backed signal store focused on verifiable provenance,
|
||||||
|
offline replay, and privacy-preserving sharing of market signals across venues. The MVP defines
|
||||||
|
canonical graph primitives (SignalNode, Edge, Scenario, HedgePlan) and a Graph-of-Contracts
|
||||||
|
registry to map adapters to data feeds, risk models, and execution engines.
|
||||||
|
|
||||||
|
What you get in this MVP:
|
||||||
|
- Core graph primitives and a tiny in-memory registry
|
||||||
|
- Deterministic replay engine for applying deltas and reconstructing signal state
|
||||||
|
- Privacy primitives placeholders (secure aggregation, DP budgets)
|
||||||
|
- Toy adapters for a price feed and a simulated venue
|
||||||
|
- Lightweight tests validating schema and replay behavior
|
||||||
|
|
||||||
|
How to run locally:
|
||||||
|
- Ensure Python 3.11+ is installed
|
||||||
|
- Run tests via: ./test.sh
|
||||||
|
- Build package via: python3 -m build (as part of test.sh)
|
||||||
|
|
||||||
|
Publishing notes:
|
||||||
|
- Python package name: signalvault_verifiable_privacy_preservin
|
||||||
|
- Exposes a small public API surface for MVP exploration and integration tests
|
||||||
|
|
||||||
|
This repo intentionally keeps scope minimal and testable to facilitate 1-feature-per-sprint iteration.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "signalvault_verifiable_privacy_preservin"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "MVP: verifiable, privacy-preserving market signal repository with offline replay"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["." ]
|
||||||
|
|
||||||
|
# Version is defined in [project]. No dynamic tooling required for MVP.
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
"""SignalVault Verifiable, Privacy-Preserving Market Signals (MVP).
|
||||||
|
|
||||||
|
A minimal Python package scaffolding for SignalVault MVP focusing on
|
||||||
|
canonical graph primitives, a Graph-of-Contracts registry, deterministic
|
||||||
|
replay, and privacy primitives placeholders.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .schema import SignalNode, Edge, Scenario, HedgePlan, AuditLog, PrivacyBudget
|
||||||
|
from .registry import GraphRegistry
|
||||||
|
from .replay import DeterministicReplayEngine
|
||||||
|
from .privacy import SecureAggregator, DPBudget
|
||||||
|
from .adapters.price_feed import PriceFeedAdapter
|
||||||
|
from .adapters.simulated_venue import SimulatedVenueAdapter
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"SignalNode",
|
||||||
|
"Edge",
|
||||||
|
"Scenario",
|
||||||
|
"HedgePlan",
|
||||||
|
"AuditLog",
|
||||||
|
"PrivacyBudget",
|
||||||
|
"GraphRegistry",
|
||||||
|
"DeterministicReplayEngine",
|
||||||
|
"SecureAggregator",
|
||||||
|
"DPBudget",
|
||||||
|
"PriceFeedAdapter",
|
||||||
|
"SimulatedVenueAdapter",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""Adapter package for SignalVault MVP."""
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, List
|
||||||
|
from ..schema import SignalNode
|
||||||
|
|
||||||
|
|
||||||
|
class PriceFeedAdapter:
|
||||||
|
"""Toy price feed adapter that exposes SignalNode objects."""
|
||||||
|
|
||||||
|
def __init__(self, feed_name: str = "toy-price-feed") -> None:
|
||||||
|
self.feed_name = feed_name
|
||||||
|
|
||||||
|
def to_signal(self, asset: str, venue: str, price: float, ts: int) -> SignalNode:
|
||||||
|
return SignalNode(
|
||||||
|
asset=asset,
|
||||||
|
venue=venue,
|
||||||
|
signal_type="price",
|
||||||
|
timestamp=ts,
|
||||||
|
quality=1.0,
|
||||||
|
metadata={"feed": self.feed_name, "price": price},
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, List
|
||||||
|
from ..schema import HedgePlan
|
||||||
|
|
||||||
|
|
||||||
|
class SimulatedVenueAdapter:
|
||||||
|
"""Toy venue adapter that turns HedgePlan deltas into orders."""
|
||||||
|
|
||||||
|
def __init__(self, venue_name: str = "toy-venue") -> None:
|
||||||
|
self.venue_name = venue_name
|
||||||
|
|
||||||
|
def delta_to_order(self, hedge_plan: HedgePlan) -> Dict[str, object]:
|
||||||
|
# very simple mapping: delta keys -> order dict
|
||||||
|
order = {
|
||||||
|
"venue": self.venue_name,
|
||||||
|
"hedge_id": hedge_plan.id,
|
||||||
|
"action": hedge_plan.delta.get("action", "adjust"),
|
||||||
|
"params": hedge_plan.delta,
|
||||||
|
}
|
||||||
|
return order
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
|
||||||
|
class SecureAggregator:
|
||||||
|
"""Placeholder secure-aggregation primitive.
|
||||||
|
In a real system this would perform cryptographic aggregation (e.g., via MPC).
|
||||||
|
Here we simply sum numeric signals and average others defensively.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def aggregate(signals: List[Dict[str, object]]) -> Dict[str, object]:
|
||||||
|
if not signals:
|
||||||
|
return {}
|
||||||
|
# naive aggregation by key
|
||||||
|
keys = signals[0].keys()
|
||||||
|
result: Dict[str, object] = {}
|
||||||
|
for k in keys:
|
||||||
|
try:
|
||||||
|
vals = [float(s.get(k, 0)) for s in signals]
|
||||||
|
result[k] = sum(vals) / len(vals)
|
||||||
|
except Exception:
|
||||||
|
# fallback to last value
|
||||||
|
result[k] = signals[-1].get(k)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class DPBudget:
|
||||||
|
"""Per-signal differential privacy budget placeholder."""
|
||||||
|
|
||||||
|
def __init__(self, total_budget: float = 1.0) -> None:
|
||||||
|
self.total_budget = float(total_budget)
|
||||||
|
self.used = 0.0
|
||||||
|
|
||||||
|
def allocate(self, amount: float) -> float:
|
||||||
|
amount = float(amount)
|
||||||
|
remaining = max(0.0, self.total_budget - self.used)
|
||||||
|
take = min(remaining, amount)
|
||||||
|
self.used += take
|
||||||
|
return take
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, Optional
|
||||||
|
from .schema import RegistryEntry
|
||||||
|
|
||||||
|
|
||||||
|
class GraphRegistry:
|
||||||
|
"""A tiny in-memory Graph-of-Contracts registry mapping adapters to contracts."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._registry: Dict[str, RegistryEntry] = {}
|
||||||
|
|
||||||
|
def register_adapter(self, adapter_name: str, contract: Dict[str, object], conformance: bool = False) -> None:
|
||||||
|
self._registry[adapter_name] = RegistryEntry(
|
||||||
|
adapter_name=adapter_name,
|
||||||
|
contract=contract,
|
||||||
|
conformance=conformance,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_contract(self, adapter_name: str) -> Optional[Dict[str, object]]:
|
||||||
|
entry = self._registry.get(adapter_name)
|
||||||
|
return entry.contract if entry else None
|
||||||
|
|
||||||
|
def is_conformant(self, adapter_name: str) -> bool:
|
||||||
|
entry = self._registry.get(adapter_name)
|
||||||
|
return bool(entry and entry.conformance)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, List
|
||||||
|
from .schema import SignalNode, Edge, HedgePlan
|
||||||
|
|
||||||
|
|
||||||
|
class DeterministicReplayEngine:
|
||||||
|
"""A tiny deterministic replay engine for applying deltas to a stored graph state."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def apply_delta(self, state: Dict[str, object], delta: Dict[str, object]) -> Dict[str, object]:
|
||||||
|
"""Apply a delta to a state dict in a deterministic way.
|
||||||
|
This is a minimal placeholder: keys in delta override state.
|
||||||
|
"""
|
||||||
|
new_state = dict(state)
|
||||||
|
for k, v in delta.items():
|
||||||
|
new_state[k] = v
|
||||||
|
return new_state
|
||||||
|
|
||||||
|
def replay(self, base_state: Dict[str, object], deltas: List[Dict[str, object]]) -> Dict[str, object]:
|
||||||
|
state = dict(base_state)
|
||||||
|
for d in deltas:
|
||||||
|
state = self.apply_delta(state, d)
|
||||||
|
return state
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List, Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SignalNode:
|
||||||
|
asset: str
|
||||||
|
venue: str
|
||||||
|
signal_type: str
|
||||||
|
timestamp: int # epoch seconds
|
||||||
|
quality: float = 0.0
|
||||||
|
metadata: Dict[str, object] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Edge:
|
||||||
|
from_id: str
|
||||||
|
to_id: str
|
||||||
|
relation: str # e.g., "causal", "temporal"
|
||||||
|
timestamp: int
|
||||||
|
metadata: Dict[str, object] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Scenario:
|
||||||
|
id: str
|
||||||
|
nodes: List[str] = field(default_factory=list)
|
||||||
|
edges: List[str] = field(default_factory=list)
|
||||||
|
description: str = ""
|
||||||
|
version: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HedgePlan:
|
||||||
|
id: str
|
||||||
|
delta: Dict[str, object] # minimal representation of hedging actions
|
||||||
|
version: int = 1
|
||||||
|
approvals: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLog:
|
||||||
|
entry: str
|
||||||
|
timestamp: int
|
||||||
|
actor: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PrivacyBudget:
|
||||||
|
signal_id: str
|
||||||
|
budget: float # DP budget allocated per signal
|
||||||
|
policy: Dict[str, object] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RegistryEntry:
|
||||||
|
adapter_name: str
|
||||||
|
contract: Dict[str, object]
|
||||||
|
conformance: bool = False
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Running tests..."
|
||||||
|
# Ensure the repo root is on PYTHONPATH so local packages are importable
|
||||||
|
export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$PWD"
|
||||||
|
pytest -q
|
||||||
|
|
||||||
|
echo "Building package..."
|
||||||
|
python3 -m build
|
||||||
|
|
||||||
|
echo "All tests passed and build completed."
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import time
|
||||||
|
from signalvault_verifiable_privacy_preservin.schema import SignalNode, HedgePlan
|
||||||
|
from signalvault_verifiable_privacy_preservin.replay import DeterministicReplayEngine
|
||||||
|
|
||||||
|
|
||||||
|
def test_delta_application_is_deterministic():
|
||||||
|
# base state represents a simple graph state snapshot
|
||||||
|
base = {
|
||||||
|
"signals": [],
|
||||||
|
"hedges": [],
|
||||||
|
"version": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
delta1 = {"signals": ["s1"], "version": 2}
|
||||||
|
delta2 = {"hedges": ["h1"], "version": 3}
|
||||||
|
|
||||||
|
engine = DeterministicReplayEngine()
|
||||||
|
st1 = engine.apply_delta(base, delta1)
|
||||||
|
st2 = engine.apply_delta(st1, delta2)
|
||||||
|
|
||||||
|
assert st2["version"] == 3
|
||||||
|
assert st2["signals"] == ["s1"]
|
||||||
|
assert st2["hedges"] == ["h1"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema_dataclasses_are_instantiable():
|
||||||
|
sn = SignalNode(asset="ETH", venue="VenueA", signal_type="price", timestamp=int(time.time()), quality=0.9)
|
||||||
|
hp = HedgePlan(id="hp1", delta={"action": "buy"})
|
||||||
|
assert sn.asset == "ETH"
|
||||||
|
assert hp.id == "hp1"
|
||||||
Loading…
Reference in New Issue