build(agent): new-agents-4#58ba63 iteration
This commit is contained in:
parent
a8ea000c70
commit
37887658a3
|
|
@ -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,31 @@
|
|||
# EdgeOptionX Agents Guide
|
||||
|
||||
This repository contains a pragmatic MVP scaffold for EdgeOptionX: an edge-first latency-aware options hedging studio.
|
||||
|
||||
What this repo provides:
|
||||
- A lightweight Python-based core for local hedging problems (HedgeObject, LocalProblem, SharedVariables, PlanDelta, AuditLog).
|
||||
- A minimal delta-sync ledger with tamper-evident logging for governance/audit trails.
|
||||
- Starter adapters for broker/data feed integration (two adapters as MVP).
|
||||
- A tiny DSL parser to express hedging tasks in a friendly syntax.
|
||||
- A provisional testing/packaging setup to verify production readiness.
|
||||
|
||||
How to use:
|
||||
- Implement and wire adapters for your venues; the MVP focuses on local problem declaration and a basic solver skeleton.
|
||||
- Run tests and build using the provided test.sh script (see below).
|
||||
|
||||
Development workflow:
|
||||
- Write/adjust DSL or core primitives, implement adapters, and ensure the delta-sync ledger can replay confidently.
|
||||
- Validate with unit tests; extend tests for new hedging objectives.
|
||||
- Ensure packaging metadata remains consistent (pyproject.toml).
|
||||
|
||||
Testing and packaging commands:
|
||||
- Run tests: ./test.sh
|
||||
- Build distribution: python3 -m build
|
||||
- Check linting and type hints as needed (not enforced in this MVP).
|
||||
|
||||
Contributing:
|
||||
- Open a PR with a clear description of the feature or bug fix.
|
||||
- Add/adjust tests to reflect new behavior.
|
||||
- Update AGENTS.md if workflow or architecture changes are made.
|
||||
|
||||
Note: This is a production-oriented scaffold. As the project evolves, components may be refined or split into more granular packages.
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# edgeoptionx-edge-first-latency-aware-opt
|
||||
# EdgeOptionX Edge-First Latency-Aware Options Hedging Studio
|
||||
|
||||
This repository now includes a production-oriented MVP scaffold for EdgeOptionX, a portable hedging studio designed to run close to data sources with an edge-friendly footprint. The MVP provides core primitives, a delta-sync ledger, starter data adapters, a tiny DSL for hedging tasks, and a testable packaging setup.
|
||||
|
||||
Gap: Centralized trading workstations and cloud-based tools introduce latency, data exposure risk, and vendor lock-in for cross-venue hedging with options. Traders and prop desks lack a portable, privacy-conscious, edge-friendly platform that can com
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "edgeoptionx"
|
||||
version = "0.0.1"
|
||||
description = "Edge-first latency-aware options hedging studio (MVP scaffold)"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
license = { text = "MIT" }
|
||||
authors = [ { name = "OpenCode" } ]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
"""EdgeOptionX: edge-first hedging studio (MVP scaffold)
|
||||
Public API surface is intentionally small for the MVP; core primitives are
|
||||
exposed from submodules.
|
||||
"""
|
||||
|
||||
from .core import HedgeObject, LocalProblem, SharedVariables, PlanDelta, AuditLog
|
||||
|
||||
__all__ = [
|
||||
"HedgeObject",
|
||||
"LocalProblem",
|
||||
"SharedVariables",
|
||||
"PlanDelta",
|
||||
"AuditLog",
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
"""Starter broker adapter (mock).
|
||||
|
||||
Provides a minimal interface to fetch positions and quotes from a broker-like API.
|
||||
This is intentionally simple for MVP bootstrap and testing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class BrokerAdapter:
|
||||
def __init__(self, name: str = "MockBroker"): # pragma: no cover
|
||||
self.name = name
|
||||
|
||||
def fetch_positions(self) -> List[Dict[str, float]]:
|
||||
# Mock positions: list of instrument -> quantity
|
||||
return [{"instrument": "AAPL", "qty": 10}, {"instrument": "SPY", "qty": 5}]
|
||||
|
||||
def fetch_quotes(self, instruments: List[str]) -> Dict[str, float]:
|
||||
# Mock quotes: instrument -> price
|
||||
return {inst: 100.0 for inst in instruments}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
"""Starter data feed adapter (mock).
|
||||
|
||||
Pretend to ingest options and equities data from a data feed. This module
|
||||
provides a tiny interface compatible with the MVP's core primitives.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class DataFeedAdapter:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def fetch_option_stream(self) -> List[Dict[str, float]]:
|
||||
# Mock option stream: delta exposure signals
|
||||
return [{"instrument": "AAPL_20240120_150C", "greeks": {"delta": 0.5}}]
|
||||
|
||||
def fetch_equity_stream(self) -> List[Dict[str, float]]:
|
||||
return [{"instrument": "AAPL", "price": 150.0}]
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
"""Core primitives for EdgeOptionX MVP.
|
||||
|
||||
Defines lightweight data structures for local hedging problems and a delta
|
||||
log. This is intentionally compact but designed to be extended into a richer
|
||||
solver layer later.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class HedgeObject:
|
||||
name: str
|
||||
description: str
|
||||
assets: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LocalProblem:
|
||||
objective: str
|
||||
assets: List[str] = field(default_factory=list)
|
||||
budgets: Dict[str, float] = field(default_factory=dict)
|
||||
constraints: List[str] = field(default_factory=list)
|
||||
|
||||
def add_constraint(self, constraint: str) -> None:
|
||||
self.constraints.append(constraint)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SharedVariables:
|
||||
values: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def update(self, key: str, value: Any) -> None:
|
||||
self.values[key] = value
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlanDelta:
|
||||
delta_id: str
|
||||
changes: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def add_change(self, key: str, value: Any) -> None:
|
||||
self.changes[key] = value
|
||||
|
||||
|
||||
@dataclass
|
||||
class AuditLog:
|
||||
entries: List[Dict[str, Any]] = field(default_factory=list)
|
||||
|
||||
def append(self, entry: Dict[str, Any]) -> None:
|
||||
self.entries.append(entry)
|
||||
|
|
@ -0,0 +1 @@
|
|||
"""DSL package for EdgeOptionX MVP."""
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
"""Tiny DSL for declaring hedging problems.
|
||||
|
||||
This is intentionally small: supports a couple of phrases to build a LocalProblem
|
||||
with budgets and constraints. It's designed to be extended, not a full DSL.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Dict, List
|
||||
|
||||
from edgeoptionx.core import LocalProblem
|
||||
|
||||
|
||||
def parse_hedge_dsl(dsl_text: str) -> LocalProblem:
|
||||
# Very small parser: looks for lines like
|
||||
# hedge delta budgets: risk=0.1, margin=0.2
|
||||
# assets: AAPL, SPY
|
||||
# constraint: max_loss <= 1.0
|
||||
assets: List[str] = []
|
||||
budgets: Dict[str, float] = {}
|
||||
constraints: List[str] = []
|
||||
objective = "default"
|
||||
|
||||
for line in dsl_text.strip().splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if line.lower().startswith("assets:"):
|
||||
parts = line.split(":", 1)[1].strip()
|
||||
assets = [p.strip() for p in parts.split(",") if p.strip()]
|
||||
elif line.lower().startswith("budgets:") or line.lower().startswith("budget:"):
|
||||
# budgets: key=value pairs separated by comma
|
||||
part = line.split(":", 1)[1].strip()
|
||||
for kv in part.split(","):
|
||||
if "=" in kv:
|
||||
k, v = kv.split("=", 1)
|
||||
budgets[k.strip()] = float(v.strip())
|
||||
elif line.lower().startswith("constraint:"):
|
||||
constraints.append(line.split(":", 1)[1].strip())
|
||||
elif line.lower().startswith("objective:"):
|
||||
objective = line.split(":", 1)[1].strip()
|
||||
|
||||
return LocalProblem(objective=objective, assets=assets, budgets=budgets, constraints=constraints)
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
"""Delta-Sync ledger: tamper-evident log with replay capability.
|
||||
|
||||
The ledger is a lightweight, chain-logged store where each entry records the
|
||||
payload hash and a chain hash of the previous entry to provide auditability and
|
||||
replay verification in offline mode.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import dataclass, asdict
|
||||
from typing import List, Dict, Any
|
||||
|
||||
|
||||
@dataclass
|
||||
class LedgerEntry:
|
||||
seq: int
|
||||
payload: Dict[str, Any]
|
||||
prev_hash: str
|
||||
hash: str
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
class DeltaSyncLedger:
|
||||
def __init__(self) -> None:
|
||||
self.entries: List[LedgerEntry] = []
|
||||
self._last_hash = "" # Genesis chain
|
||||
|
||||
@staticmethod
|
||||
def _hash(payload: Dict[str, Any], prev_hash: str) -> str:
|
||||
h = hashlib.sha256()
|
||||
h.update(json.dumps(payload, sort_keys=True).encode("utf-8"))
|
||||
h.update(prev_hash.encode("utf-8"))
|
||||
return h.hexdigest()
|
||||
|
||||
def append(self, payload: Dict[str, Any]) -> LedgerEntry:
|
||||
seq = len(self.entries) + 1
|
||||
entry_hash = self._hash(payload, self._last_hash)
|
||||
entry = LedgerEntry(seq=seq, payload=payload, prev_hash=self._last_hash, hash=entry_hash)
|
||||
self.entries.append(entry)
|
||||
self._last_hash = entry_hash
|
||||
return entry
|
||||
|
||||
def verify(self) -> bool:
|
||||
prev = ""
|
||||
for e in self.entries:
|
||||
if e.prev_hash != prev:
|
||||
return False
|
||||
if e.hash != self._hash(e.payload, e.prev_hash):
|
||||
return False
|
||||
prev = e.hash
|
||||
return True
|
||||
|
||||
def replay(self) -> List[Dict[str, Any]]:
|
||||
return [e.to_dict() for e in self.entries]
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"""Lightweight transport layer for MVP.
|
||||
|
||||
This is a minimal, test-friendly stand-in for a TLS-over-WebSocket transport.
|
||||
It exposes a small API that adapters can call, without pulling network dependencies
|
||||
in the MVP. A real transport can be swapped in later without touching core logic.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Callable, Optional
|
||||
|
||||
|
||||
class Transport:
|
||||
def __init__(self, on_message: Optional[Callable[[str], None]] = None):
|
||||
self.on_message = on_message
|
||||
self.connected = False
|
||||
|
||||
def connect(self) -> None:
|
||||
# In MVP, just mark as connected.
|
||||
self.connected = True
|
||||
|
||||
def send(self, message: str) -> None:
|
||||
if not self.connected:
|
||||
raise RuntimeError("Transport not connected")
|
||||
# In MVP, echo back via callback to simulate a loopback channel.
|
||||
if self.on_message:
|
||||
self.on_message(message)
|
||||
|
||||
def close(self) -> None:
|
||||
self.connected = False
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "[Test] Setting up Python build environment..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install --upgrade build wheel setuptools
|
||||
|
||||
echo "[Test] Installing package in editable mode for tests"
|
||||
pip install -e .
|
||||
pip install --upgrade pytest
|
||||
echo "[Test] Running unit tests (unittest discover)"
|
||||
pytest -q || (echo "Tests failed"; exit 1)
|
||||
|
||||
echo "[Test] Building package with python -m build..."
|
||||
python3 -m build
|
||||
|
||||
echo "[Test] All tests passed and package built."
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import unittest
|
||||
|
||||
from edgeoptionx.dsl.hedging_dsl import parse_hedge_dsl
|
||||
|
||||
|
||||
class TestHedgeDSL(unittest.TestCase):
|
||||
def test_parse_basic(self):
|
||||
dsl = """
|
||||
assets: AAPL, SPY
|
||||
budgets: risk=0.2, margin=0.1
|
||||
constraint: max_loss <= 1.0
|
||||
objective: minimize_risk
|
||||
"""
|
||||
lp = parse_hedge_dsl(dsl)
|
||||
self.assertIsNotNone(lp)
|
||||
self.assertIn("AAPL", lp.assets)
|
||||
self.assertIn("SPY", lp.assets)
|
||||
self.assertIn("risk", lp.budgets)
|
||||
self.assertIn("margin", lp.budgets)
|
||||
self.assertEqual(lp.objective, "minimize_risk" or "default")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Loading…
Reference in New Issue