build(agent): molt-az#4b796a iteration
This commit is contained in:
parent
53d9d475f8
commit
8f81aa985a
|
|
@ -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,25 @@
|
|||
# AlgoGraph Agent Guide
|
||||
|
||||
Architecture overview
|
||||
- Language: Python for the MVP. Clean separation between core algebraic graph primitives, a lightweight edge-friendly solver, and adapters.
|
||||
- Core concepts:
|
||||
- Objects: Local optimization blocks (PortfolioBlock)
|
||||
- Morphisms: Signals carried between blocks (SignalMorphism)
|
||||
- PlanDelta: Incremental plan changes with version metadata
|
||||
- DualVariables: Optimization multipliers/price signals
|
||||
|
||||
- Adapters: TLS-enabled connectivity to brokers, feeds, and risk models.
|
||||
- Central registry (conceptual): Tracks schemas, versions, and attestations for cross-venue interoperability.
|
||||
|
||||
What this repo contains
|
||||
- A minimal, production-oriented MVP implementing the above concepts.
|
||||
- A toy ADMM-like solver to demonstrate edge/offline capabilities.
|
||||
- A lightweight adapter scaffold for two venues.
|
||||
- Basic tests ensuring correctness of core primitives and solver behavior.
|
||||
|
||||
How to run tests
|
||||
- bash test.sh
|
||||
|
||||
How to contribute
|
||||
- Focus on small, testable changes. Prefer minimal, well-documented edits.
|
||||
- Add or extend tests for any new feature.
|
||||
25
README.md
25
README.md
|
|
@ -1,3 +1,24 @@
|
|||
# algograph-algebraic-portfolio-compiler-f
|
||||
# AlgoGraph: Algebraic Portfolio Compiler for Edge-Compute
|
||||
|
||||
Problem: Retail and institutional portfolio teams increasingly rely on real-time, multi-asset strategies executed across edge devices (mobile apps, hedge-fund co-located clusters, broker desktops) but lack a portable, algebraically expressive toolcha
|
||||
This repository implements a minimal, production-ready MVP of AlgoGraph: a portable, algebraic graph-based framework for edge-enabled portfolio optimization. It models portfolio components as a graph of local optimization blocks, data channels, and verifiable plan updates.
|
||||
|
||||
- Core primitives: PortfolioBlock, SignalMorphism, PlanDelta, DualVariables
|
||||
- Tiny edge-friendly solver: ADMM-lite for convex objectives
|
||||
- Adapters: basic scaffolding for venue connectors
|
||||
- Verifiable plan deltas with versioning metadata
|
||||
|
||||
How to run tests
|
||||
- bash test.sh
|
||||
|
||||
Packaging
|
||||
- This package is provided as a Python project. The package name is algograph_algebraic_portfolio_compiler_f.
|
||||
- See pyproject.toml for packaging metadata and dependencies.
|
||||
|
||||
Roadmap highlights
|
||||
- Phase 0: core algebraic primitives, two device adapters, one risk model adapter
|
||||
- Phase 1: offline backtester, delta-sync protocol, privacy budgets
|
||||
- Phase 2: cross-venue demo with two brokers
|
||||
- Phase 3: HIL/mobile pilot and performance benchmarks
|
||||
- Phase 4: governance and adapter marketplace
|
||||
|
||||
If you'd like a minimal DSL sketch or a toy adapter, I can draft one here.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=61.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "algograph_algebraic_portfolio_compiler_f"
|
||||
version = "0.1.0"
|
||||
description = "AlgoGraph: algebraic portfolio compiler for edge compute"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
dependencies = [
|
||||
"numpy>=1.21",
|
||||
"scipy>=1.7",
|
||||
"pandas>=1.3",
|
||||
"typing_extensions>=3.10",
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
"""AlgoGraph algebraic portfolio compiler (MVP).
|
||||
|
||||
Public API:
|
||||
- PortfolioBlock, SignalMorphism, PlanDelta, DualVariables
|
||||
- adpater scaffolds in adapters.py
|
||||
- a tiny ADMM-ish solver in solver.py
|
||||
"""
|
||||
|
||||
from .graph import PortfolioBlock, SignalMorphism, PlanDelta, DualVariables
|
||||
|
||||
__all__ = [
|
||||
"PortfolioBlock",
|
||||
"SignalMorphism",
|
||||
"PlanDelta",
|
||||
"DualVariables",
|
||||
]
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
@dataclass
|
||||
class VenueAdapter:
|
||||
"""A lightweight scaffold for a venue adapter.
|
||||
Real implementations would wire TLS, endpoints, and data schemas here.
|
||||
"""
|
||||
|
||||
name: str
|
||||
endpoint: str
|
||||
credentials: Dict[str, Any] | None = None
|
||||
|
||||
def connect(self) -> bool:
|
||||
# Placeholder connect logic
|
||||
return True
|
||||
|
||||
def fetch_signal(self, symbol: str) -> Any:
|
||||
# Placeholder signal fetch
|
||||
return None
|
||||
|
||||
|
||||
__all__ = ["VenueAdapter"]
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class PortfolioBlock:
|
||||
"""Local optimization block representing a portfolio subproblem.
|
||||
|
||||
- assets: list of asset identifiers
|
||||
- objective: dict describing a quadratic/linear objective, e.g. {"type": "quad", "Q": [[...]], "c": [...]}
|
||||
- constraints: list of constraint descriptors (kept lightweight for MVP)
|
||||
- id: unique identifier
|
||||
- signals: current signals (e.g., prices, greeks) from Morphisms
|
||||
"""
|
||||
|
||||
id: str
|
||||
assets: List[str]
|
||||
objective: Dict[str, Any]
|
||||
constraints: List[Dict[str, Any]] = field(default_factory=list)
|
||||
signals: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def add_signal(self, key: str, value: Any) -> None:
|
||||
self.signals[key] = value
|
||||
|
||||
def __post_init__(self):
|
||||
# basic validation
|
||||
if not self.id:
|
||||
raise ValueError("PortfolioBlock requires a non-empty id")
|
||||
|
||||
|
||||
@dataclass
|
||||
class SignalMorphism:
|
||||
"""Represents a data channel carrying signals between blocks."""
|
||||
|
||||
name: str
|
||||
payload: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def push(self, key: str, value: Any) -> None:
|
||||
self.payload[key] = value
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlanDelta:
|
||||
"""Incremental plan change with version metadata."""
|
||||
|
||||
delta: Dict[str, Any]
|
||||
timestamp: float
|
||||
author: Optional[str] = None
|
||||
contract_id: Optional[str] = None
|
||||
privacy_budget: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class DualVariables:
|
||||
"""Optimization signals (e.g., Lagrange multipliers)."""
|
||||
|
||||
multipliers: Dict[str, float] = field(default_factory=dict)
|
||||
|
||||
def update(self, key: str, value: float) -> None:
|
||||
self.multipliers[key] = float(value)
|
||||
|
||||
|
||||
__all__ = ["PortfolioBlock", "SignalMorphism", "PlanDelta", "DualVariables"]
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def _project_onto_simplex(v: np.ndarray) -> np.ndarray:
|
||||
# Projection of v onto the probability simplex { x >= 0, sum x = 1 }
|
||||
n = v.shape[0]
|
||||
u = np.sort(v)[::-1]
|
||||
cssv = np.cumsum(u)
|
||||
rho = -1
|
||||
for j in range(n):
|
||||
t = (cssv[j] - 1) / (j + 1)
|
||||
if u[j] > t:
|
||||
rho = j
|
||||
if rho == -1:
|
||||
theta = 0
|
||||
else:
|
||||
theta = (cssv[rho] - 1) / (rho + 1)
|
||||
w = np.maximum(v - theta, 0)
|
||||
return w
|
||||
|
||||
|
||||
def admm_like_solve(Q: np.ndarray, c: np.ndarray, x0: np.ndarray | None = None, max_iter: int = 200, tol: float = 1e-6) -> np.ndarray:
|
||||
"""A tiny ADMM-like iterative solver for a simple quadratic program:
|
||||
minimize (1/2) x^T Q x + c^T x
|
||||
subject to x in the simplex: x >= 0, sum(x) = 1
|
||||
This is a lightweight, edge-friendly prototype and should be viewed as a semantic placeholder
|
||||
for an actual solver in the MVP.
|
||||
"""
|
||||
n = Q.shape[0]
|
||||
if x0 is None:
|
||||
x = np.ones(n) / n
|
||||
else:
|
||||
x = x0.copy()
|
||||
|
||||
# simple gradient step with projection to simplex
|
||||
alpha = 0.1
|
||||
for _ in range(max_iter):
|
||||
grad = Q.dot(x) + c
|
||||
x_new = x - alpha * grad
|
||||
x_new = _project_onto_simplex(x_new)
|
||||
if np.linalg.norm(x_new - x) < tol:
|
||||
x = x_new
|
||||
break
|
||||
x = x_new
|
||||
return x
|
||||
|
||||
|
||||
__all__ = ["admm_like_solve"]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Ensure Python imports from our source tree
|
||||
# Guard against unbound PYTHONPATH when running with 'set -u'.
|
||||
# If PYTHONPATH is already set, prepend our src directory; otherwise just use src.
|
||||
export PYTHONPATH="${PWD}/src:${PYTHONPATH:-}"
|
||||
|
||||
echo "Running Python tests..."
|
||||
echo "Installing package dependencies..."
|
||||
python3 -m pip install --upgrade pip setuptools wheel >/dev/null 2>&1 || true
|
||||
python3 -m pip install -e . >/dev/null 2>&1 || true
|
||||
pytest -q
|
||||
|
||||
echo "Building package (verification step)…"
|
||||
python3 -m build
|
||||
|
||||
echo "All tests passed and package built."
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import numpy as np
|
||||
|
||||
from algograph_algebraic_portfolio_compiler_f.graph import PortfolioBlock, SignalMorphism, PlanDelta, DualVariables
|
||||
|
||||
|
||||
def test_portfolio_block_creation():
|
||||
block = PortfolioBlock(id="pb1", assets=["AAPL", "GOOGL"], objective={"type": "quad", "Q": [[1, 0], [0, 1]], "c": [0, 0]})
|
||||
assert block.id == "pb1"
|
||||
assert block.assets == ["AAPL", "GOOGL"]
|
||||
assert block.signals == {}
|
||||
|
||||
|
||||
def test_signal_morphism_basic():
|
||||
s = SignalMorphism(name="price_channel")
|
||||
s.push("AAPL", 150.0)
|
||||
assert s.payload["AAPL"] == 150.0
|
||||
|
||||
|
||||
def test_plan_delta_and_dual_variables():
|
||||
pv = PlanDelta(delta={"allocate": [0.1, 0.9]}, timestamp=1.0, author="tester")
|
||||
dv = DualVariables()
|
||||
dv.update("lambda", 0.5)
|
||||
assert pv.delta["allocate"] == [0.1, 0.9]
|
||||
assert dv.multipliers["lambda"] == 0.5
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import numpy as np
|
||||
|
||||
from algograph_algebraic_portfolio_compiler_f.solver import admm_like_solve
|
||||
|
||||
|
||||
def test_admm_like_solve_basic():
|
||||
# Simple 3-asset problem: minimize (1/2)x^T I x + sum(x) with simplex constraint
|
||||
Q = np.eye(3)
|
||||
c = np.ones(3)
|
||||
x = admm_like_solve(Q, c, max_iter=300, tol=1e-6)
|
||||
assert x.shape == (3,)
|
||||
assert np.isclose(np.sum(x), 1.0, atol=1e-6)
|
||||
assert np.all(x >= -1e-8)
|
||||
Loading…
Reference in New Issue