refactor(layout): rename multi_swarm → multi_swarm_core con doppia nidificazione uv workspace
- mv src/multi_swarm → src/multi_swarm_core/multi_swarm_core (member layout) - sed-replace globale degli import: from/import multi_swarm.* → multi_swarm_core.* - 115 occorrenze aggiornate in src/ scripts/ tests/ - multi_swarm_coevolutive (nome repo) preservato dal word boundary Pre-condizione per il setup uv workspace della Fase 3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,13 +17,13 @@ import math
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from multi_swarm.agents.adversarial import AdversarialAgent
|
||||
from multi_swarm.agents.falsification import FalsificationAgent
|
||||
from multi_swarm.cerbero.client import CerberoClient
|
||||
from multi_swarm.config import load_settings
|
||||
from multi_swarm.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest
|
||||
from multi_swarm.protocol.parser import parse_strategy
|
||||
from multi_swarm.protocol.validator import validate_strategy
|
||||
from multi_swarm_core.agents.adversarial import AdversarialAgent
|
||||
from multi_swarm_core.agents.falsification import FalsificationAgent
|
||||
from multi_swarm_core.cerbero.client import CerberoClient
|
||||
from multi_swarm_core.config import load_settings
|
||||
from multi_swarm_core.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest
|
||||
from multi_swarm_core.protocol.parser import parse_strategy
|
||||
from multi_swarm_core.protocol.validator import validate_strategy
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
"""Replay diagnostico: per ciascuna strategia conta quanti bar avrebbero
|
||||
soddisfatto le condizioni di ciascuna regola sull'ultimo `--days` di storico.
|
||||
|
||||
Ouput tabellare per branch: total_bars, fires, fire_rate, primo/ultimo fire.
|
||||
Esegue anche un backtest grezzo (entry-on-signal, exit-on-flat) per stimare
|
||||
n_trades e total_return realistici nel periodo.
|
||||
|
||||
Esempio:
|
||||
docker compose exec multi-swarm-paper \
|
||||
python /app/scripts/replay_strategies_window.py --days 30
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from multi_swarm_core.cerbero.client import CerberoClient
|
||||
from multi_swarm_core.config import load_settings
|
||||
from multi_swarm_core.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest
|
||||
from multi_swarm_core.protocol.compiler import _eval_node, compile_strategy
|
||||
from multi_swarm_core.protocol.parser import parse_strategy
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("--days", type=int, default=30)
|
||||
p.add_argument("--strategies-dir", default=str(PROJECT_ROOT / "strategies"))
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def fetch_window(loader: CerberoOHLCVLoader, symbol: str, days: int) -> pd.DataFrame:
|
||||
end = datetime.now(UTC).replace(minute=0, second=0, microsecond=0)
|
||||
start = end - timedelta(days=days)
|
||||
req = OHLCVRequest(
|
||||
symbol=symbol, timeframe="1h", start=start, end=end, exchange="deribit"
|
||||
)
|
||||
return loader._fetch(req) # noqa: SLF001 — bypass cache
|
||||
|
||||
|
||||
def per_branch_fires(strategy_path: Path, ohlcv: pd.DataFrame) -> list[dict]:
|
||||
raw = strategy_path.read_text()
|
||||
parsed = parse_strategy(raw)
|
||||
out = []
|
||||
for idx, rule in enumerate(parsed.rules):
|
||||
cond_series = _eval_node(rule.condition, ohlcv).fillna(False).astype(bool)
|
||||
n = int(cond_series.sum())
|
||||
first = ohlcv.index[cond_series.argmax()] if n > 0 else None
|
||||
# last fire: argmax on reversed
|
||||
last = ohlcv.index[len(cond_series) - 1 - cond_series[::-1].argmax()] if n > 0 else None
|
||||
out.append({
|
||||
"branch_idx": idx,
|
||||
"action": rule.action,
|
||||
"fires": n,
|
||||
"fire_rate_pct": round(100.0 * n / len(ohlcv), 2),
|
||||
"first_fire": first,
|
||||
"last_fire": last,
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
def quick_pnl(strategy_path: Path, ohlcv: pd.DataFrame, fees_bp: float = 5.0) -> dict:
|
||||
"""Approx: at each bar evaluate compiled signal series (long/short/flat),
|
||||
apply position to next-bar return, charge fees on changes. No leverage."""
|
||||
raw = strategy_path.read_text()
|
||||
parsed = parse_strategy(raw)
|
||||
sig_fn = compile_strategy(parsed)
|
||||
signals = sig_fn(ohlcv) # series of "long"/"short"/"flat"
|
||||
# map to position: long=+1, short=-1, flat=0
|
||||
pos = signals.map({"long": 1, "short": -1, "flat": 0}).fillna(0).astype(int)
|
||||
rets = ohlcv["close"].pct_change().fillna(0.0)
|
||||
# next-bar execution: position decided at bar t applies to return t+1 -> shift
|
||||
pnl = pos.shift(1).fillna(0) * rets
|
||||
# fees on position changes
|
||||
changes = pos.diff().abs().fillna(0).astype(int)
|
||||
fee_per_change = fees_bp / 10_000.0
|
||||
pnl_after_fees = pnl - changes * fee_per_change
|
||||
cum = (1 + pnl_after_fees).prod() - 1
|
||||
n_trades = int((changes > 0).sum())
|
||||
time_in_market = float((pos != 0).mean())
|
||||
return {
|
||||
"n_trades": n_trades,
|
||||
"total_return_pct": round(100.0 * float(cum), 3),
|
||||
"time_in_market_pct": round(100.0 * time_in_market, 2),
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
settings = load_settings()
|
||||
token = (
|
||||
settings.cerbero_mainnet_token.get_secret_value()
|
||||
if settings.cerbero_mainnet_token
|
||||
else settings.cerbero_testnet_token.get_secret_value()
|
||||
)
|
||||
cerbero = CerberoClient(
|
||||
base_url=settings.cerbero_base_url,
|
||||
token=token,
|
||||
bot_tag=settings.cerbero_bot_tag,
|
||||
)
|
||||
loader = CerberoOHLCVLoader(client=cerbero, cache_dir=settings.series_dir)
|
||||
|
||||
strategies_dir = Path(args.strategies_dir)
|
||||
pairs = [
|
||||
("BTC-PERPETUAL", sorted(strategies_dir.glob("btc_*.json"))[0]),
|
||||
("ETH-PERPETUAL", sorted(strategies_dir.glob("eth_*.json"))[0]),
|
||||
]
|
||||
|
||||
for symbol, strat_path in pairs:
|
||||
print(f"\n=== {symbol} strategy={strat_path.name} window={args.days}d ===")
|
||||
ohlcv = fetch_window(loader, symbol, args.days)
|
||||
print(f"bars: {len(ohlcv)} range: {ohlcv.index[0]} -> {ohlcv.index[-1]}")
|
||||
print("\n-- per branch --")
|
||||
for row in per_branch_fires(strat_path, ohlcv):
|
||||
print(json.dumps(row, default=str))
|
||||
print("\n-- quick pnl (next-bar exec, fees=5bp) --")
|
||||
print(json.dumps(quick_pnl(strat_path, ohlcv), default=str))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -25,13 +25,13 @@ from dataclasses import dataclass
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
from multi_swarm.cerbero.client import CerberoClient
|
||||
from multi_swarm.config import load_settings
|
||||
from multi_swarm.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest
|
||||
from multi_swarm.paper_trading.executor import PaperExecutor
|
||||
from multi_swarm.paper_trading.persistence import PaperRepository
|
||||
from multi_swarm.paper_trading.portfolio import Portfolio
|
||||
from multi_swarm.persistence.repository import Repository
|
||||
from multi_swarm_core.cerbero.client import CerberoClient
|
||||
from multi_swarm_core.config import load_settings
|
||||
from multi_swarm_core.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest
|
||||
from multi_swarm_core.paper_trading.executor import PaperExecutor
|
||||
from multi_swarm_core.paper_trading.persistence import PaperRepository
|
||||
from multi_swarm_core.paper_trading.portfolio import Portfolio
|
||||
from multi_swarm_core.persistence.repository import Repository
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ from __future__ import annotations
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
from multi_swarm.cerbero.client import CerberoClient
|
||||
from multi_swarm.config import load_settings
|
||||
from multi_swarm.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest
|
||||
from multi_swarm.genome.hypothesis import ModelTier
|
||||
from multi_swarm.llm.client import LLMClient
|
||||
from multi_swarm.orchestrator.run import RunConfig, run_phase1
|
||||
from multi_swarm_core.cerbero.client import CerberoClient
|
||||
from multi_swarm_core.config import load_settings
|
||||
from multi_swarm_core.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest
|
||||
from multi_swarm_core.genome.hypothesis import ModelTier
|
||||
from multi_swarm_core.llm.client import LLMClient
|
||||
from multi_swarm_core.orchestrator.run import RunConfig, run_phase1
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
|
||||
@@ -6,9 +6,9 @@ from pathlib import Path
|
||||
import numpy as np
|
||||
import pandas as pd # type: ignore[import-untyped]
|
||||
|
||||
from multi_swarm.genome.hypothesis import HypothesisAgentGenome, ModelTier
|
||||
from multi_swarm.llm.client import CompletionResult
|
||||
from multi_swarm.orchestrator.run import RunConfig, run_phase1
|
||||
from multi_swarm_core.genome.hypothesis import HypothesisAgentGenome, ModelTier
|
||||
from multi_swarm_core.llm.client import CompletionResult
|
||||
from multi_swarm_core.orchestrator.run import RunConfig, run_phase1
|
||||
|
||||
_MOCK_STRATEGY = json.dumps(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user