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
|
||||||
|
|
||||||
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
|
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