Compare commits
8 Commits
main
..
7f2db19a7c
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f2db19a7c | |||
| 369a77b5cf | |||
| 2aa5646aeb | |||
| 6a9e2c28b1 | |||
| 37558a34f5 | |||
| 14f476dd09 | |||
| 05c7b5e89e | |||
| 6655e425fa |
@@ -47,3 +47,6 @@ logs/
|
||||
build/
|
||||
dist/
|
||||
*.egg
|
||||
|
||||
# Pythagoras source PDFs (local only, not tracked)
|
||||
src/strategy_pythagoras/Pythagoras/*.pdf
|
||||
|
||||
+2
-2
@@ -88,8 +88,8 @@ services:
|
||||
<<: *swarm-env
|
||||
DASHBOARD_ROOT_PATH: /strategy_crypto_gui
|
||||
volumes:
|
||||
# Dashboard legge solo strategy_crypto.db: state/ in read-only (WAL: vedi nota)
|
||||
- ./state:/app/state:ro
|
||||
# RW richiesto: SQLite WAL mode richiede write-access dal reader per SHM.
|
||||
- ./state:/app/state
|
||||
entrypoint:
|
||||
- python
|
||||
- -m
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,343 @@
|
||||
# strategy_pythagoras — Design
|
||||
|
||||
**Data:** 2026-05-19
|
||||
**Autore design:** Adriano Dal Pastro (con Claude/brainstorming)
|
||||
**Stato:** Design approvato per parti 1-4, in attesa di review utente sulla spec consolidata
|
||||
**Audience:** implementatori (Claude executor o umano)
|
||||
|
||||
## 0. Riassunto esecutivo
|
||||
|
||||
Nuovo workspace member `strategy_pythagoras` parallelo a `strategy_crypto`. Replica il pattern coevolutivo GA del monorepo applicato a un dominio diverso: la **scoperta di pattern frattali ricorrenti** sui mercati crypto secondo il framework Pythagoras-Malanga (Fourier + frattali + geometria Evideon), descritto nei due PDF in `src/strategy_pythagoras/Pythagoras/`.
|
||||
|
||||
**Non cambia il genoma del core:** gli agenti restano `HypothesisAgentGenome` e producono `Strategy JSON` nella stessa DSL S-expression. Cambiano:
|
||||
- 7 stili cognitivi Pythagoras-themed (in `prompts.json`)
|
||||
- 3 nuovi indicatori candle (`candle_pattern`, `pythagorean_ratio`, `fractal_mirror`)
|
||||
- Fitness con bonus di asset-invariance BTC↔ETH
|
||||
- Output: strategie JSON freezate + dashboard NiceGUI + report markdown di analisi cross-book
|
||||
|
||||
**Deliverable di chiusura task:** scaffolding completo + 1 GA run short (smoke test) + `docs/analysis_first_run.md` con sintesi numerica e top winners.
|
||||
|
||||
---
|
||||
|
||||
## 1. Architettura
|
||||
|
||||
### Layout package
|
||||
|
||||
```
|
||||
src/strategy_pythagoras/
|
||||
├── pyproject.toml # workspace member, dipende da multi-swarm-core
|
||||
├── README.md
|
||||
├── tests/
|
||||
│ └── test_indicators.py # unit-test per i 3 nuovi indicatori
|
||||
└── strategy_pythagoras/
|
||||
├── __init__.py
|
||||
├── prompts.json # 7 stili Pythagoras-aligned (schema v3.2 di strategy_crypto)
|
||||
├── indicators.py # candle_pattern, pythagorean_ratio, fractal_mirror
|
||||
├── backend/
|
||||
│ ├── __init__.py
|
||||
│ ├── schema.py # tabelle paper_trading_*
|
||||
│ ├── executor.py # PaperExecutor (port da strategy_crypto)
|
||||
│ ├── portfolio.py # Portfolio (port da strategy_crypto)
|
||||
│ └── persistence.py # PaperRepository
|
||||
├── frontend/
|
||||
│ ├── __init__.py
|
||||
│ ├── nicegui_app.py # /strategy_pythagoras_gui
|
||||
│ └── data.py # dual-reader: GA db + paper db + invariance metrics
|
||||
└── strategies/ # JSON winners shippati col package
|
||||
└── (vuoto al t0)
|
||||
```
|
||||
|
||||
### Workspace registration
|
||||
|
||||
In `pyproject.toml` (root):
|
||||
```toml
|
||||
[tool.uv.workspace]
|
||||
members = ["src/multi_swarm_core", "src/strategy_crypto", "src/strategy_pythagoras"]
|
||||
|
||||
[tool.uv.sources]
|
||||
strategy-pythagoras = { workspace = true }
|
||||
```
|
||||
|
||||
Aggiungere `strategy-pythagoras` a `[project].dependencies` per il deployable root.
|
||||
|
||||
### Riuso
|
||||
|
||||
| Componente | Source | Note |
|
||||
|---|---|---|
|
||||
| GA loop, mutation, crossover | `multi_swarm_core.ga` | invariato |
|
||||
| Protocol parser/validator/compiler | `multi_swarm_core.protocol` | esteso con 3 indicatori (vedi §2) |
|
||||
| Backtest engine | `multi_swarm_core.backtest` | invariato |
|
||||
| LLM / OpenRouter / Anthropic clients | `multi_swarm_core.llm` | invariato |
|
||||
| PaperExecutor + Portfolio | `strategy_crypto.backend` | **port** (non import), per isolamento DB |
|
||||
| NiceGUI dashboard shell | `strategy_crypto.frontend` | **port** + adatta tabs |
|
||||
|
||||
### Persistence
|
||||
|
||||
```
|
||||
state/strategy_pythagoras.db # GA: genomi, generazioni, fitness history
|
||||
state/strategy_pythagoras_paper.db # paper-trading post-deploy
|
||||
strategy_pythagoras/strategies/ # JSON winners shippati
|
||||
docs/analysis_first_run.md # report cross-book
|
||||
docs/analysis_runs/<run-id>/ # per-run dump
|
||||
```
|
||||
|
||||
Env vars:
|
||||
- `STRATEGY_PYTHAGORAS_DB_PATH` (default `state/strategy_pythagoras.db`)
|
||||
- `STRATEGY_PYTHAGORAS_PAPER_DB_PATH` (default `state/strategy_pythagoras_paper.db`)
|
||||
- `GA_INVARIANCE_ALPHA` (default 0.3)
|
||||
- `GA_INVARIANCE_TOLERANCE_BARS` (default 36 = ±3h su 5m TF)
|
||||
|
||||
### Routing GUI
|
||||
|
||||
`/strategy_pythagoras_gui` (allinea a [[gui_subpath_routing]] in memory: ogni asset GUI su subpath, root dominio libera).
|
||||
|
||||
### Docker (Phase 2, fuori scope di questa spec)
|
||||
|
||||
Servizi paralleli a quelli di strategy_crypto:
|
||||
- `strategy-pythagoras-paper` (runner)
|
||||
- `strategy-pythagoras-gui` (dashboard)
|
||||
Rete Traefik external, bind mount uid 1000 (vedi [[production_deployment]]).
|
||||
|
||||
---
|
||||
|
||||
## 2. Genoma e DSL
|
||||
|
||||
### Il genoma non cambia
|
||||
|
||||
`HypothesisAgentGenome` di `multi_swarm_core.genome.hypothesis` resta identico:
|
||||
- `system_prompt: str`
|
||||
- `feature_access: list[str]`
|
||||
- `temperature: float`
|
||||
- `top_p: float`
|
||||
- `model_tier: ModelTier`
|
||||
- `lookback_window: int` — vincolo 12 ≤ lw ≤ 200
|
||||
- `cognitive_style: str` — uno dei 7 nuovi stili
|
||||
- `parent_ids, generation, id` — invariati
|
||||
|
||||
### 3 nuovi indicatori (`strategy_pythagoras/indicators.py`)
|
||||
|
||||
| Nome | Params (JSON) | Output | Semantica operativa |
|
||||
|---|---|---|---|
|
||||
| `candle_pattern` | `[seq_str]` es. `"UDU"`, `"UUD0U"` | 1.0 se le ultime k=len(seq_str) candele matchano la sequenza, 0.0 altrimenti | `"U"` = close>open; `"D"` = close<open; `"0"` = `abs(close-open)/open < 0.001` |
|
||||
| `pythagorean_ratio` | `[lookback: int]` | float = `max(close[-lookback:]) / min(close[-lookback:])` | Ratio prezzo, da confrontare con literal vicini a φ=1.618, π=3.1416, √2=1.414, e=2.718 |
|
||||
| `fractal_mirror` | `[k: int, axis: str]` axis ∈ `{"h","v"}` | float ∈ [-1, +1] | Correlazione di Pearson tra ultime k candele e loro mirror: `"h"` = mirror tempo (inversione sequenza); `"v"` = mirror prezzo (1 - close/max) |
|
||||
|
||||
Vincoli del compiler:
|
||||
- `candle_pattern`: `len(seq_str)` ∈ [3, 12], simboli ∈ `{U,D,0}`
|
||||
- `pythagorean_ratio`: `lookback` ∈ [12, 200]
|
||||
- `fractal_mirror`: `k` ∈ [3, 12], `axis` ∈ `{"h","v"}`
|
||||
|
||||
Tutti e 3 vanno aggiunti a `KNOWN_INDICATORS` in `multi_swarm_core.protocol.grammar`.
|
||||
|
||||
### Esempio strategy JSON tipica
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"condition": {
|
||||
"op": "and",
|
||||
"args": [
|
||||
{"op": "eq", "args": [
|
||||
{"kind": "indicator", "name": "candle_pattern", "params": ["UDDU"]},
|
||||
{"kind": "literal", "value": 1.0}
|
||||
]},
|
||||
{"op": "gt", "args": [
|
||||
{"kind": "indicator", "name": "pythagorean_ratio", "params": [89]},
|
||||
{"kind": "literal", "value": 1.618}
|
||||
]}
|
||||
]
|
||||
},
|
||||
"action": "entry-long"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"op": "or", "args": [
|
||||
{"op": "gt", "args": [
|
||||
{"kind": "indicator", "name": "pythagorean_ratio", "params": [89]},
|
||||
{"kind": "literal", "value": 2.618}
|
||||
]},
|
||||
{"op": "crossunder", "args": [
|
||||
{"kind": "indicator", "name": "fractal_mirror", "params": [12, "h"]},
|
||||
{"kind": "literal", "value": 0.0}
|
||||
]}
|
||||
]
|
||||
},
|
||||
"action": "exit"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Vincoli protettivi (anti-overfitting)
|
||||
|
||||
- `lookback_window ≤ 200`
|
||||
- `candle_pattern` seq length ∈ [3, 12] (range 1+2 del Libro dei Frattali)
|
||||
- `time_in_market` monitorato come metric (red flag [[selectivity_red_flag]]); non hard-gate al primo run
|
||||
- Letterali con `pythagorean_ratio`: max 4 decimali (no `1.6180339`)
|
||||
- Max 4 condizioni in AND per regola (eredita da prompts.json)
|
||||
|
||||
---
|
||||
|
||||
## 3. Stili cognitivi (`strategy_pythagoras/prompts.json`)
|
||||
|
||||
### Schema
|
||||
|
||||
Schema v3.2 identico a `strategy_crypto/prompts.json` (campi `_schema`, `_changelog`, `_design_invariants`, `agent_role`, `pattern_guidance`, `instruction`, `domain_warnings`, `anti_patterns`, `output_priorities`, `styles`).
|
||||
|
||||
### `agent_role`
|
||||
|
||||
> Sei un agente di un sistema swarm coevolutivo che cerca pattern frattali ricorrenti sui mercati crypto secondo il framework Pythagoras-Malanga (frattali H-C, trasformata di Fourier, geometria Evideon). Sei parte di una popolazione che esplora collettivamente lo spazio dei pattern: la diversità delle ipotesi è un asset critico. Preferisci esplorare strutture meno ovvie per la tua lente cognitiva. La strategia che produci deve essere riconoscibile come emanata dal tuo stile.
|
||||
|
||||
### `pattern_guidance` (specifico Pythagoras)
|
||||
|
||||
- Sequenze candle 3-12 lunghezza in alfabeto `{U,D,0}` via `candle_pattern`
|
||||
- Mirror H/V come operatori di proiezione via `fractal_mirror`
|
||||
- Ratios di prezzo vicini a φ=1.618, 1/φ=0.618, √2=1.414, π/2=1.571, e/2=1.359 entro tolleranza 0.5%
|
||||
- Pattern composti: pattern lunghi (6-12) come concatenazione di pattern corti (Pattern 13 = Pattern 3 + Pattern 11, p. 53 del paper)
|
||||
- Cicli ricorrenti: stesso pattern firato a distanze regolari (Poincaré)
|
||||
|
||||
### `domain_warnings`
|
||||
|
||||
- Crypto 24/7, no CME gap
|
||||
- I "numeri sacri" (Solfeggio 396-852 Hz, 137.0359, etc.) sono prior teorici, NON leggi: usali come scale candidate, non come dogma
|
||||
- Il paper Pythagoras è esplicitamente non-falsificabile (cita "consapevolezza del trader" come jolly per il fallimento): il backtest è l'unico arbitro
|
||||
- `time_in_market > 80%` red flag (leveraged B&H camuffato)
|
||||
- Tolleranza ±3h del paper → su 5m TF = ±36 barre
|
||||
|
||||
### `anti_patterns`
|
||||
|
||||
- Sequenza `candle_pattern` con `len > 7` simboli vincolati → overfitting
|
||||
- `pythagorean_ratio` con tolleranza > 2% sui literal → numerologia spuriosa
|
||||
- `fractal_mirror` con `k == lookback_window` → tautologico
|
||||
- Letterali con più di 4 decimali
|
||||
- Più di 4 condizioni in AND
|
||||
- Crossover tra indicatori dello stesso tipo con lookback vicini → chattering
|
||||
|
||||
### `output_priorities`
|
||||
|
||||
1. **Coerenza con lente cognitiva** (es. `pythagorean` usa ratios, `candle_grammarian` usa sequenze esplicite)
|
||||
2. **Asset-invariance** (segnali che attivano sia su BTC che su ETH entro ±36 barre)
|
||||
3. **Selettività** (poche entry forti)
|
||||
4. **Composizionalità** (pattern lunghi come somma di corti)
|
||||
5. **Robustezza vs random baseline** (3σ richiesto da `skeptic_quant`)
|
||||
|
||||
### I 7 stili (`styles`)
|
||||
|
||||
Ogni stile mantiene shape v3.2 (directive 800-950 char ASCII-safe, `focus_metrics` 4 voci, ultimo periodo = "Archetipo dominante: X.", lookback range esplicito):
|
||||
|
||||
| `cognitive_style` | Archetipo / Metafora ancorante | `focus_metrics` | Lookback consigliato |
|
||||
|---|---|---|---|
|
||||
| `pythagorean` | Armonia di ratios sacri (φ, π, √2) | `pythagorean_ratio`, `candle_pattern`, `sma_pct`, `realized_vol` | 89-144 |
|
||||
| `fractal_geometer` | Autosimilarità: pattern di 3 candele si ripetono dilatati a 6, 12 | `candle_pattern`, `fractal_mirror`, `atr_pct`, `pythagorean_ratio` | 48-144 |
|
||||
| `fourier_analyst` | Somma di seni: frequenze ricorrenti dominanti | `sma_pct`, `realized_vol`, `candle_pattern`, `atr` | 60-200 |
|
||||
| `evideonic_projector` | Presente = passato proiettato via mirror H+V e scale | `fractal_mirror`, `pythagorean_ratio`, `candle_pattern`, `sma_pct` | 24-96 |
|
||||
| `candle_grammarian` | Lingua di 3 simboli (U,D,0); parole 3-12 lettere | `candle_pattern`, `volume`, `atr`, `realized_vol` | 12-48 |
|
||||
| `recurrence_theorist` | Per Poincaré, eventi tornano: cerca pattern di oggi che firarono ieri | `candle_pattern`, `fractal_mirror`, `pythagorean_ratio`, `sma_pct` | 100-200 |
|
||||
| `skeptic_quant` | Anticorpo all'unfalsifiability: solo edge 3σ vs random | `realized_vol`, `atr_pct`, `sma_pct`, `candle_pattern` | 60-150 |
|
||||
|
||||
Lo `skeptic_quant` è importante: la sua directive richiede esplicitamente che la strategia sia testabile e che il fitness sia confrontato contro random baseline.
|
||||
|
||||
### `_design_invariants`
|
||||
|
||||
Stessa filosofia di v3.2 di strategy_crypto:
|
||||
- ASCII-safe (no Unicode oltre U+007F nelle directive)
|
||||
- Ogni directive chiude con `Archetipo dominante: <metafora>.`
|
||||
- Ogni directive ha range lookback numerico esplicito
|
||||
- Prima frase: `Il mercato e ...`
|
||||
- Lunghezza 800-950 char
|
||||
|
||||
---
|
||||
|
||||
## 4. Fitness, run GA short, deliverable analisi
|
||||
|
||||
### Fitness con bonus invariance
|
||||
|
||||
```
|
||||
fitness(genome) = mean(sharpe_BTC, sharpe_ETH) × (1 + α × invariance_score)
|
||||
|
||||
invariance_score = corr_signal(entries_BTC, entries_ETH, tolerance_bars=GA_INVARIANCE_TOLERANCE_BARS)
|
||||
∈ [0, 1]
|
||||
α = GA_INVARIANCE_ALPHA (default 0.3)
|
||||
```
|
||||
|
||||
`corr_signal` = frazione di entries su BTC che hanno una entry corrispondente su ETH entro ±36 barre (=±3h su 5m TF).
|
||||
|
||||
Implementazione come callback al GA in `multi_swarm_core.ga`, registrata da `strategy_pythagoras` al startup. Il core non sa nulla di Pythagoras: riceve solo la callback.
|
||||
|
||||
### GA run short (smoke test)
|
||||
|
||||
| Parametro | Valore | Note |
|
||||
|---|---|---|
|
||||
| `population_size` | 20 | minimo per 7 stili (~3 per stile) |
|
||||
| `generations` | 5 | smoke test, non training |
|
||||
| `elite_fraction` | 0.2 | top-4 sopravvivono |
|
||||
| `mutation_rate` | 0.3 | invariato vs strategy_crypto |
|
||||
| `crossover_rate` | 0.5 | invariato |
|
||||
| `model_tier` distribuzione | 70% C (qwen-2.5-72b), 30% B (sonnet) | rispetta [[model_qwen_dependency]] |
|
||||
| `dataset` | BTC 5m + ETH 5m da `strategy_crypto/series/` | riusa serie esistenti |
|
||||
| `train_window` | 2024-07 → 2024-12 | copre le date Pythagoras (lug-ago 2024) |
|
||||
| `test_window` | 2025-01 (1 mese) | hold-out per validare invariance |
|
||||
| `name` | `pythagoras-smoke-001` | run id |
|
||||
|
||||
Lo smoke test verifica:
|
||||
1. Workspace member installato in venv (`uv sync` + `uv run python -c "import strategy_pythagoras"`)
|
||||
2. I 3 nuovi indicatori registrati nel grammar e compilabili
|
||||
3. `prompts.json` caricato, 7 stili producono genomi distinti (no collisioni di id)
|
||||
4. Bonus invariance impatta fitness (verifica via log)
|
||||
5. JSON winners atterrano in `strategy_pythagoras/strategies/`
|
||||
6. Dashboard NiceGUI si avvia e legge i due DB
|
||||
|
||||
### Deliverable analisi cross-book
|
||||
|
||||
`docs/analysis_first_run.md` con:
|
||||
|
||||
1. **Sintesi numerica dei riassunti** — riferimenti a `src/strategy_pythagoras/Pythagoras/Pythagoras_Trading_Prediction.summary.md` e `Libro_frattali.summary.md`
|
||||
2. **Top-5 winners** — id, cognitive_style, fitness, sharpe_BTC, sharpe_ETH, invariance_score
|
||||
3. **Pattern frattali emersi** — dump dei `candle_pattern` seq usate, conteggio per stile, % sovrapposizione con spazio teorico dei 57 pattern del Libro
|
||||
4. **Ratios di prezzo emersi** — distribuzione literal usati con `pythagorean_ratio`, distanza dai numeri universali (φ/π/√2/Solfeggio)
|
||||
5. **Cross-asset invariance osservata** — istogramma di `corr_signal` per top genomi
|
||||
6. **Conclusione onesta** — confronto vs random baseline, quanti winners superano sharpe>1.0 su test + invariance>0.3, cosa il framework Pythagoras predice e cosa NON regge al backtest
|
||||
|
||||
Niente "consapevolezza" come jolly. Solo numeri.
|
||||
|
||||
### Dashboard NiceGUI
|
||||
|
||||
`/strategy_pythagoras_gui`:
|
||||
- Tab **Genomes** — winners con stile/sharpe/invariance, click per drill su rules
|
||||
- Tab **Patterns** — heatmap delle sequenze `candle_pattern` emerse, frequenza per stile
|
||||
- Tab **Ratios** — istogramma literal vicini a costanti universali, bins centrati su φ, π, √2, ecc.
|
||||
- Tab **Invariance** — scatter sharpe_BTC vs sharpe_ETH per ogni winner
|
||||
|
||||
---
|
||||
|
||||
## 5. Out-of-scope (esplicito)
|
||||
|
||||
- **Asset oltre BTC/ETH** (Oro/Argento del paper): non in primo run. Estensione futura.
|
||||
- **Range candele oltre 12**: range 3-5 del Libro (12-56 candele). Estensione futura quando lo smoke test conferma stabilità.
|
||||
- **Live trading reale**: solo paper-trading via stesso pattern di strategy_crypto.
|
||||
- **OCR/Vision sulle figure del Libro dei Frattali**: esplicitamente ESCLUSO da request utente ("senza passare alle immagini").
|
||||
- **Modifica del genoma del core**: nessuna modifica a `HypothesisAgentGenome`. Solo extension del grammar (3 indicatori).
|
||||
- **Riferimenti pseudoscientifici operativizzati come legge**: i numeri sacri/Solfeggio sono prior teorici per literal candidati, non vincoli rigidi.
|
||||
|
||||
---
|
||||
|
||||
## 6. Criteri di accettazione
|
||||
|
||||
- [ ] `uv sync` riesce dalla root con `strategy_pythagoras` come member
|
||||
- [ ] `uv run python -c "from strategy_pythagoras.indicators import candle_pattern, pythagorean_ratio, fractal_mirror"` non solleva
|
||||
- [ ] `pytest src/strategy_pythagoras/tests/` verde (almeno unit-test per i 3 indicatori)
|
||||
- [ ] GA short run `pythagoras-smoke-001` completa 5 generazioni senza errori
|
||||
- [ ] Almeno 1 winner con fitness > 0 e `cognitive_style` ∈ {7 stili Pythagoras}
|
||||
- [ ] Dashboard avvia su `http://localhost:PORT/strategy_pythagoras_gui` e mostra winners
|
||||
- [ ] `docs/analysis_first_run.md` esiste e contiene tutte le sezioni elencate in §4
|
||||
|
||||
---
|
||||
|
||||
## 7. Riferimenti
|
||||
|
||||
- [Pythagoras Trading Prediction — riassunto](../../../src/strategy_pythagoras/Pythagoras/Pythagoras_Trading_Prediction.summary.md)
|
||||
- [Libro dei Frattali — riassunto](../../../src/strategy_pythagoras/Pythagoras/Libro_frattali.summary.md)
|
||||
- Memory: [[monorepo_uv_workspace]], [[gui_subpath_routing]], [[ownership_per_modulo]], [[production_deployment]], [[model_qwen_dependency]], [[selectivity_red_flag]]
|
||||
- Template: `src/strategy_crypto/` (paper-trading + GUI), `src/strategy_crypto/strategy_crypto/prompts.json` (schema v3.2)
|
||||
- Core: `src/multi_swarm_core/multi_swarm_core/genome/hypothesis.py`, `multi_swarm_core/protocol/grammar.py`
|
||||
+4
-2
@@ -10,14 +10,16 @@ requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"multi-swarm-core",
|
||||
"strategy-crypto",
|
||||
"strategy-pythagoras",
|
||||
]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["src/multi_swarm_core", "src/strategy_crypto"]
|
||||
members = ["src/multi_swarm_core", "src/strategy_crypto", "src/strategy_pythagoras"]
|
||||
|
||||
[tool.uv.sources]
|
||||
multi-swarm-core = { workspace = true }
|
||||
strategy-crypto = { workspace = true }
|
||||
strategy-pythagoras = { workspace = true }
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
@@ -42,7 +44,7 @@ python_version = "3.13"
|
||||
strict = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["src/multi_swarm_core/tests", "src/strategy_crypto/tests"]
|
||||
testpaths = ["src/multi_swarm_core/tests", "src/strategy_crypto/tests", "src/strategy_pythagoras/tests"]
|
||||
addopts = "-v --tb=short --import-mode=importlib"
|
||||
markers = [
|
||||
"integration: tests that require external services (Cerbero, LLM API)",
|
||||
|
||||
@@ -33,6 +33,36 @@ from strategy_crypto.backend import PaperExecutor, PaperRepository, Portfolio
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Mapping timeframe stringa Cerbero -> minuti del bar. Le strategie tradano
|
||||
# sul "bar appena chiuso", quindi end deve essere snappato al boundary del
|
||||
# loro timeframe (NON sempre al top dell'ora) per evitare la regressione in
|
||||
# cui ETH 5m veniva valutato una volta sola ogni 60 min.
|
||||
_TIMEFRAME_MINUTES: dict[str, int] = {
|
||||
"1m": 1,
|
||||
"5m": 5,
|
||||
"15m": 15,
|
||||
"30m": 30,
|
||||
"1h": 60,
|
||||
"4h": 240,
|
||||
"1d": 1440,
|
||||
}
|
||||
|
||||
|
||||
def _align_end_to_timeframe(now: datetime, timeframe: str) -> datetime:
|
||||
"""Snap ``now`` al boundary del bar timeframe (UTC, naive seconds).
|
||||
|
||||
Es.: now=14:37:42, tf="5m" -> 14:35:00
|
||||
now=14:37:42, tf="1h" -> 14:00:00
|
||||
now=14:00:00, tf="1h" -> 14:00:00
|
||||
"""
|
||||
bar_min = _TIMEFRAME_MINUTES[timeframe]
|
||||
aligned = now.replace(second=0, microsecond=0)
|
||||
if bar_min >= 1440:
|
||||
return aligned.replace(hour=0, minute=0)
|
||||
total_min = aligned.hour * 60 + aligned.minute
|
||||
snapped = (total_min // bar_min) * bar_min
|
||||
return aligned.replace(hour=snapped // 60, minute=snapped % 60)
|
||||
|
||||
|
||||
def _default_strategies_dir() -> Path:
|
||||
"""Cartella JSON shippata col package strategy_crypto."""
|
||||
@@ -131,9 +161,12 @@ def main() -> None:
|
||||
now = datetime.now(UTC)
|
||||
last_prices: dict[str, float] = {}
|
||||
for asset, executor in zip(assets, executors, strict=True):
|
||||
# fetch OHLCV most recent lookback bars
|
||||
end = now.replace(minute=0, second=0, microsecond=0)
|
||||
start = end - timedelta(hours=args.lookback_bars + 1)
|
||||
# fetch OHLCV most recent lookback bars: end snappato al timeframe
|
||||
# dell'asset, non sempre all'ora (altrimenti ETH 5m veniva valutato
|
||||
# solo ogni 60 min, regressione vs backtest tunato 5m).
|
||||
bar_min = _TIMEFRAME_MINUTES[asset.timeframe]
|
||||
end = _align_end_to_timeframe(now, asset.timeframe)
|
||||
start = end - timedelta(minutes=bar_min * (args.lookback_bars + 1))
|
||||
req = OHLCVRequest(
|
||||
symbol=asset.symbol,
|
||||
timeframe=asset.timeframe,
|
||||
|
||||
@@ -263,7 +263,8 @@ def index() -> None:
|
||||
refresh()
|
||||
|
||||
select.on_value_change(on_select_change)
|
||||
ui.timer(REFRESH_INTERVAL_S, refresh)
|
||||
_timer = ui.timer(REFRESH_INTERVAL_S, refresh)
|
||||
ui.context.client.on_disconnect(_timer.deactivate)
|
||||
refresh()
|
||||
|
||||
|
||||
@@ -353,7 +354,8 @@ def convergence() -> None:
|
||||
refresh()
|
||||
|
||||
select.on_value_change(on_select_change)
|
||||
ui.timer(REFRESH_INTERVAL_S, refresh)
|
||||
_timer = ui.timer(REFRESH_INTERVAL_S, refresh)
|
||||
ui.context.client.on_disconnect(_timer.deactivate)
|
||||
refresh()
|
||||
|
||||
|
||||
@@ -535,7 +537,8 @@ def genomes() -> None:
|
||||
select.on_value_change(on_select_change)
|
||||
top_k_select.on_value_change(lambda _: refresh())
|
||||
top_table.on("selection", on_row_selected)
|
||||
ui.timer(REFRESH_INTERVAL_S, refresh)
|
||||
_timer = ui.timer(REFRESH_INTERVAL_S, refresh)
|
||||
ui.context.client.on_disconnect(_timer.deactivate)
|
||||
refresh()
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ from typing import Any
|
||||
import numpy as np
|
||||
import pandas as pd # type: ignore[import-untyped]
|
||||
|
||||
from strategy_pythagoras.indicators import (
|
||||
candle_pattern as _public_candle_pattern,
|
||||
fractal_mirror as _public_fractal_mirror,
|
||||
pythagorean_ratio as _public_pythagorean_ratio,
|
||||
)
|
||||
|
||||
from ..backtest.orders import Side
|
||||
from .parser import (
|
||||
FeatureNode,
|
||||
@@ -126,6 +132,22 @@ def _ind_macd(
|
||||
return macd_line - signal_line
|
||||
|
||||
|
||||
def _ind_candle_pattern(df: pd.DataFrame, *params: float) -> pd.Series:
|
||||
# Adapter: il dispatch in _eval_node fa ``fn(df, *node.params)``, ma la
|
||||
# public API in strategy_pythagoras.indicators accetta ``params: list[float]``
|
||||
# come singolo argomento. Re-pack qui per mantenere indicators.py testabile
|
||||
# in isolamento.
|
||||
return _public_candle_pattern(df, list(params))
|
||||
|
||||
|
||||
def _ind_pythagorean_ratio(df: pd.DataFrame, lookback: float) -> pd.Series:
|
||||
return _public_pythagorean_ratio(df, [lookback])
|
||||
|
||||
|
||||
def _ind_fractal_mirror(df: pd.DataFrame, k: float, axis_int: float) -> pd.Series:
|
||||
return _public_fractal_mirror(df, [k, axis_int])
|
||||
|
||||
|
||||
# Annotated as ``dict[str, Any]`` deliberately: each indicator has its own
|
||||
# arity and parameter names, so a single ``Callable`` signature would be a
|
||||
# lie. Dispatch happens in :func:`_eval_node`, which validates the verb name
|
||||
@@ -139,6 +161,9 @@ INDICATOR_FNS: dict[str, Any] = {
|
||||
"realized_vol": _ind_realized_vol,
|
||||
"macd": _ind_macd,
|
||||
"macd_pct": _ind_macd_pct,
|
||||
"candle_pattern": _ind_candle_pattern,
|
||||
"pythagorean_ratio": _ind_pythagorean_ratio,
|
||||
"fractal_mirror": _ind_fractal_mirror,
|
||||
}
|
||||
|
||||
_TIME_FEATURE_FNS: dict[str, Callable[[pd.DatetimeIndex], pd.Series]] = {
|
||||
|
||||
@@ -17,7 +17,8 @@ ACTION_VALUES: frozenset[str] = frozenset(
|
||||
KIND_VALUES: frozenset[str] = frozenset({"indicator", "feature", "literal"})
|
||||
|
||||
KNOWN_INDICATORS: frozenset[str] = frozenset(
|
||||
{"sma", "sma_pct", "rsi", "atr", "atr_pct", "macd", "macd_pct", "realized_vol"}
|
||||
{"sma", "sma_pct", "rsi", "atr", "atr_pct", "macd", "macd_pct", "realized_vol",
|
||||
"candle_pattern", "pythagorean_ratio", "fractal_mirror"}
|
||||
)
|
||||
KNOWN_FEATURES: frozenset[str] = frozenset(
|
||||
{"open", "high", "low", "close", "volume",
|
||||
|
||||
@@ -39,6 +39,10 @@ INDICATOR_ARITY: dict[str, tuple[int, int]] = {
|
||||
"realized_vol": (1, 1), # window
|
||||
"macd": (0, 3), # fast, slow, signal (tutti opzionali)
|
||||
"macd_pct": (0, 3), # macd/close, frazionale (per confronti con literal)
|
||||
# Pythagoras indicators (params encoded as floats)
|
||||
"candle_pattern": (4, 13), # [length, sym0, ..., sym_{length-1}]
|
||||
"pythagorean_ratio": (1, 1), # lookback in [12,200]
|
||||
"fractal_mirror": (2, 2), # k in [3,12], axis_int in {0=h,1=v}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,3 +114,37 @@ def _validate_indicator(node: IndicatorNode) -> None:
|
||||
raise ValidationError(
|
||||
f"indicator '{node.name}' arity {n_params} out of [{min_p},{max_p}]"
|
||||
)
|
||||
|
||||
# Pythagoras-specific param semantics
|
||||
name = node.name
|
||||
if name == "candle_pattern":
|
||||
length = int(node.params[0])
|
||||
if not (3 <= length <= 12):
|
||||
raise ValidationError(
|
||||
f"candle_pattern length must be in [3,12], got {length}"
|
||||
)
|
||||
if n_params != 1 + length:
|
||||
raise ValidationError(
|
||||
f"candle_pattern: expected 1+length={1 + length} params, got {n_params}"
|
||||
)
|
||||
for i, sym in enumerate(node.params[1:], start=1):
|
||||
sym_int = int(sym)
|
||||
if sym_int not in (0, 1, 2):
|
||||
raise ValidationError(
|
||||
f"candle_pattern sym[{i - 1}] must be 0(U)/1(D)/2(doji), got {sym}"
|
||||
)
|
||||
elif name == "pythagorean_ratio":
|
||||
lookback = int(node.params[0])
|
||||
if not (12 <= lookback <= 200):
|
||||
raise ValidationError(
|
||||
f"pythagorean_ratio lookback in [12,200], got {lookback}"
|
||||
)
|
||||
elif name == "fractal_mirror":
|
||||
k = int(node.params[0])
|
||||
if not (3 <= k <= 12):
|
||||
raise ValidationError(f"fractal_mirror k must be in [3,12], got {k}")
|
||||
axis_int = int(node.params[1])
|
||||
if axis_int not in (0, 1):
|
||||
raise ValidationError(
|
||||
f"fractal_mirror axis must be 0(h)/1(v), got {axis_int}"
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -14,9 +15,18 @@ import pandas as pd # type: ignore[import-untyped]
|
||||
|
||||
|
||||
def _paper_conn(db_path: str | Path) -> sqlite3.Connection:
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
# Cold-start race: GUI può avviarsi prima che il paper writer crei il file.
|
||||
db_path_str = str(db_path)
|
||||
deadline = time.monotonic() + 5.0
|
||||
while True:
|
||||
try:
|
||||
conn = sqlite3.connect(db_path_str, timeout=5.0)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
except sqlite3.OperationalError:
|
||||
if time.monotonic() >= deadline:
|
||||
raise
|
||||
time.sleep(1.0)
|
||||
|
||||
|
||||
def paper_runs_df(db_path: str | Path) -> pd.DataFrame:
|
||||
|
||||
@@ -34,8 +34,10 @@ def test_frontend_imports() -> None:
|
||||
def test_strategies_json_loadable() -> None:
|
||||
files = importlib.resources.files("strategy_crypto") / "strategies"
|
||||
found = sorted(p.name for p in files.iterdir() if p.name.endswith(".json"))
|
||||
assert "btc_fb63e851.json" in found
|
||||
assert "eth_facd6af85d5d.json" in found
|
||||
# Convenzione: almeno un winner BTC e uno ETH shippati col package.
|
||||
# Non hard-codare hash specifici: cambiano ad ogni swap di paper winner.
|
||||
assert any(n.startswith("btc_") for n in found), f"no btc_*.json in {found}"
|
||||
assert any(n.startswith("eth_") for n in found), f"no eth_*.json in {found}"
|
||||
for fname in found:
|
||||
data = json.loads((files / fname).read_text())
|
||||
assert "rules" in data, f"{fname} missing 'rules' key"
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Regression guard: end-of-window snap deve seguire il timeframe dell'asset.
|
||||
|
||||
Bug originale (scripts/run_paper_trading.py): ``end = now.replace(minute=0,...)``
|
||||
snappava sempre all'ora; ETH 5m veniva quindi valutato 1 volta ogni 60 min
|
||||
invece di ogni 5 min, riducendo la fedelta' al backtest tunato 5m.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||
_RUNNER_PATH = _REPO_ROOT / "scripts" / "run_paper_trading.py"
|
||||
|
||||
|
||||
def _load_runner_module():
|
||||
spec = importlib.util.spec_from_file_location("run_paper_trading", _RUNNER_PATH)
|
||||
assert spec is not None and spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules["run_paper_trading"] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def runner():
|
||||
return _load_runner_module()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"now, tf, expected",
|
||||
[
|
||||
# 5m: snap al boundary di 5 min, NON all'ora
|
||||
(datetime(2026, 5, 18, 14, 37, 42, tzinfo=UTC), "5m", datetime(2026, 5, 18, 14, 35, tzinfo=UTC)),
|
||||
(datetime(2026, 5, 18, 14, 35, 0, tzinfo=UTC), "5m", datetime(2026, 5, 18, 14, 35, tzinfo=UTC)),
|
||||
(datetime(2026, 5, 18, 14, 34, 59, tzinfo=UTC), "5m", datetime(2026, 5, 18, 14, 30, tzinfo=UTC)),
|
||||
# 1h: comportamento storico preservato
|
||||
(datetime(2026, 5, 18, 14, 37, 42, tzinfo=UTC), "1h", datetime(2026, 5, 18, 14, 0, tzinfo=UTC)),
|
||||
(datetime(2026, 5, 18, 14, 0, 0, tzinfo=UTC), "1h", datetime(2026, 5, 18, 14, 0, tzinfo=UTC)),
|
||||
# 15m / 4h
|
||||
(datetime(2026, 5, 18, 14, 22, 0, tzinfo=UTC), "15m", datetime(2026, 5, 18, 14, 15, tzinfo=UTC)),
|
||||
(datetime(2026, 5, 18, 14, 22, 0, tzinfo=UTC), "4h", datetime(2026, 5, 18, 12, 0, tzinfo=UTC)),
|
||||
],
|
||||
)
|
||||
def test_align_end_to_timeframe(runner, now, tf, expected) -> None:
|
||||
assert runner._align_end_to_timeframe(now, tf) == expected
|
||||
|
||||
|
||||
def test_align_end_5m_advances_every_5_minutes(runner) -> None:
|
||||
"""Bug-regression: chiamate consecutive a 5 min di distanza devono
|
||||
produrre end DIVERSI per tf=5m (prima del fix erano identici)."""
|
||||
a = datetime(2026, 5, 18, 14, 30, 0, tzinfo=UTC)
|
||||
b = datetime(2026, 5, 18, 14, 35, 0, tzinfo=UTC)
|
||||
c = datetime(2026, 5, 18, 14, 40, 0, tzinfo=UTC)
|
||||
ends = {runner._align_end_to_timeframe(t, "5m") for t in (a, b, c)}
|
||||
assert len(ends) == 3
|
||||
|
||||
|
||||
def test_align_end_1h_stable_within_hour(runner) -> None:
|
||||
"""Per tf=1h, chiamate dentro la stessa ora devono dare lo stesso end."""
|
||||
ends = {
|
||||
runner._align_end_to_timeframe(datetime(2026, 5, 18, 14, m, 0, tzinfo=UTC), "1h")
|
||||
for m in (0, 15, 30, 45, 59)
|
||||
}
|
||||
assert ends == {datetime(2026, 5, 18, 14, 0, tzinfo=UTC)}
|
||||
@@ -0,0 +1,110 @@
|
||||
# Libro dei Frattali — Riassunto
|
||||
|
||||
**Autori:** Luca Serleto, Corrado Malanga
|
||||
**Editore/Stampa:** Adobe InDesign, nov 2024
|
||||
**Pagine:** 58 — **testo embedded ≈ nullo (~1.6 KB)**
|
||||
**Estratto raw:** [_extracted/Libro_frattali.txt](_extracted/Libro_frattali.txt)
|
||||
|
||||
## Natura del documento
|
||||
|
||||
Il "Libro dei Frattali" è un **catalogo grafico** di pattern candlestick, non un testo discorsivo. Le pagine sono composte quasi interamente da figure (candle-chart estratti via trasformata di Fourier secondo la metodologia esposta nel companion paper [Pythagoras Trading Prediction](Pythagoras_Trading_Prediction.summary.md)).
|
||||
|
||||
L'estrazione testuale rivela **solo le etichette**:
|
||||
|
||||
| Pagina | Contenuto testuale |
|
||||
|---|---|
|
||||
| 1 | "Luca Serleto / Corrado Malanga / Libro dei Frattali" (frontespizio) |
|
||||
| 2 | "Pattern da 3 candele a massimo 6 candele (LONG)" |
|
||||
| 3–58 | "Pattern 1", "Pattern 2", …, "Pattern 57" — una riga per pagina |
|
||||
|
||||
**Tutto il resto è immagine.** L'utente ha esplicitamente richiesto che gli agenti **non passino dalle immagini** ma costruiscano schemi dai numeri.
|
||||
|
||||
## Struttura numerica deducibile (senza guardare le figure)
|
||||
|
||||
### Conteggio dei pattern
|
||||
- **Totale pattern catalogati: 57**
|
||||
- Numerati progressivamente da 1 a 57
|
||||
- Tutti dichiarati "**LONG**" (segnali rialzisti)
|
||||
- Tutti nel range **3–6 candele** (range 1 della tassonomia esposta in *Pythagoras Trading Prediction*, pp. 49)
|
||||
|
||||
### Coerenza col paper companion
|
||||
Il paper *Pythagoras Trading Prediction* (p. 49) afferma testualmente:
|
||||
> "Questa tipologia di pattern può estendersi fino a 6 candele, per questo motivo nel libro i pattern sono identificati con range da 3 a 6, da 6 a 12, da 12 a 24, da 24 a 39 e da 39 a 56."
|
||||
|
||||
⇒ Il Libro dei Frattali pubblicato copre **solo il primo range** (3–6 candele). Il numero massimo (56) della tassonomia coincide quasi esattamente con il numero di pattern catalogati (57) — possibile errore tipografico o discrepanza intenzionale di 1 unità.
|
||||
|
||||
### Pattern composti citati nel paper
|
||||
Sempre nel paper companion (p. 53), è dichiarato che pattern di range superiore sono composti dai pattern semplici di range 1:
|
||||
- **Pattern 13 = Pattern 3 + Pattern 11**
|
||||
|
||||
Implicazione: la numerazione del Libro NON è una sequenza temporale ma una **enumerazione di forme distinte** che fungono da "lettere" dell'alfabeto frattale; i pattern complessi sono "parole" composte.
|
||||
|
||||
## Spazio numerico (3–6 candele LONG)
|
||||
|
||||
Per i 57 pattern, l'**unico contenuto numerico estraibile senza vision** è il loro indice 1–57. Tuttavia il **dominio combinatorio** che il catalogo intende coprire può essere ricostruito teoricamente:
|
||||
|
||||
### Encoding compatto di una candela
|
||||
Una candela in approssimazione discreta può essere ridotta a un simbolo dell'alfabeto:
|
||||
- `U` = up (close > open) — bullish body
|
||||
- `D` = down (close < open) — bearish body
|
||||
- `0` = doji (close ≈ open)
|
||||
|
||||
Quindi una sequenza di 3–6 candele appartiene allo spazio `{U,D,0}^k` con k ∈ {3,4,5,6}.
|
||||
|
||||
| k | |Σᵏ| = 3ᵏ |
|
||||
|---|---|
|
||||
| 3 | 27 |
|
||||
| 4 | 81 |
|
||||
| 5 | 243 |
|
||||
| 6 | 729 |
|
||||
| **Tot** | **1080** sequenze teoriche |
|
||||
|
||||
57 pattern catalogati su 1080 sequenze ⇒ il catalogo seleziona ≈ **5.3%** dello spazio totale.
|
||||
|
||||
### Filtro implicito "LONG"
|
||||
"LONG" implica che il pattern preceda un breakout/continuazione al rialzo. Restrizione plausibile:
|
||||
- pattern terminanti con candela bullish forte, o
|
||||
- pattern con dominanza U / 0 nelle ultime k-1 candele, o
|
||||
- pattern strutturali noti dell'analisi tecnica (martello, inverted hammer, morning star, ecc.) — limitati a 3–6 candele.
|
||||
|
||||
## Numeri ricavabili dal Libro (l'unico set "puro")
|
||||
|
||||
Dal solo testo del Libro, gli agenti hanno accesso a:
|
||||
1. **Cardinalità del catalogo**: 57
|
||||
2. **Numerazione**: serie 1, 2, 3, …, 57
|
||||
3. **Range candele**: [3, 6] (chiuso, intero)
|
||||
4. **Direzione**: solo LONG
|
||||
5. **Costante autori**: 2 (Serleto, Malanga)
|
||||
6. **Edizione**: nov 2024
|
||||
7. **Numero di pagine**: 58
|
||||
|
||||
## Indizi numerici per l'analisi di similitudine
|
||||
|
||||
| Numero | Match con Pythagoras Trading Prediction |
|
||||
|---|---|
|
||||
| 57 pattern | tassonomia bordo superiore = 56 (errore di 1) |
|
||||
| 56 (bordo tassonomia) | numerologia: 56 = 8·7 = ottava·sacro |
|
||||
| 3 (min candele) | numero sacro pitagorico (triade); 3 = lati triangolo Evideon |
|
||||
| 6 (max candele) | numero perfetto (1+2+3); φ²-... |
|
||||
| 6/3 = 2 | ratio 2 = ottava musicale |
|
||||
| (max-min)/min = 1 | unità |
|
||||
| log(57)/log(3) ≈ 3.69 | dimensione frattale di Hausdorff di insieme dimensionale 4 ≈ 3.69 |
|
||||
|
||||
## Punti utili per estrazione numerica (per il task di similitudini)
|
||||
|
||||
1. **N = 57** pattern → cardinality, da confrontare con 56 (bordo del paper), 528 Hz (Solfeggio), 588 candele indicatore
|
||||
2. **k ∈ {3, 4, 5, 6}** → estremi del range
|
||||
3. **Σ³⁺⁴⁺⁵⁺⁶ = 1080** sequenze teoriche (3ᵏ encoded) → 1080 = 8·135 = 2³·3³·5
|
||||
4. **Tassonomia bordi**: 3, 6, 12, 24, 39, 56 — sequenza utile per fingerprint
|
||||
5. **Direzione "LONG"** → variabile binaria
|
||||
6. **Composizionalità**: Pattern 13 = Pattern 3 + Pattern 11 → grammar di pattern frattali
|
||||
|
||||
## Conclusione
|
||||
|
||||
Il Libro dei Frattali, **letto senza immagini**, è un oggetto numerico molto povero (57 etichette su 58 pagine). Per produrre similitudini significative tra i due testi, occorre:
|
||||
|
||||
1. Trattare il Libro come **schema combinatorio implicito** (1080 sequenze possibili, 57 selezionate)
|
||||
2. Confrontare i **numeri-chiave** (3, 6, 57, range) con i numeri estratti dal paper teorico (φ, π, e, 528, 588, 137.0359, etc.)
|
||||
3. Lasciare agli agenti il compito di **trovare ricorrenze, ratios, e dimensioni frattali** che colleghino i due insiemi numerici.
|
||||
|
||||
Vedere [Pythagoras_Trading_Prediction.summary.md](Pythagoras_Trading_Prediction.summary.md) per il dominio numerico del paper teorico.
|
||||
@@ -0,0 +1,182 @@
|
||||
# Pythagoras Trading Prediction — Riassunto
|
||||
|
||||
**Autori:** Luca Serleto, Corrado Malanga
|
||||
**Editore/Stampa:** Adobe InDesign, dic 2024
|
||||
**Pagine:** 66 — testo embedded ~91 KB (no immagini estraibili: figure presenti ma non analizzate per scelta esplicita dell'utente)
|
||||
**Estratto raw:** [_extracted/Pythagoras_Trading_Prediction_.txt](_extracted/Pythagoras_Trading_Prediction_.txt)
|
||||
|
||||
## Tesi centrale
|
||||
|
||||
Il prezzo dei mercati finanziari è descrivibile come **funzione frattale**, scomponibile via **trasformata di Fourier** in componenti sinusoidali periodiche, e quindi proiettabile nel futuro. Il "parametro nascosto" che la fisica quantistica cerca da decenni è la **Coscienza** (entropia di Shannon ΔS), che modula sia la capacità di osservare/predire che la traiettoria stessa dei prezzi.
|
||||
|
||||
## Struttura del libro (4 parti)
|
||||
|
||||
1. **Parte teorica / coscienziale** (pp. 2–24): framework filosofico-fisico
|
||||
2. **Frattali** (pp. 37–44): indicatore frattale H-C
|
||||
3. **Trasformata di Fourier** (pp. 45–58): pattern catalogati e applicazione
|
||||
4. **Piattaforma Pythagoras** (pp. 59–66): manuale operativo (Auto + Manual mode)
|
||||
|
||||
## Concetti chiave (in ordine di apparizione)
|
||||
|
||||
### 1. Approccio "femminile" vs "maschile" alla predizione
|
||||
- **Femminile/animico**: visione olografica del tempo (Pribram, Bohm), tutto co-presente nel presente; il cervello come "lettore di ologrammi"
|
||||
- **Maschile/spirituale**: approccio galileiano-statistico via algoritmo
|
||||
- Tesi: i due metodi vanno integrati
|
||||
|
||||
### 2. Probabilità → Certezza
|
||||
> "Se il sistema predittivo è corretto al 10%, possiamo trovare il parametro che trasformi quella probabilità nella certezza al 100%."
|
||||
|
||||
Il parametro mancante è la **Coscienza**.
|
||||
|
||||
### 3. Teorema di ricorrenza di Poincaré
|
||||
In meccanica hamiltoniana, in un sistema dinamico a spazio delle fasi limitato, lo stato può tornare arbitrariamente vicino a quello iniziale dopo tempo sufficiente. → Il tempo è **circolare**, gli eventi ricorrono → mercati ripetibili.
|
||||
|
||||
### 4. Entropia di Shannon S
|
||||
- S = -log(W), W = microstati
|
||||
- Coscienza ≡ S; **Consapevolezza ≡ ΔS** (variazione di entropia tra istante e successivo)
|
||||
- Inizio universo: S = -∞; fine: S = 0; entropia totale = ∞
|
||||
|
||||
### 5. Hartman-Curry → "Indicatore frattale H-C"
|
||||
- Introdotto da Corrado Malanga al XCongress di Pescara (2018)
|
||||
- Prima applicazione su grafico EUR/USD
|
||||
- Struttura tridimensionale costruita su "numeri universali" che genera ricorrenze
|
||||
|
||||
### 6. Numeri Universali (Evideon)
|
||||
> "I numeri dell'universo sono: la sezione aurea φ, π, il numero di Eulero e, √2, ecc. Numeri irrazionali e trascendenti."
|
||||
|
||||
Triangolo rettangolo con costanti universali fisiche; lati moltiplicati per opportuni coefficienti basati su π, e, φ e temperatura assoluta di Kelvin/100.
|
||||
|
||||
**Costanti numeriche citate esplicitamente:**
|
||||
- 137.0359 × √π / 2 = **121.449**
|
||||
- 266.87 × 1 / √e = **161.86**
|
||||
- Frequenze Solfeggio: **741, 528, 852, 639, 396, 417** (p. 27)
|
||||
- Numero di Avogadro N = 6.022·10²³ (citato come ipotesi sul numero di sub-componenti dell'Universo)
|
||||
|
||||
### 7. Trasformata di Fourier
|
||||
Scomposizione di qualsiasi funzione continua in seni e coseni:
|
||||
- Frequenze positive (orario) e negative (antiorario) → propagazione **avanti e indietro nel tempo**
|
||||
- Ricostruzione approssimata con N componenti → **N = misura della consapevolezza**
|
||||
- Pochi N: previsione approssimativa; molti N: previsione accurata
|
||||
|
||||
### 8. Equazione di Schrödinger ↔ trading
|
||||
Identificazione formale:
|
||||
|
||||
| Schrödinger | Trading |
|
||||
|---|---|
|
||||
| iℏ ∂Ψ/∂t = -ℏ²/(2m) ∂²Ψ/∂x² + V(x)Ψ | dinamica del prezzo |
|
||||
| derivata seconda di Ψ in x | "piegatura" spazio-temporale del grafico |
|
||||
| m | massa fittizia ≈ volume d'affari |
|
||||
| V(Ψ) | perturbazioni esterne (manipolazione di mercato) |
|
||||
| Ψ ampiezza | altezza candele (asse energia/prezzo) |
|
||||
| Ψ² probabilità | probabilità che l'evento si materializzi |
|
||||
|
||||
Identificazione con energia libera di Gibbs: termine -TΔS della formula di Gibbs ≡ termine entropico in Schrödinger.
|
||||
|
||||
### 9. Centro di inversione (operazione geometrica chiave)
|
||||
Sul modello "Evideon" (3 assi: spazio, tempo, energia), un punto A del passato si trasforma nel punto E del futuro tramite:
|
||||
- **Riflessione speculare sull'asse verticale** (energia/prezzo)
|
||||
- **Riflessione speculare sull'asse orizzontale** (tempo)
|
||||
- = traslazione temporale + inversione di pattern
|
||||
|
||||
Inoltre il pattern subisce:
|
||||
- **Compressione** o **dilatazione** sull'asse x (tempo)
|
||||
- **Compressione** o **dilatazione** sull'asse y (prezzo)
|
||||
A seconda della posizione del punto sull'Evideon (tempo sotto-multiplo → contrazione).
|
||||
|
||||
### 10. Finestra di Overton
|
||||
- Apertura temporale che definisce "il presente"
|
||||
- Analogia con SAR (Synthetic Aperture Radar): più larga la finestra, più precisa la previsione
|
||||
- Pythagoras applica la stessa finestra al passato per proiettare nel futuro
|
||||
|
||||
### 11. ZPE (Zero Point Energy)
|
||||
Universo non vuoto: pieno di particelle e anti-particelle che si annichiliscono. Permette di "conoscere perfettamente coordinate spazio-temporali e valore nullo dell'energia": non c'è incertezza vera (contro Heisenberg).
|
||||
|
||||
### 12. Indicatore frattale su TradingView
|
||||
Disponibile su licenza dedicata. Parametri operativi:
|
||||
- Tracciato da minimo → massimo
|
||||
- **25 linee verticali** all'interno di **588 candele**
|
||||
- 90% segnala accelerazione (aumento volatilità), 10% lateralizzazione
|
||||
- Tolleranza ±3 ore su time frame 1H/4H
|
||||
- **Linee blu inclinate** = supporti/resistenze tempo-condizionati
|
||||
- **Cerchi bianchi** = obiettivi di prezzo circolari
|
||||
|
||||
### 13. Pattern range (taxonomy del Book dei Frattali)
|
||||
| Range | Numero candele |
|
||||
|---|---|
|
||||
| Range 1 | 3 – 6 candele |
|
||||
| Range 2 | 6 – 12 candele |
|
||||
| Range 3 | 12 – 24 candele |
|
||||
| Range 4 | 24 – 39 candele |
|
||||
| Range 5 | 39 – 56 candele |
|
||||
|
||||
I pattern oltre 6 candele diventano "frattali complessi": composizione di pattern semplici (es. Pattern 13 = Pattern 3 + Pattern 11).
|
||||
|
||||
### 14. Variabili che NON alterano il target
|
||||
- Inclinazione del pattern
|
||||
- Ampiezza
|
||||
- Curvatura
|
||||
- Numero di candele entro lo stesso range
|
||||
|
||||
→ il target di proiezione è invariante per queste deformazioni.
|
||||
|
||||
### 15. Variabili che alterano l'applicabilità
|
||||
- **Time frame**: 15m, 1H, 4H, 1D
|
||||
- **Volatilità dell'asset** ("consapevolezza dell'asset")
|
||||
- **Leva finanziaria**
|
||||
|
||||
## Esempi concreti citati
|
||||
|
||||
| Pattern | Asset | Data/Ora | TF |
|
||||
|---|---|---|---|
|
||||
| 1 | BTC | 28 ago 2024 00:00 | 15m |
|
||||
| 1 | ETH | 28 ago 2024 00:00 | 15m |
|
||||
| 1 | Oro | 29 ago 2024 14:30 | 15m |
|
||||
| 1 | Argento | 17 lug 2024 18:15 | 15m |
|
||||
| 13 | BTC | 24 ago 2024 23:15 | 15m |
|
||||
| 13 | ETH | 24 ago 2024 23:45 | 15m |
|
||||
| 13 | Oro | 02 ago 2024 16:45 | 15m |
|
||||
| 13 | Argento | 02 ago 2024 16:45 | 15m |
|
||||
| 15 | DJT (Trump Media) | 12 lug 2024 (chiusura) | 1D |
|
||||
|
||||
Il caso DJT è presentato come "predizione" dell'attentato a Trump del 14 lug 2024 (frattale complesso al rialzo formatosi il 12 lug → escludeva evento negativo).
|
||||
|
||||
## Piattaforma Pythagoras
|
||||
|
||||
### Auto Mode
|
||||
1. Seleziona asset
|
||||
2. Seleziona TF (15m, 1H, 4H)
|
||||
3. Click "Predict" → Fourier identifica frattale e proietta futuro (colore giallo)
|
||||
|
||||
### Manual Mode
|
||||
1. Seleziona asset
|
||||
2. Seleziona frattale dal Book per data
|
||||
3. Click "Predict" → proiezione
|
||||
|
||||
## Disclaimer interno al testo
|
||||
|
||||
> "Anche se si conosce un potenziale futuro, questo non è garanzia di guadagno. Solo la consapevolezza del trader, basata sull'esperienza, potrà trasformare la predizione di un potenziale futuro in un profitto concreto."
|
||||
|
||||
Il testo enfatizza che il **target del trader non è guadagnare ma capire sé stesso** (approccio "coscienziale"). Il denaro è strumento di apprendimento dell'entropia/dualità.
|
||||
|
||||
## Punti utili per estrazione numerica (per il task di similitudini)
|
||||
|
||||
1. **Numeri universali esatti**: φ ≈ 1.6180339887, π ≈ 3.1415926535, e ≈ 2.71828, √2 ≈ 1.41421
|
||||
2. **Costanti derivate dal testo**: 137.0359, 121.449, 266.87, 161.86
|
||||
3. **Frequenze Solfeggio (Hz)**: 396, 417, 528, 639, 741, 852
|
||||
4. **Conteggi operativi**: 25 linee/588 candele = ratio 0.0425
|
||||
5. **Numeri ricorrenti**: 6.022·10²³ (Avogadro)
|
||||
6. **Range candele**: bordi 3, 6, 12, 24, 39, 56 — sequenza con incrementi 3, 6, 12, 15, 17 (NON Fibonacci puro, ma sequenza con ratios ~2x)
|
||||
7. **Ratios tra bordi**: 6/3=2, 12/6=2, 24/12=2, 39/24=1.625, 56/39=1.436 — gli ultimi due si avvicinano a φ ≈ 1.618 e √2 ≈ 1.414
|
||||
8. **Sequenza pattern numerati**: 1, 2, 3, ... 57 (matching col Libro dei Frattali)
|
||||
9. **Operazioni geometriche**: mirror H, mirror V, scaling x/y (4 trasformazioni)
|
||||
10. **TF candidati**: 15m, 1H, 4H, 1D (multipli 4x e 6x)
|
||||
|
||||
## Bibliografia interna / link YouTube citati
|
||||
- youtube.com/shorts/OnM09uMCS50 (teorema di Poincaré)
|
||||
- youtu.be/hHig-PP4Mcs (gravità come Coscienza)
|
||||
- youtu.be/HTIYZBrzZbM (struttura dell'Evideon)
|
||||
|
||||
## Opere citate degli autori
|
||||
- Corrado Malanga, *Io e Dio*, Spazio Interiore, Roma
|
||||
- Corrado Malanga, *Trilogia della Coscienza*, Spazio Interiore, Roma
|
||||
- Corrado Malanga al XCongress, Pescara, 2018 (intro pattern H-C)
|
||||
@@ -0,0 +1,227 @@
|
||||
|
||||
|
||||
===PAGE 1===
|
||||
Luca Serleto
|
||||
Corrado Malanga
|
||||
Libro dei Libro dei
|
||||
FrattaliFrattali
|
||||
|
||||
===PAGE 2===
|
||||
Pattern da 3 candele a massimo 6 candele (LONG)
|
||||
Pattern 1
|
||||
|
||||
===PAGE 3===
|
||||
Pattern 2
|
||||
|
||||
===PAGE 4===
|
||||
Pattern 3
|
||||
|
||||
===PAGE 5===
|
||||
Pattern 4
|
||||
|
||||
===PAGE 6===
|
||||
Pattern 5
|
||||
|
||||
|
||||
===PAGE 7===
|
||||
Pattern 6
|
||||
|
||||
|
||||
===PAGE 8===
|
||||
Pattern 7
|
||||
|
||||
|
||||
===PAGE 9===
|
||||
Pattern 8
|
||||
|
||||
|
||||
===PAGE 10===
|
||||
Pattern 9
|
||||
|
||||
|
||||
===PAGE 11===
|
||||
Pattern 10
|
||||
|
||||
|
||||
===PAGE 12===
|
||||
Pattern 11
|
||||
|
||||
|
||||
===PAGE 13===
|
||||
Pattern 12
|
||||
|
||||
|
||||
===PAGE 14===
|
||||
Pattern 13
|
||||
|
||||
|
||||
===PAGE 15===
|
||||
Pattern 14
|
||||
|
||||
|
||||
===PAGE 16===
|
||||
Pattern 15
|
||||
|
||||
|
||||
===PAGE 17===
|
||||
Pattern 16
|
||||
|
||||
|
||||
===PAGE 18===
|
||||
Pattern 17
|
||||
|
||||
|
||||
===PAGE 19===
|
||||
Pattern 18
|
||||
|
||||
|
||||
===PAGE 20===
|
||||
Pattern 19
|
||||
|
||||
|
||||
===PAGE 21===
|
||||
Pattern 20
|
||||
|
||||
|
||||
===PAGE 22===
|
||||
Pattern 21
|
||||
|
||||
|
||||
===PAGE 23===
|
||||
Pattern 22
|
||||
|
||||
|
||||
===PAGE 24===
|
||||
Pattern 23
|
||||
|
||||
|
||||
===PAGE 25===
|
||||
Pattern 24
|
||||
|
||||
|
||||
===PAGE 26===
|
||||
Pattern 25
|
||||
|
||||
|
||||
===PAGE 27===
|
||||
Pattern 26
|
||||
|
||||
|
||||
===PAGE 28===
|
||||
Pattern 27
|
||||
|
||||
|
||||
===PAGE 29===
|
||||
Pattern 28
|
||||
|
||||
|
||||
===PAGE 30===
|
||||
Pattern 29
|
||||
|
||||
|
||||
===PAGE 31===
|
||||
Pattern 30
|
||||
|
||||
|
||||
===PAGE 32===
|
||||
Pattern 31
|
||||
|
||||
|
||||
===PAGE 33===
|
||||
Pattern 32
|
||||
|
||||
|
||||
===PAGE 34===
|
||||
Pattern 33
|
||||
|
||||
|
||||
===PAGE 35===
|
||||
Pattern 34
|
||||
|
||||
|
||||
===PAGE 36===
|
||||
Pattern 35
|
||||
|
||||
|
||||
===PAGE 37===
|
||||
Pattern 36
|
||||
|
||||
|
||||
===PAGE 38===
|
||||
Pattern 37
|
||||
|
||||
|
||||
===PAGE 39===
|
||||
Pattern 38
|
||||
|
||||
|
||||
===PAGE 40===
|
||||
Pattern 39
|
||||
|
||||
|
||||
===PAGE 41===
|
||||
Pattern 40
|
||||
|
||||
|
||||
===PAGE 42===
|
||||
Pattern 41
|
||||
|
||||
|
||||
===PAGE 43===
|
||||
Pattern 42
|
||||
|
||||
|
||||
===PAGE 44===
|
||||
Pattern 43
|
||||
|
||||
|
||||
===PAGE 45===
|
||||
Pattern 44
|
||||
|
||||
|
||||
===PAGE 46===
|
||||
Pattern 45
|
||||
|
||||
|
||||
===PAGE 47===
|
||||
Pattern 46
|
||||
|
||||
|
||||
===PAGE 48===
|
||||
Pattern 47
|
||||
|
||||
|
||||
===PAGE 49===
|
||||
Pattern 48
|
||||
|
||||
|
||||
===PAGE 50===
|
||||
Pattern 49
|
||||
|
||||
|
||||
===PAGE 51===
|
||||
Pattern 50
|
||||
|
||||
|
||||
===PAGE 52===
|
||||
Pattern 51
|
||||
|
||||
|
||||
===PAGE 53===
|
||||
Pattern 52
|
||||
|
||||
|
||||
===PAGE 54===
|
||||
Pattern 53
|
||||
|
||||
===PAGE 55===
|
||||
Pattern 54
|
||||
|
||||
===PAGE 56===
|
||||
Pattern 55
|
||||
|
||||
===PAGE 57===
|
||||
Pattern 56
|
||||
|
||||
===PAGE 58===
|
||||
Pattern 57
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
# strategy_pythagoras
|
||||
|
||||
Strategia di trading basata sul framework **Pythagoras-Malanga**
|
||||
(candle-pattern + geometria frattale), evoluta via GA sul core
|
||||
`multi_swarm_core`. Workspace member del monorepo `multi_swarm_coevolutive`.
|
||||
|
||||
## Scope
|
||||
|
||||
Pipeline coevolutiva su candele OHLC: il GA del core esplora combinazioni
|
||||
di indicatori candle-pattern, geometria pitagorica e ratio frattali, e
|
||||
produce strategie JSON freezate. Questo member le esegue in
|
||||
**paper-trading forward-test** ed espone una dashboard NiceGUI per
|
||||
analisi invarianza scala/tick e candle.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
strategy_pythagoras/
|
||||
├── backend/ paper trading runner (PaperExecutor, Portfolio, PaperRepository)
|
||||
│ └── schema.py tabelle paper_trading_* (DB locale dedicato)
|
||||
├── frontend/ NiceGUI dashboard (tab invariance, candle, equity, ticks)
|
||||
├── strategies/ JSON freezate input al runner
|
||||
│ (pythagoras_*.json)
|
||||
└── prompts.json 7 stili di prompt LLM (candle-pattern, frattale, ratio,
|
||||
pivot, kagi, renko, hybrid)
|
||||
```
|
||||
|
||||
## Run paper-trading smoke
|
||||
|
||||
```bash
|
||||
uv run python scripts/run_pythagoras_smoke.py \
|
||||
--name pythagoras-smoke-001 \
|
||||
--initial-capital 1000 \
|
||||
--poll-seconds 300
|
||||
```
|
||||
|
||||
Il default `--strategies-dir` punta ai JSON shippati col package via
|
||||
`importlib.resources.files("strategy_pythagoras") / "strategies"`.
|
||||
|
||||
## Dashboard
|
||||
|
||||
```bash
|
||||
uv run python -m strategy_pythagoras.frontend.nicegui_app
|
||||
```
|
||||
|
||||
In produzione: `https://swarm.tielogic.xyz/strategy_pythagoras_gui/`
|
||||
(root_path configurato via `DASHBOARD_ROOT_PATH=/strategy_pythagoras_gui`).
|
||||
|
||||
## DB schema
|
||||
|
||||
Schema isolato dal core e dalle altre strategie. Due DB distinti:
|
||||
|
||||
- `state/strategy_pythagoras.db` — GA + analisi invarianza
|
||||
(env `STRATEGY_PYTHAGORAS_DB_PATH`)
|
||||
- `state/strategy_pythagoras_paper.db` — paper-trading runs
|
||||
(env `STRATEGY_PYTHAGORAS_PAPER_DB_PATH`)
|
||||
|
||||
Tabelle paper-trading:
|
||||
|
||||
- `paper_trading_runs` — metadata run (id, name, capital, status)
|
||||
- `paper_trading_positions` — posizioni aperte (long/short)
|
||||
- `paper_trading_trades` — trade realized (entry/exit, pnl, fees)
|
||||
- `paper_trading_equity` — equity curve snapshot
|
||||
- `paper_trading_ticks` — log signal/action per ogni bar
|
||||
|
||||
DDL gestito da `strategy_pythagoras.backend.schema.init_schema()`.
|
||||
|
||||
La dashboard legge **anche** il `runs.db` del core GA (env `GA_DB_PATH`)
|
||||
per correlare paper performance con i genomi di provenienza e con i
|
||||
risultati di fitness invariance.
|
||||
|
||||
## References
|
||||
|
||||
- Spec: `docs/superpowers/specs/2026-05-19-strategy-pythagoras-design.md`
|
||||
- Plan: `docs/superpowers/plans/2026-05-19-strategy-pythagoras.md`
|
||||
- Summary paper: `Pythagoras/Pythagoras_Trading_Prediction.summary.md`
|
||||
- Summary frattali: `Pythagoras/Libro_frattali.summary.md`
|
||||
- Pattern member: vedi `src/strategy_crypto/README.md`
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "strategy-pythagoras"
|
||||
version = "0.1.0"
|
||||
description = "Strategy Pythagoras: candle-pattern GA su framework Pythagoras-Malanga, paper-trading runner + NiceGUI dashboard"
|
||||
authors = [{ name = "Adriano Dal Pastro", email = "adrianodalpastro@tielogic.com" }]
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"multi-swarm-core",
|
||||
"nicegui>=3.11.1",
|
||||
"plotly>=5.24",
|
||||
"pandas>=2.2",
|
||||
"pyarrow>=18.0",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel.force-include]
|
||||
"strategy_pythagoras/strategies" = "strategy_pythagoras/strategies"
|
||||
"strategy_pythagoras/prompts.json" = "strategy_pythagoras/prompts.json"
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Indicatori candle Pythagoras.
|
||||
|
||||
Vincoli grammar: ``IndicatorNode.params`` e' sempre ``list[float]``. Quindi:
|
||||
- candle_pattern: params = [length, sym0, sym1, ..., sym_{length-1}]
|
||||
length in [3,12]; sym in {0=U, 1=D, 2=doji}
|
||||
- pythagorean_ratio: params = [lookback] lookback in [12,200]
|
||||
- fractal_mirror: params = [k, axis_int] k in [3,12]; axis_int=0(h) 1(v)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
_DOJI_THRESHOLD = 0.001
|
||||
|
||||
|
||||
def _symbol_series(df: pd.DataFrame) -> pd.Series:
|
||||
"""Mappa ogni candela in {0=U, 1=D, 2=doji}."""
|
||||
close = df["close"]
|
||||
open_ = df["open"]
|
||||
rel = (close - open_).abs() / open_.replace(0, np.nan)
|
||||
sym = np.where(close > open_, 0, np.where(close < open_, 1, 2))
|
||||
sym = np.where(rel.values < _DOJI_THRESHOLD, 2, sym)
|
||||
return pd.Series(sym, index=df.index, dtype="int64")
|
||||
|
||||
|
||||
def candle_pattern(df: pd.DataFrame, params: list[float]) -> pd.Series:
|
||||
"""1.0 se le ultime ``length`` candele matchano la sequenza, 0.0 altrimenti."""
|
||||
length = int(params[0])
|
||||
target = [int(s) for s in params[1:1 + length]]
|
||||
syms = _symbol_series(df)
|
||||
out = pd.Series(0.0, index=df.index, dtype="float64")
|
||||
if len(syms) < length:
|
||||
return out
|
||||
arr = syms.values
|
||||
target_arr = np.array(target, dtype=arr.dtype)
|
||||
for i in range(length - 1, len(arr)):
|
||||
if np.array_equal(arr[i - length + 1: i + 1], target_arr):
|
||||
out.iat[i] = 1.0
|
||||
return out
|
||||
|
||||
|
||||
def pythagorean_ratio(df: pd.DataFrame, params: list[float]) -> pd.Series:
|
||||
"""``max(close[-lookback:]) / min(close[-lookback:])`` rolling."""
|
||||
lookback = int(params[0])
|
||||
close = df["close"]
|
||||
hi = close.rolling(lookback, min_periods=1).max()
|
||||
lo = close.rolling(lookback, min_periods=1).min().replace(0, np.nan)
|
||||
return (hi / lo).fillna(1.0)
|
||||
|
||||
|
||||
def fractal_mirror(df: pd.DataFrame, params: list[float]) -> pd.Series:
|
||||
"""Correlation tra close[-k:] e suo mirror su asse axis."""
|
||||
k = int(params[0])
|
||||
axis_int = int(params[1])
|
||||
close = df["close"].values
|
||||
out = np.full(len(close), 0.0)
|
||||
for i in range(k - 1, len(close)):
|
||||
window = close[i - k + 1: i + 1]
|
||||
if axis_int == 0: # h: mirror temporale
|
||||
mirror = window[::-1]
|
||||
else: # v: mirror prezzo
|
||||
mirror = window.max() - (window - window.min())
|
||||
std_w = window.std()
|
||||
std_m = mirror.std()
|
||||
if std_w < 1e-12 or std_m < 1e-12:
|
||||
out[i] = 0.0
|
||||
else:
|
||||
out[i] = float(np.corrcoef(window, mirror)[0, 1])
|
||||
return pd.Series(out, index=df.index, dtype="float64")
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"_comment": "Stili cognitivi del GA per strategy_pythagoras. Dominio: pattern frattali ricorrenti su mercati crypto secondo il framework Pythagoras-Malanga (Fourier + frattali H-C + Evideon).",
|
||||
"_schema": "3.2",
|
||||
"_changelog": "v1.0 - Initial release. Schema clonato da strategy_crypto v3.2 con contenuto Pythagoras-aligned.",
|
||||
"_focus_metrics_design": "Le focus_metrics sono ENFASI per la lente, non filtri. 4 per stile. Devono includere almeno 1 dei 3 indicatori Pythagoras (candle_pattern, pythagorean_ratio, fractal_mirror).",
|
||||
"_design_invariants": "(1) ASCII-safe; (2) Archetipo dominante: <metafora> come ancora; (3) Lookback range esplicito; (4) Prima frase 'Il mercato e ...'; (5) Lunghezza directive 800-950 char.",
|
||||
"_param_encoding_note": "Vincolo grammar: tutti i params sono float. candle_pattern: [length, sym0, sym1, ...] (sym: 0=U up close>open, 1=D down close<open, 2=doji). pythagorean_ratio: [lookback]. fractal_mirror: [k, axis_int] (0=h temporale, 1=v prezzo).",
|
||||
|
||||
"agent_role": "Sei un agente generatore di ipotesi di trading quantitativo per un sistema swarm coevolutivo che cerca pattern frattali ricorrenti sui mercati crypto secondo il framework Pythagoras-Malanga (frattali H-C, trasformata di Fourier, geometria Evideon). Sei parte di una popolazione che esplora collettivamente lo spazio dei pattern: la diversita delle ipotesi e un asset critico. Preferisci esplorare territori meno ovvi rispetto a quelli che la tua lente cognitiva renderebbe predicibili. La strategia che produci deve essere riconoscibile come emanata dal tuo stile.",
|
||||
|
||||
"pattern_guidance": "Forme da cercare:\n - Sequenze candle 3-12 lunghezza in alfabeto {U,D,doji} via candle_pattern\n - Ratios di prezzo vicini a 1.618 (phi), 0.618 (1/phi), 1.414 (sqrt2), 1.571 (pi/2), 2.718 (e) via pythagorean_ratio\n - Mirror H/V come proiezioni via fractal_mirror\n - Pattern composti: lunghi come concatenazione di corti (Pattern 13 = Pattern 3 + Pattern 11 nel paper)\n - Cicli ricorrenti via Poincare: stesso pattern firato a distanze regolari\nCerca pattern che si REPLICANO su BTC e ETH al ~stesso timestamp (entro 36 barre = 3h su 5m TF).",
|
||||
|
||||
"instruction": "Genera una strategia che cerchi pattern frattali ricorrenti coerenti col framework Pythagoras-Malanga, riconoscibile come emanata dal tuo stile.",
|
||||
|
||||
"domain_warnings": "Crypto trada 24/7 senza CME gap. I numeri sacri (Solfeggio 396-417-528-639-741-852 Hz, 137.0359, 121.449, 161.86) sono prior teorici, NON leggi: usali come scale candidate per literal, non come dogma. Il paper Pythagoras e' esplicitamente non-falsificabile (cita 'consapevolezza del trader' come jolly): il backtest e' l'unico arbitro. La tolleranza +/-3h del paper su 5m TF = +/-36 barre per il bonus di asset-invariance.",
|
||||
|
||||
"anti_patterns": "Evita: (1) sequenza candle_pattern con piu di 7 simboli vincolati (overfitting); (2) pythagorean_ratio con tolleranza > 2% sui literal (numerologia spuria); (3) fractal_mirror con k = lookback_window (tautologico, sempre prossimo a 1.0); (4) letterali con piu di 4 decimali (es. 1.6180339 -> usa 1.618); (5) piu di 4 condizioni in AND; (6) crossover tra indicatori dello stesso tipo con lookback vicini (chattering); (7) time_in_market > 80% (leveraged buy&hold camuffato).",
|
||||
|
||||
"output_priorities": "Trade-off: (1) coerenza con la lente cognitiva: la strategia deve essere riconoscibile come emanata dal tuo stile (es. pythagorean usa ratios, candle_grammarian sequenze esplicite); (2) asset-invariance: segnali che attivano sia su BTC che su ETH entro 36 barre (e' il bonus della fitness); (3) selettivita: poche entry forti, alto SNR; (4) composizionalita: pattern lunghi come somma di corti; (5) robustezza vs random baseline (3-sigma richiesto dal skeptic_quant).",
|
||||
|
||||
"styles": {
|
||||
"pythagorean": {
|
||||
"directive": "Il mercato e un'armonia di ratios sacri: la sezione aurea (phi=1.618 e 1/phi=0.618), la radice di due (1.414), pi mezzi (1.571) ed e mezzi (1.359) strutturano i livelli di supporto, resistenza e target. Usa pythagorean_ratio con lookback Fibonacci (89, 144) per misurare l'estensione del range recente e cerca punti dove esso e' prossimo a phi entro 0.5%: indica un swing maturo che tende all'inversione armonica. Combina con candle_pattern di 3-5 candele per timing. Evita di chiudere troppo presto: la potenza di un ratio sacro si manifesta quando il prezzo passa il livello e poi torna a testarlo. Mai usare letterali con piu di 4 decimali: l'armonia non e' nei decimali ma nella struttura. Lookback consigliato: 89-144. Archetipo dominante: musicista che ascolta gli accordi nascosti del mercato.",
|
||||
"focus_metrics": ["pythagorean_ratio", "candle_pattern", "sma_pct", "realized_vol"]
|
||||
},
|
||||
"fractal_geometer": {
|
||||
"directive": "Il mercato e autosimilare: una sequenza di 3 candele si ripete dilatata su 6, 12 candele e oltre. La tua firma e' detectare la stessa struttura a scale diverse. Usa candle_pattern di lunghezza 3 per identificare l'unita atomica, poi cerca conferma con fractal_mirror su finestra 2x o 3x: se la correlation e' alta significa che la scala maggiore replica la struttura. Pythagorean_ratio con lookback grande (>100) misura l'ampiezza globale del frattale e i suoi target di proiezione. Atr_pct conferma che la volatilita corrente e coerente con la scala. Evita pattern di lunghezza > 6: i frattali complessi vanno composti, non vincolati esplicitamente. Mai usare fractal_mirror con k uguale al lookback_window. Lookback consigliato: 48-144. Archetipo dominante: geometra che misura la dimensione di Hausdorff del prezzo.",
|
||||
"focus_metrics": ["candle_pattern", "fractal_mirror", "atr_pct", "pythagorean_ratio"]
|
||||
},
|
||||
"fourier_analyst": {
|
||||
"directive": "Il mercato e somma di seni: una funzione complessa scomposta in frequenze armoniche distinte. Il tuo compito e' isolare le componenti ricorrenti dominanti. Usa sma_pct su 3 lookback diversi (es. 30, 60, 120) come proxy delle componenti a bassa frequenza; quando 2 su 3 si allineano nella stessa direzione, la frequenza dominante e' chiara e attiva l'entry. Realized_vol misura l'energia totale del segnale; un calo improvviso indica trasformazione di fase. Candle_pattern di 4-6 candele identifica l'inviluppo locale del segnale composito. Cerca pattern dove il presente e' la proiezione del passato dopo una traslazione temporale costante (mappa di Poincare). Evita oscillatori dello stesso tipo a lookback vicini: producono chattering inutile. Lookback consigliato: 60-200. Archetipo dominante: ingegnere del segnale che ricostruisce la portante armonica nascosta.",
|
||||
"focus_metrics": ["sma_pct", "realized_vol", "candle_pattern", "atr"]
|
||||
},
|
||||
"evideonic_projector": {
|
||||
"directive": "Il mercato e il presente proiettato dal passato via due operazioni geometriche: mirror H (inversione temporale) e mirror V (inversione prezzo), poi scalato per phi, 1/phi, sqrt2. Il tuo segnale principale e' fractal_mirror su entrambi gli assi: quando l'h mirror su 6-12 candele e' altamente positivo (>0.7), il pattern recente e' un riflesso temporale di un pattern precedente, segnale di prosecuzione armonica; quando il v mirror e' positivo, e' un riflesso direzionale, segnale di inversione imminente. Usa pythagorean_ratio per fissare i target di proiezione (entry moltiplicata per phi). Candle_pattern serve da gate per filtrare il contesto. Evita target con ratio > 3 (zona di rumore, fuori scala phi). Lookback consigliato: 24-96. Archetipo dominante: proiettore evideonico che riflette ogni evento nei suoi quattro alter-ego speculari.",
|
||||
"focus_metrics": ["fractal_mirror", "pythagorean_ratio", "candle_pattern", "sma_pct"]
|
||||
},
|
||||
"candle_grammarian": {
|
||||
"directive": "Il mercato e una lingua di tre simboli atomici: U (close>open), D (close<open), doji (close circa open). Ogni parola di 3-12 lettere e' una frase compiuta con significato direzionale. Il tuo dominio e' la sintassi diretta: usa candle_pattern come segnale principale, con sequenze di lunghezza 3-5 per la decisione di entry. Cerca pattern dove l'ultima candela e' U dopo una serie 'DD' o '0D' (reversal classico) o sequenze 'UU0U' che indicano accumulazione con pausa. Volume conferma la pronuncia del pattern: alto volume = pattern ben articolato. Atr modula la dimensione di stop e target. Combina al massimo due candle_pattern indipendenti in AND, mai di piu. Mai pattern di lunghezza > 7 (frasi troppo lunghe = overfitting). Lookback consigliato: 12-48. Archetipo dominante: grammatico che decifra la sintassi delle candele.",
|
||||
"focus_metrics": ["candle_pattern", "volume", "atr", "realized_vol"]
|
||||
},
|
||||
"recurrence_theorist": {
|
||||
"directive": "Il mercato e regolato dal teorema di Poincare: ogni configurazione torna prima o poi vicino a se stessa nel tempo. Il tuo lavoro e' trovare il pattern di oggi che firo' anche ieri o la settimana scorsa a distanza k regolare. Usa candle_pattern su 5-8 candele come firma riconoscibile, poi fractal_mirror con axis=0 (h, mirror temporale) su lookback grande (100-200) per misurare quanto la struttura corrente assomigli a quella passata invertita: se >0.5, il ciclo e' nella fase di ripetizione attiva. Pythagorean_ratio fornisce i target di estensione del ciclo. Sma_pct su lookback medio conferma il regime macro coerente. Evita pattern troppo corti (3 candele): troppo poco distinti per ricorrenza affidabile. Lookback consigliato: 100-200. Archetipo dominante: archeologo del prezzo che cerca repliche di antichi pattern dimenticati.",
|
||||
"focus_metrics": ["candle_pattern", "fractal_mirror", "pythagorean_ratio", "sma_pct"]
|
||||
},
|
||||
"skeptic_quant": {
|
||||
"directive": "Il mercato e rumore con piccoli edge statistici nascosti tra il caos. Tu sei l'anticorpo del sistema: non fidarti mai dei pattern senza significativita 3-sigma vs random baseline. Usa realized_vol e atr_pct come gate di regime (entra solo in regimi di volatilita media, percentile 30-70). Sma_pct conferma la direzione del trend macro corrente. Candle_pattern di 3-4 candele puo' essere usato, ma solo come conferma di un setup gia validato da volatilita e trend: mai come segnale primario isolato. Vincolo duro: max 3 condizioni in AND (poche e robuste); literal con almeno il 20% di margine di sicurezza tra entry ed exit. Nessun pythagorean_ratio (numerologia spuria) ne fractal_mirror (tautologici nella tua lente scettica). Lookback: 60-150. Archetipo dominante: scettico quantitativo che falsifica prima di confermare.",
|
||||
"focus_metrics": ["realized_vol", "atr_pct", "sma_pct", "candle_pattern"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
"""Unit-test dei 3 indicatori Pythagoras."""
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from strategy_pythagoras.indicators import (
|
||||
candle_pattern,
|
||||
fractal_mirror,
|
||||
pythagorean_ratio,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ohlcv_30() -> pd.DataFrame:
|
||||
"""30 candele sintetiche: pattern alternato U,D,U,D,..."""
|
||||
n = 30
|
||||
close = np.array([100.0 + i for i in range(n)]) # monotone
|
||||
open_ = close - np.tile([1.0, -1.0], n // 2) # U,D,U,D,...
|
||||
return pd.DataFrame({"open": open_, "high": close + 0.5, "low": open_ - 0.5, "close": close, "volume": [1.0] * n})
|
||||
|
||||
|
||||
# -- candle_pattern -----------------------------------------------------------
|
||||
|
||||
def test_candle_pattern_matches_recent(ohlcv_30: pd.DataFrame) -> None:
|
||||
# Verifica che il symbol mapping U=0,D=1,doji=2 lavori correttamente.
|
||||
# Con la fixture: indices pari sono U (close>open), dispari sono D (close<open).
|
||||
# ultime 3 candele (idx 27,28,29) -> D,U,D
|
||||
out = candle_pattern(ohlcv_30, [3, 1, 0, 1]) # D, U, D
|
||||
assert out.iloc[-1] == 1.0
|
||||
out2 = candle_pattern(ohlcv_30, [3, 0, 0, 0]) # U, U, U: no match
|
||||
assert out2.iloc[-1] == 0.0
|
||||
|
||||
|
||||
def test_candle_pattern_zero_for_short_history(ohlcv_30: pd.DataFrame) -> None:
|
||||
out = candle_pattern(ohlcv_30, [3, 0, 0, 0])
|
||||
assert out.iloc[0] == 0.0
|
||||
assert out.iloc[1] == 0.0
|
||||
|
||||
|
||||
def test_candle_pattern_doji_symbol(ohlcv_30: pd.DataFrame) -> None:
|
||||
df = ohlcv_30.copy()
|
||||
# forza la candela [-1] doji: |close-open|/open < 0.001
|
||||
df.loc[df.index[-1], "open"] = df["close"].iloc[-1] * (1 - 1e-5)
|
||||
# ultime 3 dopo modifica: D (idx 27), U (idx 28), doji (idx 29)
|
||||
out = candle_pattern(df, [3, 1, 0, 2])
|
||||
assert out.iloc[-1] == 1.0
|
||||
|
||||
|
||||
# -- pythagorean_ratio --------------------------------------------------------
|
||||
|
||||
def test_pythagorean_ratio_basic(ohlcv_30: pd.DataFrame) -> None:
|
||||
out = pythagorean_ratio(ohlcv_30, [12])
|
||||
# ultimi 12 close: 118..129 -> max/min = 129/118
|
||||
expected = 129.0 / 118.0
|
||||
assert abs(out.iloc[-1] - expected) < 1e-9
|
||||
|
||||
|
||||
def test_pythagorean_ratio_no_lookahead(ohlcv_30: pd.DataFrame) -> None:
|
||||
out = pythagorean_ratio(ohlcv_30, [12])
|
||||
out0 = pythagorean_ratio(ohlcv_30.iloc[:13], [12])
|
||||
assert abs(out.iloc[12] - out0.iloc[-1]) < 1e-9
|
||||
|
||||
|
||||
# -- fractal_mirror -----------------------------------------------------------
|
||||
|
||||
def test_fractal_mirror_h_pattern_inverted(ohlcv_30: pd.DataFrame) -> None:
|
||||
# mirror temporale: correlazione tra close[-k:] e close[-k:][::-1]
|
||||
# Per close monotono, correlation(seq, seq_reversed) = -1
|
||||
out = fractal_mirror(ohlcv_30, [6, 0]) # axis=0 (h)
|
||||
assert out.iloc[-1] < -0.99
|
||||
|
||||
|
||||
def test_fractal_mirror_v_axis(ohlcv_30: pd.DataFrame) -> None:
|
||||
out = fractal_mirror(ohlcv_30, [6, 1])
|
||||
assert out.iloc[-1] < -0.99
|
||||
|
||||
|
||||
def test_fractal_mirror_clamps_initial(ohlcv_30: pd.DataFrame) -> None:
|
||||
out = fractal_mirror(ohlcv_30, [6, 0])
|
||||
assert len(out) == len(ohlcv_30)
|
||||
@@ -0,0 +1,67 @@
|
||||
"""prompts.json valido, 7 stili Pythagoras-aligned, schema v3.2."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from importlib.resources import files
|
||||
|
||||
import pytest
|
||||
|
||||
EXPECTED_STYLES = {
|
||||
"pythagorean", "fractal_geometer", "fourier_analyst",
|
||||
"evideonic_projector", "candle_grammarian", "recurrence_theorist",
|
||||
"skeptic_quant",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def prompts() -> dict:
|
||||
p = files("strategy_pythagoras") / "prompts.json"
|
||||
return json.loads(p.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def test_schema_version(prompts: dict) -> None:
|
||||
assert prompts["_schema"] == "3.2"
|
||||
|
||||
|
||||
def test_top_level_fields(prompts: dict) -> None:
|
||||
for k in (
|
||||
"agent_role", "pattern_guidance", "instruction", "domain_warnings",
|
||||
"anti_patterns", "output_priorities", "styles",
|
||||
):
|
||||
assert k in prompts, f"missing field: {k}"
|
||||
|
||||
|
||||
def test_styles_set(prompts: dict) -> None:
|
||||
assert set(prompts["styles"].keys()) == EXPECTED_STYLES
|
||||
|
||||
|
||||
def test_styles_have_directive_and_focus_metrics(prompts: dict) -> None:
|
||||
for name, style in prompts["styles"].items():
|
||||
assert "directive" in style, f"{name} missing directive"
|
||||
assert "focus_metrics" in style, f"{name} missing focus_metrics"
|
||||
assert len(style["focus_metrics"]) == 4, f"{name} focus_metrics len != 4"
|
||||
|
||||
|
||||
def test_directives_ascii_safe(prompts: dict) -> None:
|
||||
for name, style in prompts["styles"].items():
|
||||
directive = style["directive"]
|
||||
for ch in directive:
|
||||
assert ord(ch) <= 0x7F, f"{name} directive contains non-ASCII char {ch!r}"
|
||||
|
||||
|
||||
def test_directives_length(prompts: dict) -> None:
|
||||
for name, style in prompts["styles"].items():
|
||||
n = len(style["directive"])
|
||||
assert 800 <= n <= 950, f"{name} directive length {n} outside [800,950]"
|
||||
|
||||
|
||||
def test_directives_first_phrase(prompts: dict) -> None:
|
||||
for name, style in prompts["styles"].items():
|
||||
assert style["directive"].startswith("Il mercato e "), \
|
||||
f"{name} directive must start with 'Il mercato e '"
|
||||
|
||||
|
||||
def test_directives_end_archetype(prompts: dict) -> None:
|
||||
for name, style in prompts["styles"].items():
|
||||
assert "Archetipo dominante:" in style["directive"][-200:], \
|
||||
f"{name} missing Archetipo dominante in last 200 chars"
|
||||
@@ -0,0 +1,71 @@
|
||||
"""Validator accetta i 3 nuovi indicatori candle con arity corrette."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from multi_swarm_core.protocol.parser import parse_strategy
|
||||
from multi_swarm_core.protocol.validator import ValidationError, validate_strategy
|
||||
|
||||
|
||||
def _strategy_with_indicator(name: str, params: list[float]) -> str:
|
||||
return json.dumps(
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"condition": {
|
||||
"op": "gt",
|
||||
"args": [
|
||||
{"kind": "indicator", "name": name, "params": params},
|
||||
{"kind": "literal", "value": 0.5},
|
||||
],
|
||||
},
|
||||
"action": "entry-long",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_candle_pattern_valid_min_arity() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("candle_pattern", [3, 0, 1, 0]))
|
||||
validate_strategy(s)
|
||||
|
||||
|
||||
def test_candle_pattern_valid_max_arity() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("candle_pattern", [12] + [0] * 12))
|
||||
validate_strategy(s)
|
||||
|
||||
|
||||
def test_candle_pattern_rejects_too_few_params() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("candle_pattern", [3, 0, 1]))
|
||||
with pytest.raises(ValidationError):
|
||||
validate_strategy(s)
|
||||
|
||||
|
||||
def test_pythagorean_ratio_valid() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("pythagorean_ratio", [89]))
|
||||
validate_strategy(s)
|
||||
|
||||
|
||||
def test_pythagorean_ratio_rejects_zero_params() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("pythagorean_ratio", []))
|
||||
with pytest.raises(ValidationError):
|
||||
validate_strategy(s)
|
||||
|
||||
|
||||
def test_fractal_mirror_valid_h() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("fractal_mirror", [12, 0]))
|
||||
validate_strategy(s)
|
||||
|
||||
|
||||
def test_fractal_mirror_valid_v() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("fractal_mirror", [12, 1]))
|
||||
validate_strategy(s)
|
||||
|
||||
|
||||
def test_fractal_mirror_rejects_one_param() -> None:
|
||||
s = parse_strategy(_strategy_with_indicator("fractal_mirror", [12]))
|
||||
with pytest.raises(ValidationError):
|
||||
validate_strategy(s)
|
||||
@@ -0,0 +1,324 @@
|
||||
{
|
||||
"run_id": "7f65bd1832d94c638b588aab02fb223e",
|
||||
"run_name": "phase1-hardened-001",
|
||||
"n_folds": 4,
|
||||
"top_k_requested": 5,
|
||||
"top_k_evaluated": 5,
|
||||
"symbol": "BTC-PERPETUAL",
|
||||
"timeframe": "1h",
|
||||
"start": "2018-09-01T00:00:00+00:00",
|
||||
"end": "2026-01-01T00:00:00+00:00",
|
||||
"ohlcv_bars": 64297,
|
||||
"results": [
|
||||
{
|
||||
"genome_id": "9cf506b83bec55f6",
|
||||
"fitness_is": 0.2847465321576384,
|
||||
"sharpe_is": 0.6809931251900289,
|
||||
"folds": [
|
||||
{
|
||||
"fold": 0,
|
||||
"fitness": 0.4454407113532186,
|
||||
"sharpe": 0.940612398713799,
|
||||
"dsr": 0.09856838950479485,
|
||||
"dsr_pvalue": 0.9014316104952051,
|
||||
"return": 0.12691347502077277,
|
||||
"max_dd": 0.08467873586477132,
|
||||
"n_trades": 50,
|
||||
"test_start": "2022-05-02 12:00:00+00:00",
|
||||
"test_end": "2023-04-02 08:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 1,
|
||||
"fitness": 0.3348901947844796,
|
||||
"sharpe": 0.6215567075501345,
|
||||
"dsr": 0.05615871498414856,
|
||||
"dsr_pvalue": 0.9438412850158514,
|
||||
"return": 0.1669550204052912,
|
||||
"max_dd": 0.2425926649805225,
|
||||
"n_trades": 60,
|
||||
"test_start": "2023-04-02 09:00:00+00:00",
|
||||
"test_end": "2024-03-02 05:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 2,
|
||||
"fitness": 0.08496628060413243,
|
||||
"sharpe": -0.291593157960215,
|
||||
"dsr": 0.006828013272159182,
|
||||
"dsr_pvalue": 0.9931719867278408,
|
||||
"return": -0.06496567446731383,
|
||||
"max_dd": 0.1933746053658072,
|
||||
"n_trades": 72,
|
||||
"test_start": "2024-03-02 06:00:00+00:00",
|
||||
"test_end": "2025-01-31 02:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 3,
|
||||
"fitness": 0.10547685784422405,
|
||||
"sharpe": -0.04385303190774091,
|
||||
"dsr": 0.013045759744378084,
|
||||
"dsr_pvalue": 0.986954240255622,
|
||||
"return": -0.003908970230222186,
|
||||
"max_dd": 0.05825300658307936,
|
||||
"n_trades": 31,
|
||||
"test_start": "2025-01-31 03:00:00+00:00",
|
||||
"test_end": "2025-12-31 23:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"fitness_oos_mean": 0.24269351114651366,
|
||||
"fitness_oos_min": 0.08496628060413243,
|
||||
"fitness_oos_max": 0.4454407113532186,
|
||||
"fitness_oos_std": 0.15273583802070415,
|
||||
"sharpe_oos_mean": 0.3066807290989944,
|
||||
"sharpe_oos_min": -0.291593157960215,
|
||||
"robust_score": 0.08496628060413243
|
||||
},
|
||||
{
|
||||
"genome_id": "9f1f7ffa40b4900a",
|
||||
"fitness_is": 0.2653851546757454,
|
||||
"sharpe_is": 0.7183647852569522,
|
||||
"folds": [
|
||||
{
|
||||
"fold": 0,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2022-05-02 12:00:00+00:00",
|
||||
"test_end": "2023-04-02 08:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 1,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2023-04-02 09:00:00+00:00",
|
||||
"test_end": "2024-03-02 05:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 2,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2024-03-02 06:00:00+00:00",
|
||||
"test_end": "2025-01-31 02:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 3,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2025-01-31 03:00:00+00:00",
|
||||
"test_end": "2025-12-31 23:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"fitness_oos_mean": 0.0,
|
||||
"fitness_oos_min": 0.0,
|
||||
"fitness_oos_max": 0.0,
|
||||
"fitness_oos_std": 0.0,
|
||||
"sharpe_oos_mean": 0.0,
|
||||
"sharpe_oos_min": 0.0,
|
||||
"robust_score": 0.0
|
||||
},
|
||||
{
|
||||
"genome_id": "aff702f86c30e30f",
|
||||
"fitness_is": 0.22070023288031862,
|
||||
"sharpe_is": 0.49220575419744833,
|
||||
"folds": [
|
||||
{
|
||||
"fold": 0,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2022-05-02 12:00:00+00:00",
|
||||
"test_end": "2023-04-02 08:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 1,
|
||||
"fitness": 0.0,
|
||||
"sharpe": -1.3160615555061568,
|
||||
"dsr": 2.0480110912755528e-14,
|
||||
"dsr_pvalue": 0.9999999999999796,
|
||||
"return": -0.004197667464114874,
|
||||
"max_dd": 0.004197667464114874,
|
||||
"n_trades": 1,
|
||||
"test_start": "2023-04-02 09:00:00+00:00",
|
||||
"test_end": "2024-03-02 05:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 2,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2024-03-02 06:00:00+00:00",
|
||||
"test_end": "2025-01-31 02:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 3,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2025-01-31 03:00:00+00:00",
|
||||
"test_end": "2025-12-31 23:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"fitness_oos_mean": 0.0,
|
||||
"fitness_oos_min": 0.0,
|
||||
"fitness_oos_max": 0.0,
|
||||
"fitness_oos_std": 0.0,
|
||||
"sharpe_oos_mean": -0.3290153888765392,
|
||||
"sharpe_oos_min": -1.3160615555061568,
|
||||
"robust_score": 0.0
|
||||
},
|
||||
{
|
||||
"genome_id": "43236904205071d5",
|
||||
"fitness_is": 0.22070023288031862,
|
||||
"sharpe_is": 0.49220575419744833,
|
||||
"folds": [
|
||||
{
|
||||
"fold": 0,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2022-05-02 12:00:00+00:00",
|
||||
"test_end": "2023-04-02 08:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 1,
|
||||
"fitness": 0.0,
|
||||
"sharpe": -1.3160615555061568,
|
||||
"dsr": 2.0480110912755528e-14,
|
||||
"dsr_pvalue": 0.9999999999999796,
|
||||
"return": -0.004197667464114874,
|
||||
"max_dd": 0.004197667464114874,
|
||||
"n_trades": 1,
|
||||
"test_start": "2023-04-02 09:00:00+00:00",
|
||||
"test_end": "2024-03-02 05:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 2,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2024-03-02 06:00:00+00:00",
|
||||
"test_end": "2025-01-31 02:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 3,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 0.0,
|
||||
"dsr": 0.0,
|
||||
"dsr_pvalue": 1.0,
|
||||
"return": 0.0,
|
||||
"max_dd": 0.0,
|
||||
"n_trades": 0,
|
||||
"test_start": "2025-01-31 03:00:00+00:00",
|
||||
"test_end": "2025-12-31 23:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"fitness_oos_mean": 0.0,
|
||||
"fitness_oos_min": 0.0,
|
||||
"fitness_oos_max": 0.0,
|
||||
"fitness_oos_std": 0.0,
|
||||
"sharpe_oos_mean": -0.3290153888765392,
|
||||
"sharpe_oos_min": -1.3160615555061568,
|
||||
"robust_score": 0.0
|
||||
},
|
||||
{
|
||||
"genome_id": "78a445d3043e0771",
|
||||
"fitness_is": 0.20950868586433583,
|
||||
"sharpe_is": 0.2940379509455447,
|
||||
"folds": [
|
||||
{
|
||||
"fold": 0,
|
||||
"fitness": 0.07182313189843528,
|
||||
"sharpe": -0.5971649832941454,
|
||||
"dsr": 0.0029614586254508167,
|
||||
"dsr_pvalue": 0.9970385413745492,
|
||||
"return": -0.10657245144370586,
|
||||
"max_dd": 0.17076725093180228,
|
||||
"n_trades": 14,
|
||||
"test_start": "2022-05-02 12:00:00+00:00",
|
||||
"test_end": "2023-04-02 08:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 1,
|
||||
"fitness": 0.04914620786914511,
|
||||
"sharpe": -0.7350195766937807,
|
||||
"dsr": 0.0007433210570883239,
|
||||
"dsr_pvalue": 0.9992566789429117,
|
||||
"return": -0.03472119511680272,
|
||||
"max_dd": 0.060780295013844174,
|
||||
"n_trades": 20,
|
||||
"test_start": "2023-04-02 09:00:00+00:00",
|
||||
"test_end": "2024-03-02 05:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 2,
|
||||
"fitness": 0.0,
|
||||
"sharpe": 1.2071345821102195,
|
||||
"dsr": 0.14855640183813817,
|
||||
"dsr_pvalue": 0.8514435981618618,
|
||||
"return": 0.12296871148346566,
|
||||
"max_dd": 0.05065940776643054,
|
||||
"n_trades": 9,
|
||||
"test_start": "2024-03-02 06:00:00+00:00",
|
||||
"test_end": "2025-01-31 02:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"fold": 3,
|
||||
"fitness": 0.0,
|
||||
"sharpe": -1.6874561123211371,
|
||||
"dsr": 9.510326955509528e-06,
|
||||
"dsr_pvalue": 0.9999904896730445,
|
||||
"return": -0.03197955364502458,
|
||||
"max_dd": 0.032239270519921044,
|
||||
"n_trades": 7,
|
||||
"test_start": "2025-01-31 03:00:00+00:00",
|
||||
"test_end": "2025-12-31 23:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"fitness_oos_mean": 0.0302423349418951,
|
||||
"fitness_oos_min": 0.0,
|
||||
"fitness_oos_max": 0.07182313189843528,
|
||||
"fitness_oos_std": 0.03128704497338385,
|
||||
"sharpe_oos_mean": -0.4531265225497109,
|
||||
"sharpe_oos_min": -1.6874561123211371,
|
||||
"robust_score": 0.0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -18,6 +18,7 @@ members = [
|
||||
"multi-swarm-coevolutive",
|
||||
"multi-swarm-core",
|
||||
"strategy-crypto",
|
||||
"strategy-pythagoras",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -900,6 +901,7 @@ source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "multi-swarm-core" },
|
||||
{ name = "strategy-crypto" },
|
||||
{ name = "strategy-pythagoras" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
@@ -917,6 +919,7 @@ dev = [
|
||||
requires-dist = [
|
||||
{ name = "multi-swarm-core", editable = "src/multi_swarm_core" },
|
||||
{ name = "strategy-crypto", editable = "src/strategy_crypto" },
|
||||
{ name = "strategy-pythagoras", editable = "src/strategy_pythagoras" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
@@ -1980,6 +1983,27 @@ requires-dist = [
|
||||
{ name = "pyarrow", specifier = ">=18.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strategy-pythagoras"
|
||||
version = "0.1.0"
|
||||
source = { editable = "src/strategy_pythagoras" }
|
||||
dependencies = [
|
||||
{ name = "multi-swarm-core" },
|
||||
{ name = "nicegui" },
|
||||
{ name = "pandas" },
|
||||
{ name = "plotly" },
|
||||
{ name = "pyarrow" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "multi-swarm-core", editable = "src/multi_swarm_core" },
|
||||
{ name = "nicegui", specifier = ">=3.11.1" },
|
||||
{ name = "pandas", specifier = ">=2.2" },
|
||||
{ name = "plotly", specifier = ">=5.24" },
|
||||
{ name = "pyarrow", specifier = ">=18.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tenacity"
|
||||
version = "9.1.4"
|
||||
|
||||
Reference in New Issue
Block a user