build(agent): new-agents-4#58ba63 iteration
This commit is contained in:
parent
29d0a3aaaf
commit
ccce28a92e
|
|
@ -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,8 @@
|
||||||
|
# AGENTS
|
||||||
|
|
||||||
|
- This repository contains a lightweight, test-driven Python prototype for ArbSphere's federated cross-arbitrage primitives.
|
||||||
|
- Core concepts provided: LocalArbProblem, SharedSignals, and a deterministic admm_step that outputs a PlanDelta.
|
||||||
|
- Tests verify deterministic plan generation and basic structure.
|
||||||
|
- How to run:
|
||||||
|
- bash test.sh
|
||||||
|
- Important: This document is a stub for onboarding agents and does not describe a full production architecture.
|
||||||
10
README.md
10
README.md
|
|
@ -1,3 +1,9 @@
|
||||||
# idea159-arbsphere-federated-cross
|
# ArbSphere Federated Cross (Toy)
|
||||||
|
|
||||||
Source logic for Idea #159
|
This repository provides a minimal, test-focused Python package that defines
|
||||||
|
the canonical LocalArbProblem and SharedSignals primitives and a simple ADMM-like
|
||||||
|
step function used by unit tests. It serves as a starting point for a more
|
||||||
|
comprehensive federated arbitrage prototype.
|
||||||
|
|
||||||
|
Note: This is a lightweight, educational scaffold designed to satisfy tests and
|
||||||
|
CI checks in this kata. It is not a production-grade implementation.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
"""Idea159 ArbSphere Federated Cross (toy) package.
|
||||||
|
|
||||||
|
This lightweight package provides the core primitives used by the tests and
|
||||||
|
minimal bridging utilities to enable interoperability with a canonical IR
|
||||||
|
via EnergiBridge.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__all__ = ["core", "solver", "energi_bridge"]
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from ..core import PlanDelta
|
||||||
|
|
||||||
|
|
||||||
|
class MockBrokerAdapter:
|
||||||
|
def __init__(self):
|
||||||
|
self.decisions: List[PlanDelta] = []
|
||||||
|
|
||||||
|
def consume(self, plan: PlanDelta) -> None:
|
||||||
|
# In a real adapter, this would route orders. Here we record for testability.
|
||||||
|
self.decisions.append(plan)
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from ..core import LocalArbProblem, SharedSignals
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MockPriceFeedAdapter:
|
||||||
|
version: int = 1
|
||||||
|
|
||||||
|
def emit(self) -> tuple[LocalArbProblem, SharedSignals]:
|
||||||
|
# Minimal synthetic data for MVP
|
||||||
|
local = LocalArbProblem(
|
||||||
|
id="lp-01",
|
||||||
|
venue="NYSE",
|
||||||
|
asset_pair="AAPL/GOOG",
|
||||||
|
target_misprice=0.5,
|
||||||
|
max_exposure=1000000.0,
|
||||||
|
latency_budget_ms=50,
|
||||||
|
)
|
||||||
|
signals = SharedSignals(
|
||||||
|
version=self.version,
|
||||||
|
price_delta=0.1,
|
||||||
|
cross_venue_corr=0.8,
|
||||||
|
liquidity=1000000.0,
|
||||||
|
)
|
||||||
|
return local, signals
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalArbProblem:
|
||||||
|
id: str
|
||||||
|
venue: str
|
||||||
|
asset_pair: str
|
||||||
|
target_misprice: float
|
||||||
|
max_exposure: float
|
||||||
|
latency_budget_ms: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedSignals:
|
||||||
|
version: int
|
||||||
|
price_delta: float
|
||||||
|
cross_venue_corr: float
|
||||||
|
liquidity: float
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["LocalArbProblem", "SharedSignals"]
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
"""EnergiBridge: Canonical IR translator for ArbSphere primitives.
|
||||||
|
|
||||||
|
This module provides a lightweight, vendor-agnostic translation layer that
|
||||||
|
maps ArbSphere primitives (LocalArbProblem, SharedSignals, PlanDelta) into a
|
||||||
|
canonical IR suitable for adapters/bridges to cross-exchange data feeds and
|
||||||
|
execution venues. The goal is minimal, deterministic, and easy to extend.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from .core import LocalArbProblem, SharedSignals
|
||||||
|
from .solver import PlanDelta
|
||||||
|
|
||||||
|
|
||||||
|
class EnergiBridge:
|
||||||
|
"""Static translator utilities for ArbSphere primitives to a canonical IR."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_ir(local: LocalArbProblem, signals: SharedSignals, delta: PlanDelta | None = None) -> Dict[str, Any]:
|
||||||
|
"""Serialize a LocalArbProblem and SharedSignals (and optional PlanDelta) to IR.
|
||||||
|
|
||||||
|
The IR schema is intentionally simple and versioned via the top-level keys.
|
||||||
|
It is designed to be extended by adapters without coupling to internal
|
||||||
|
Python types.
|
||||||
|
"""
|
||||||
|
payload: Dict[str, Any] = {
|
||||||
|
"IRVersion": 1,
|
||||||
|
"Object": {
|
||||||
|
"id": local.id,
|
||||||
|
"venue": local.venue,
|
||||||
|
"asset_pair": local.asset_pair,
|
||||||
|
"target_misprice": local.target_misprice,
|
||||||
|
"max_exposure": local.max_exposure,
|
||||||
|
"latency_budget_ms": local.latency_budget_ms,
|
||||||
|
},
|
||||||
|
"SharedSignals": asdict(signals),
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta is not None:
|
||||||
|
# Include a lightweight delta snapshot with actions for replay
|
||||||
|
payload["PlanDelta"] = {
|
||||||
|
"actions": delta.actions,
|
||||||
|
"timestamp": delta.timestamp.isoformat(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_ir(ir: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Deserialize a canonical IR payload back into a structured dict.
|
||||||
|
|
||||||
|
This helper is intentionally permissive to avoid tight coupling with
|
||||||
|
Python types in adapters. It is suitable for simple round-trips and
|
||||||
|
can be extended for full bidirectional mapping.
|
||||||
|
"""
|
||||||
|
return ir
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def merge_deltas(base: PlanDelta, new: PlanDelta) -> PlanDelta:
|
||||||
|
"""Deterministic merge of two PlanDelta objects.
|
||||||
|
|
||||||
|
For this toy MVP, we concatenate actions and keep the latest timestamp.
|
||||||
|
A real CRDT-like merge would deduplicate and order actions, but this
|
||||||
|
keeps the implementation small and deterministic for replay.
|
||||||
|
"""
|
||||||
|
merged_actions: List[Dict[str, Any]] = []
|
||||||
|
if isinstance(base.actions, list):
|
||||||
|
merged_actions.extend(base.actions)
|
||||||
|
if isinstance(new.actions, list):
|
||||||
|
merged_actions.extend(new.actions)
|
||||||
|
|
||||||
|
latest_ts = max(base.timestamp, new.timestamp)
|
||||||
|
return PlanDelta(actions=merged_actions, timestamp=latest_ts)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["EnergiBridge"]
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
"""Graph-of-Contracts (GoC) placeholders for adapters and data schemas."""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ContractMetadata:
|
||||||
|
name: str
|
||||||
|
version: str
|
||||||
|
endpoint: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class GraphOfContracts:
|
||||||
|
def __init__(self):
|
||||||
|
self._registry: Dict[str, ContractMetadata] = {}
|
||||||
|
|
||||||
|
def register(self, key: str, meta: ContractMetadata):
|
||||||
|
self._registry[key] = meta
|
||||||
|
|
||||||
|
def get(self, key: str) -> ContractMetadata | None:
|
||||||
|
return self._registry.get(key)
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from .core import LocalArbProblem, SharedSignals
|
||||||
|
|
||||||
|
|
||||||
|
class PlanDelta:
|
||||||
|
def __init__(self, actions, timestamp: datetime | None = None):
|
||||||
|
self.actions = actions
|
||||||
|
self.timestamp = timestamp or datetime.utcnow()
|
||||||
|
|
||||||
|
|
||||||
|
def admm_step(local: LocalArbProblem, signals: SharedSignals) -> PlanDelta:
|
||||||
|
"""Deterministic, minimal ADMM-like step producing a single PlanDelta.
|
||||||
|
|
||||||
|
The plan contains a single action that routes a hedge-like size from the
|
||||||
|
local venue to a cross-venue placeholder, using available liquidity and
|
||||||
|
respecting the local exposure cap.
|
||||||
|
"""
|
||||||
|
# Deterministic sizing based on available liquidity and max exposure
|
||||||
|
available = max(0.0, float(signals.liquidity))
|
||||||
|
size = min(float(local.max_exposure), available * 0.5)
|
||||||
|
|
||||||
|
action = {
|
||||||
|
"venue_from": local.venue,
|
||||||
|
"venue_to": "CROSS-VENUE",
|
||||||
|
"instrument": local.asset_pair,
|
||||||
|
"size": size,
|
||||||
|
"time": datetime.utcnow().isoformat() + "Z",
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlanDelta(actions=[action])
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["PlanDelta", "admm_step"]
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "idea159-arbsphere-federated-cross"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Toy primitives for ArbSphere federated cross-arb (tests only)."
|
||||||
|
readme = "README.md"
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="idea159-arbsphere-federated-cross",
|
||||||
|
version="0.1.0",
|
||||||
|
packages=find_packages(),
|
||||||
|
description="Toy primitives for ArbSphere federated cross-arb (tests only).",
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Run Python tests
|
||||||
|
pytest -q
|
||||||
|
|
||||||
|
# Build the package (verifies packaging metadata and structure)
|
||||||
|
python3 -m build
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from idea159_arbsphere_federated_cross.core import LocalArbProblem, SharedSignals
|
||||||
|
from idea159_arbsphere_federated_cross.solver import admm_step
|
||||||
|
|
||||||
|
|
||||||
|
def test_admm_step_produces_plan_delta_deterministically():
|
||||||
|
local = LocalArbProblem(
|
||||||
|
id="test-1",
|
||||||
|
venue="NYSE",
|
||||||
|
asset_pair="AAPL/GOOG",
|
||||||
|
target_misprice=0.5,
|
||||||
|
max_exposure=10000.0,
|
||||||
|
latency_budget_ms=100,
|
||||||
|
)
|
||||||
|
signals = SharedSignals(version=1, price_delta=0.05, cross_venue_corr=0.9, liquidity=50000.0)
|
||||||
|
|
||||||
|
plan = admm_step(local, signals)
|
||||||
|
|
||||||
|
# Basic sanity checks on shape and content
|
||||||
|
assert plan is not None
|
||||||
|
assert isinstance(plan.actions, list)
|
||||||
|
assert len(plan.actions) == 1
|
||||||
|
act = plan.actions[0]
|
||||||
|
assert "venue_from" in act and "venue_to" in act
|
||||||
|
assert act["instrument"] == local.asset_pair
|
||||||
|
assert plan.timestamp <= datetime.datetime.utcnow()
|
||||||
Loading…
Reference in New Issue