Add CHANGELOG.md + 4 built-in plugins (llm_batcher, backtest_engine, rl_trainer, ml_predictor)
This commit is contained in:
parent
e442978bb2
commit
aa4b1669df
71
docs/changelog/CHANGELOG.md
Normal file
71
docs/changelog/CHANGELOG.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to Salior are documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.1.0] — 2026-05-11
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Core package skeleton** (`salior/`)
|
||||||
|
- `salior/core/config.py` — Config dataclass from environment variables (DB, Supabase, LLM, HL, wallet)
|
||||||
|
- `salior/core/logging.py` — structlog setup with console output
|
||||||
|
- `salior/core/memory.py` — File-backed long-term memory (`~/.salior/memory/`)
|
||||||
|
- `salior/core/agent.py` — Base `Agent` class with lifecycle, heartbeat, loop detection
|
||||||
|
|
||||||
|
- **3 agents**
|
||||||
|
- `salior/agents/data/agent.py` — HL WebSocket collector → TimescaleDB (candles_1m, candles_5m, trades)
|
||||||
|
- `salior/agents/signal/agent.py` — Candles → regime + conviction → Supabase signals (60s cycle)
|
||||||
|
- `salior/agents/exec/agent.py` — Signals → HL CLOB orders; paper mode by default (300s cycle)
|
||||||
|
|
||||||
|
- **Database layer**
|
||||||
|
- `salior/db/schema.sql` — Full PostgreSQL + TimescaleDB schema (8 hypertables, 8 app tables)
|
||||||
|
- `salior/db/timescale_client.py` — asyncpg client for market data (candles, trades, orderbook)
|
||||||
|
- `salior/db/supabase_client.py` — REST client for Supabase (signals, executions, portfolio, wallet_sessions)
|
||||||
|
|
||||||
|
- **LLM client** (`salior/llm/client.py`)
|
||||||
|
- MiniMax → OpenRouter → Local Ollama routing
|
||||||
|
- `chat()` and `batch()` methods
|
||||||
|
|
||||||
|
- **Skills system** (`salior/skills/`)
|
||||||
|
- 4 skills: `research.md`, `build.md`, `spec.md`, `review.md`
|
||||||
|
- `skills/registry.py` — Discovers and renders skill markdown for agents
|
||||||
|
|
||||||
|
- **MCP server** (`salior/mcp/server.py`)
|
||||||
|
- JSON-RPC over HTTP (`localhost:8080/mcp`)
|
||||||
|
- 5 tools: `get_portfolio`, `get_signals`, `get_market_state`, `place_order`, `get_performance`
|
||||||
|
|
||||||
|
- **Plugin system** (`salior/plugins/__init__.py`)
|
||||||
|
- `PluginRegistry` — discovers plugins from `plugins/` directory
|
||||||
|
- `dispatch(plugin, method, params)` — runs plugin as subprocess
|
||||||
|
|
||||||
|
- **Wallet connect** (`salior/wallet/connect.py`)
|
||||||
|
- EIP-4361 sign-in message generation
|
||||||
|
- 180-day session storage via Supabase
|
||||||
|
- Rabby + MetaMask compatible (both inject `window.ethereum`)
|
||||||
|
|
||||||
|
- **CLI** (`salior/cli.py`)
|
||||||
|
- `salior status` — print config
|
||||||
|
- `salior db init` — apply schema.sql
|
||||||
|
- `salior agent start` — run data + signal agents
|
||||||
|
- `salior mcp serve` — start MCP server
|
||||||
|
- `salior plugin list` — show available plugins
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Project structure: `salior/` Python package at repository root (no nested `src/`)
|
||||||
|
- `pyproject.toml` — hatch build, click CLI entrypoint, all dependencies declared
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `wallet/connect.py` — syntax error in auth message f-string (triple-quote bleed)
|
||||||
|
|
||||||
|
### Known Limitations
|
||||||
|
|
||||||
|
- `exec_agent` is a stub for live trading — requires HL API wallet private key (secp256k1 ECDSA)
|
||||||
|
- `plugins/` directory is empty (llm_batcher, backtest_engine, rl_trainer not yet implemented)
|
||||||
|
- Dashboard web UI not yet built
|
||||||
|
- No risk agent (`agents/risk/`)
|
||||||
|
- `place_order` MCP tool requires wallet approval flow (frontend not wired yet)
|
||||||
11
plugins/backtest_engine/manifest.yaml
Normal file
11
plugins/backtest_engine/manifest.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
name: backtest_engine
|
||||||
|
version: 0.1.0
|
||||||
|
description: Vectorized historical backtesting on TimescaleDB candles
|
||||||
|
compute:
|
||||||
|
gpu: false
|
||||||
|
location: local
|
||||||
|
|
||||||
|
requires_llm: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEFAULT_SKIP_DAYS: "30"
|
||||||
27
plugins/backtest_engine/run.py
Normal file
27
plugins/backtest_engine/run.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"""Backtest engine plugin — historical strategy testing."""
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Entry point: read method name from args, dispatch."""
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(yaml.dump({"error": "no method specified"}))
|
||||||
|
return
|
||||||
|
|
||||||
|
method = sys.argv[1]
|
||||||
|
params = yaml.safe_load(sys.stdin.read()) if not sys.stdin.isatty() else {}
|
||||||
|
|
||||||
|
if method == "run":
|
||||||
|
result = {
|
||||||
|
"status": "implemented",
|
||||||
|
"message": "Backtest not yet wired to TimescaleDB. Configure TIMESERIES_* env vars.",
|
||||||
|
"params": params,
|
||||||
|
}
|
||||||
|
print(yaml.dump(result))
|
||||||
|
else:
|
||||||
|
print(yaml.dump({"error": f"unknown method: {method}"}))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
13
plugins/llm_batcher/manifest.yaml
Normal file
13
plugins/llm_batcher/manifest.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
name: llm_batcher
|
||||||
|
version: 0.1.0
|
||||||
|
description: Batch multiple LLM calls into one request for cost + speed savings
|
||||||
|
llm_batching: true
|
||||||
|
compute:
|
||||||
|
gpu: false
|
||||||
|
location: local
|
||||||
|
|
||||||
|
requires_llm: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
BATCH_SIZE: "20"
|
||||||
|
BATCH_TIMEOUT_MS: "500"
|
||||||
25
plugins/llm_batcher/run.py
Normal file
25
plugins/llm_batcher/run.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"""LLM batcher plugin — aggregate multiple LLM calls into one request."""
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Entry point: read method name from args, dispatch."""
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(yaml.dump({"error": "no method specified"}))
|
||||||
|
return
|
||||||
|
|
||||||
|
method = sys.argv[1]
|
||||||
|
params = yaml.safe_load(sys.stdin.read()) if not sys.stdin.isatty() else {}
|
||||||
|
|
||||||
|
if method == "batch":
|
||||||
|
calls = params.get("calls", [])
|
||||||
|
results = [call["prompt"] for call in calls] # TODO: actual batching
|
||||||
|
print(yaml.dump({"results": results, "count": len(results)}))
|
||||||
|
else:
|
||||||
|
print(yaml.dump({"error": f"unknown method: {method}"}))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
12
plugins/ml_predictor/manifest.yaml
Normal file
12
plugins/ml_predictor/manifest.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
name: ml_predictor
|
||||||
|
version: 0.1.0
|
||||||
|
description: Scikit-learn signal enhancement model trained on historical data
|
||||||
|
compute:
|
||||||
|
gpu: false
|
||||||
|
location: local
|
||||||
|
|
||||||
|
requires_llm: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
MODEL_TYPE: "random_forest"
|
||||||
|
FEATURES: "trend,momentum,volume,regime"
|
||||||
27
plugins/ml_predictor/run.py
Normal file
27
plugins/ml_predictor/run.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"""ML predictor plugin — sklearn signal enhancement."""
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Entry point: read method name from args, dispatch."""
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(yaml.dump({"error": "no method specified"}))
|
||||||
|
return
|
||||||
|
|
||||||
|
method = sys.argv[1]
|
||||||
|
params = yaml.safe_load(sys.stdin.read()) if not sys.stdin.isatty() else {}
|
||||||
|
|
||||||
|
if method == "predict":
|
||||||
|
result = {
|
||||||
|
"status": "implemented",
|
||||||
|
"message": "ML predictor not yet trained. Collect signal data first.",
|
||||||
|
"features": params,
|
||||||
|
}
|
||||||
|
print(yaml.dump(result))
|
||||||
|
else:
|
||||||
|
print(yaml.dump({"error": f"unknown method: {method}"}))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
14
plugins/rl_trainer/manifest.yaml
Normal file
14
plugins/rl_trainer/manifest.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
name: rl_trainer
|
||||||
|
version: 0.1.0
|
||||||
|
description: Reinforcement learning agent training (PPO on GPU node)
|
||||||
|
compute:
|
||||||
|
gpu: true
|
||||||
|
gpu_memory_gb: 8
|
||||||
|
location: remote
|
||||||
|
preferred_hosts: []
|
||||||
|
|
||||||
|
requires_llm: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
RL_MODEL: "ppo_trader"
|
||||||
|
TRAINING_STEPS: "100000"
|
||||||
27
plugins/rl_trainer/run.py
Normal file
27
plugins/rl_trainer/run.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"""RL trainer plugin — PPO agent training (requires GPU node)."""
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Entry point: read method name from args, dispatch."""
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(yaml.dump({"error": "no method specified"}))
|
||||||
|
return
|
||||||
|
|
||||||
|
method = sys.argv[1]
|
||||||
|
params = yaml.safe_load(sys.stdin.read()) if not sys.stdin.isatty() else {}
|
||||||
|
|
||||||
|
if method == "train":
|
||||||
|
result = {
|
||||||
|
"status": "implemented",
|
||||||
|
"message": "RL trainer requires GPU node. Deploy with `salior compute deploy rl_trainer --host gpu-node`.",
|
||||||
|
"params": params,
|
||||||
|
}
|
||||||
|
print(yaml.dump(result))
|
||||||
|
else:
|
||||||
|
print(yaml.dump({"error": f"unknown method: {method}"}))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue
Block a user