refactor(prompt): SYSTEM_TEMPLATE diventa compositor — strategy controlla agent_role/pattern_guidance/instruction

Refactor architetturale: il prompt non e' piu' un template hardcoded ma viene
COMPOSTO at-runtime da scaffold core + contenuto strategy.

CORE scaffold (universal, fisso):
  - Grammar JSON spec (legato al protocol/compiler)
  - UNITA' regole semantiche
  - VINCOLI del validator
  - Esempio output

STRATEGY content (tunable in prompts.json):
  - agent_role: "Sei un agente generatore di ipotesi ... [crypto/forex/equity]"
  - pattern_guidance: sezione di archetipi tecnici, ora crypto-specific
  - instruction: frase finale del USER ("Genera una strategia ... [crypto]")
  - domain_warnings: NEW opzionale, per disclaimer di dominio (es. crypto 24/7)

Implementazione:
  - PromptLibrary v3.0: 4 nuovi campi top-level (agent_role, pattern_guidance,
    instruction, domain_warnings), parsati da prompts.json, default fallback in default()
  - hypothesis.py: SYSTEM_TEMPLATE constant rimossa, sostituita da
    _build_system_prompt(lib, genome) che compone scaffold + content
  - USER_TEMPLATE: ultima riga ora ha placeholder {instruction}
  - prompts.json v3.0 in strategy_crypto: agent_role + pattern_guidance +
    instruction + domain_warnings popolati con flavor crypto-specific

Pattern: "core = framework, strategy = contenuto". Per future strategie
(forex, equity), basta creare un prompts.json con flavor diverso, zero
modifiche al core. Universal scaffold (grammar, vincoli, units) e'
condiviso e mantiene la garanzia di parse/validate.

Test: +5 unit (compositor + PromptLibrary fields).
Tot: 232 test pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-05-15 21:18:54 +00:00
parent 898b24b6a3
commit 5202eb517b
5 changed files with 294 additions and 44 deletions
@@ -56,12 +56,9 @@ class HypothesisProposal:
n_attempts: int = 1
SYSTEM_TEMPLATE = """\
Sei un agente generatore di ipotesi di trading quantitativo per un sistema swarm.
Il tuo stile cognitivo: {cognitive_style}
Direttiva personale: {system_prompt}
# === CORE SCAFFOLD constants (universal, legato al protocol/compiler) ===
_SYSTEM_GRAMMAR_SPEC = """\
Devi proporre una strategia di trading espressa in JSON STRETTO.
La risposta deve essere un singolo oggetto JSON dentro fence ```json...```
con questa shape:
@@ -136,35 +133,18 @@ Esempi di gating temporale:
{{"op": "eq", "args": [{{"kind": "feature", "name": "is_weekend"}}, {{"kind": "literal", "value": 1}}]}}
Leaf - letterale numerico:
{{"kind": "literal", "value": 70.0}}
PATTERN GUIDANCE (oltre agli indicatori, considera forma delle curve e ripetibilità):
Forme di curva:
- Trend ascendente: SMA(short) > SMA(long) E close > SMA(short)
- Trend discendente: SMA(short) < SMA(long) E close < SMA(short)
- Compressione di volatilità (pre-breakout): atr_pct(N) < 0.01 (sotto 1% del prezzo)
- Espansione di volatilità: atr_pct(N) > 0.03 (sopra 3%) OPPURE ATR(N) > ATR(N*2) confronto relativo
- Mean reversion strutturale: sma_pct(long) > 0.05 (close 5% sopra media) OR sma_pct(long) < -0.05
- Momentum positivo conferma: macd_pct(12,26,9) > 0.005 (> 0.5% del prezzo)
Ripetibilità dell'andamento:
- Eventi crossover/crossunder ricorrenti (golden/death cross, RSI cross zone)
- Pattern intra-day: usa 'hour' per sfruttare orari di forte volatilità ricorrente
- Pattern settimanali: usa 'dow' o 'is_weekend' per cicli mercato
- Doppio top approx: RSI > 70 + crossunder RSI 70 (1° picco), poi entro N bar
nuovo crossover RSI 70 a livello close simile → entry short
- Range breakout: close > SMA(N) con SMA(short) > SMA(long) (compressione + spinta)
Cerca pattern che si REPLICANO nei dati storici, non singoli eventi rari.
{{"kind": "literal", "value": 70.0}}"""
_SYSTEM_CONSTRAINTS = """\
VINCOLI
- Gli indicator NON sono annidabili: 'params' accetta solo numeri, mai altri nodi.
- Le regole sono valutate in ordine; la prima che matcha vince per ogni timestamp.
- Default action se nessuna regola matcha = flat.
- 'op' e 'kind' sono mutuamente esclusivi sullo stesso nodo.
Rispondi SOLO con il fence ```json...``` contenente l'oggetto strategy.
Rispondi SOLO con il fence ```json...``` contenente l'oggetto strategy."""
_SYSTEM_EXAMPLE = """\
Esempio:
```json
@@ -186,8 +166,40 @@ Esempio:
}}
]
}}
```
"""
```"""
def _build_system_prompt(lib: PromptLibrary, genome: HypothesisAgentGenome) -> str:
"""Compone il SYSTEM prompt da scaffold core + contenuto strategy-specific."""
parts: list[str] = []
# 1. Header strategy-specific (da prompts.json)
parts.append(lib.agent_role)
parts.append("")
# 2. Cognitive style + directive (sempre, dal genome)
parts.append(f"Il tuo stile cognitivo: {genome.cognitive_style}")
parts.append(f"Direttiva personale: {genome.system_prompt}")
parts.append("")
# 3. Grammar spec (core scaffold)
parts.append(_SYSTEM_GRAMMAR_SPEC)
# 4. Pattern guidance (da prompts.json, opzionale)
if lib.pattern_guidance:
parts.append(
"\nPATTERN GUIDANCE (oltre agli indicatori, considera forma delle curve e ripetibilità):\n"
)
parts.append(lib.pattern_guidance)
parts.append(
"\n Cerca pattern che si REPLICANO nei dati storici, non singoli eventi rari.\n"
)
# 5. Domain warnings (da prompts.json, opzionale)
if lib.domain_warnings:
parts.append("\nWARNING DI DOMINIO:\n")
parts.append(lib.domain_warnings)
parts.append("")
# 6. Vincoli (core scaffold)
parts.append(_SYSTEM_CONSTRAINTS)
# 7. Esempio (core scaffold)
parts.append(_SYSTEM_EXAMPLE)
return "\n".join(parts)
USER_TEMPLATE = """\
@@ -213,7 +225,7 @@ Geometria & frattali:
Feature accessibili dal tuo genoma: {feature_access}.
Lookback massimo che puoi usare nel ragionamento: {lookback_window} barre.
Genera una strategia che cerchi anomalie sfruttabili in questo regime.
{instruction}
"""
@@ -361,10 +373,7 @@ class HypothesisAgent:
genome: HypothesisAgentGenome,
market: MarketSummary,
) -> HypothesisProposal:
system = SYSTEM_TEMPLATE.format(
cognitive_style=genome.cognitive_style,
system_prompt=genome.system_prompt,
)
system = _build_system_prompt(self._prompt_library, genome)
dominant_cycle_str = (
f"{market.dominant_cycle:.0f} barre"
if market.dominant_cycle is not None
@@ -372,6 +381,10 @@ class HypothesisAgent:
)
focus_keys = self._prompt_library.focus_metrics_for(genome.cognitive_style)
focus_block = _render_focus_block(focus_keys, market) if focus_keys else ""
instruction = (
self._prompt_library.instruction
or "Genera una strategia che cerchi anomalie sfruttabili in questo regime."
)
original_user = USER_TEMPLATE.format(
symbol=market.symbol,
timeframe=market.timeframe,
@@ -396,6 +409,7 @@ class HypothesisAgent:
dominant_cycle_str=dominant_cycle_str,
feature_access=", ".join(genome.feature_access),
lookback_window=genome.lookback_window,
instruction=instruction,
) + focus_block
completions: list[CompletionResult] = []
@@ -15,7 +15,11 @@ Schema JSON atteso::
"styles": {
"<name>": {"directive": "<testo system_prompt>"},
...
}
},
"agent_role": "<descrizione agente, opzionale>",
"pattern_guidance": "<sezione PATTERN GUIDANCE, opzionale>",
"instruction": "<frase finale USER, opzionale>",
"domain_warnings": "<warning di dominio, opzionale>"
}
I 6 stili default (physicist, biologist, historian, meteorologist,
@@ -26,7 +30,7 @@ per backcompat con test/script senza file esterno.
from __future__ import annotations
import json
from dataclasses import dataclass
from dataclasses import dataclass, field
from pathlib import Path
_DEFAULT_STYLES: dict[str, str] = {
@@ -61,12 +65,39 @@ class PromptLibraryError(ValueError):
"""Sollevata su JSON malformato o stili invalid."""
_DEFAULT_PATTERN_GUIDANCE = (
"Forme di curva: trend (SMA cross), compressione volatilita (atr_pct basso), "
"espansione volatilita (atr_pct alto), mean reversion (sma_pct estremo), "
"momentum (macd_pct, rsi zone).\n\n"
" Ripetibilita dell'andamento: crossover/crossunder ricorrenti, pattern intra-day "
"(hour gate), pattern settimanali (dow/is_weekend), range breakout."
)
_DEFAULT_AGENT_ROLE = (
"Sei un agente generatore di ipotesi di trading quantitativo per un sistema swarm."
)
_DEFAULT_INSTRUCTION = (
"Genera una strategia che cerchi anomalie sfruttabili in questo regime."
)
@dataclass(frozen=True)
class PromptLibrary:
"""Set immutabile di stili cognitivi + direttive system_prompt."""
"""Set immutabile di stili cognitivi + direttive system_prompt.
v3.0: aggiunge 4 campi top-level strategy-specific iniettabili nel prompt
dal compositor ``_build_system_prompt``. Tutti opzionali con default sensati
per backcompat.
"""
styles: dict[str, str]
focus: dict[str, list[str]] # style -> lista metriche prioritarie
# NEW v3.0: contenuto strategy-specific iniettabile nel prompt
agent_role: str = field(default="") # header SYSTEM, descrive chi e' l'agente
pattern_guidance: str = field(default="") # sezione PATTERN GUIDANCE del SYSTEM
instruction: str = field(default="") # frase finale USER ("Genera una strategia...")
domain_warnings: str = field(default="") # opzionale: warning di dominio (es. crypto 24/7)
def __post_init__(self) -> None:
if not self.styles:
@@ -78,15 +109,48 @@ class PromptLibrary:
raise PromptLibraryError(
f"directive vuota o invalida per stile {name!r}"
)
# Validate new optional string fields: if provided must be non-whitespace
for field_name, value in (
("agent_role", self.agent_role),
("pattern_guidance", self.pattern_guidance),
("instruction", self.instruction),
("domain_warnings", self.domain_warnings),
):
if not isinstance(value, str):
raise PromptLibraryError(
f"campo '{field_name}' deve essere stringa, non {type(value)}"
)
if value and not value.strip():
raise PromptLibraryError(
f"campo '{field_name}' fornito ma contiene solo whitespace"
)
@classmethod
def default(cls) -> PromptLibrary:
"""Libreria builtin con i 6 stili originali (fallback senza file)."""
return cls(styles=dict(_DEFAULT_STYLES), focus={})
return cls(
styles=dict(_DEFAULT_STYLES),
focus={},
agent_role=_DEFAULT_AGENT_ROLE,
pattern_guidance=_DEFAULT_PATTERN_GUIDANCE,
instruction=_DEFAULT_INSTRUCTION,
domain_warnings="",
)
@classmethod
def from_json(cls, path: Path | str) -> PromptLibrary:
"""Carica da file JSON con schema ``{"styles": {<name>: {"directive": "...", "focus_metrics": [...]}}}``."""
"""Carica da file JSON.
Schema::
{
"styles": {<name>: {"directive": "...", "focus_metrics": [...]}},
"agent_role": "...", // opzionale
"pattern_guidance": "...", // opzionale
"instruction": "...", // opzionale
"domain_warnings": "..." // opzionale
}
"""
p = Path(path)
try:
data = json.loads(p.read_text(encoding="utf-8"))
@@ -120,7 +184,21 @@ class PromptLibrary:
f"focus_metrics di {name!r} deve essere lista, non {type(fm)}"
)
focus[name] = [str(x) for x in fm]
return cls(styles=styles, focus=focus)
# Parse new optional top-level fields (v3.0)
agent_role = data.get("agent_role", "")
pattern_guidance = data.get("pattern_guidance", "")
instruction = data.get("instruction", "")
domain_warnings = data.get("domain_warnings", "")
return cls(
styles=styles,
focus=focus,
agent_role=agent_role,
pattern_guidance=pattern_guidance,
instruction=instruction,
domain_warnings=domain_warnings,
)
@property
def cognitive_styles(self) -> tuple[str, ...]:
@@ -299,3 +299,74 @@ def test_propose_no_focus_block_when_style_not_in_library(mocker): # type: igno
call_kwargs = fake_llm.complete.call_args.kwargs
user_msg = call_kwargs["user"]
assert "Focus per la tua lente:" not in user_msg
def test_build_system_prompt_includes_role_and_guidance_from_library(mocker): # type: ignore[no-untyped-def]
"""agent_role e pattern_guidance da PromptLibrary appaiono nel SYSTEM prompt."""
fake_llm = mocker.MagicMock()
fake_llm.complete.return_value = CompletionResult(
text=VALID_STRATEGY_JSON,
input_tokens=200,
output_tokens=80,
tier=ModelTier.C,
model="qwen",
)
lib = PromptLibrary(
styles={"physicist": "Pensa come un fisico."},
focus={},
agent_role="ROLE_X",
pattern_guidance="GUIDE_X",
)
agent = HypothesisAgent(llm=fake_llm, prompt_library=lib)
agent.propose(make_genome(), make_summary())
call_kwargs = fake_llm.complete.call_args.kwargs
system_msg = call_kwargs["system"]
assert "ROLE_X" in system_msg
assert "GUIDE_X" in system_msg
def test_build_system_prompt_skips_empty_optional_sections(mocker): # type: ignore[no-untyped-def]
"""domain_warnings='' e pattern_guidance='' → sezioni opzionali assenti nel SYSTEM."""
fake_llm = mocker.MagicMock()
fake_llm.complete.return_value = CompletionResult(
text=VALID_STRATEGY_JSON,
input_tokens=200,
output_tokens=80,
tier=ModelTier.C,
model="qwen",
)
lib = PromptLibrary(
styles={"physicist": "Pensa come un fisico."},
focus={},
domain_warnings="",
pattern_guidance="",
)
agent = HypothesisAgent(llm=fake_llm, prompt_library=lib)
agent.propose(make_genome(), make_summary())
call_kwargs = fake_llm.complete.call_args.kwargs
system_msg = call_kwargs["system"]
assert "WARNING DI DOMINIO" not in system_msg
assert "PATTERN GUIDANCE" not in system_msg
def test_user_template_uses_instruction_from_library(mocker): # type: ignore[no-untyped-def]
"""instruction da PromptLibrary appare nel USER message; default non viene usato."""
fake_llm = mocker.MagicMock()
fake_llm.complete.return_value = CompletionResult(
text=VALID_STRATEGY_JSON,
input_tokens=200,
output_tokens=80,
tier=ModelTier.C,
model="qwen",
)
lib = PromptLibrary(
styles={"physicist": "Pensa come un fisico."},
focus={},
instruction="INSTR_X",
)
agent = HypothesisAgent(llm=fake_llm, prompt_library=lib)
agent.propose(make_genome(), make_summary())
call_kwargs = fake_llm.complete.call_args.kwargs
user_msg = call_kwargs["user"]
assert "INSTR_X" in user_msg
assert "Genera una strategia che cerchi anomalie sfruttabili in questo regime." not in user_msg
@@ -0,0 +1,78 @@
"""Unit tests per PromptLibrary v3.0 — nuovi campi top-level."""
from __future__ import annotations
import json
import tempfile
from pathlib import Path
import pytest
from multi_swarm_core.genome.prompt_library import PromptLibrary, PromptLibraryError
def _write_json(data: dict, tmp_path: Path) -> Path:
p = tmp_path / "prompts.json"
p.write_text(json.dumps(data), encoding="utf-8")
return p
def test_from_json_loads_new_top_level_fields(tmp_path: Path) -> None:
"""from_json() legge agent_role, pattern_guidance, instruction, domain_warnings."""
data = {
"styles": {"physicist": {"directive": "Cerca leggi conservative."}},
"agent_role": "Sei un agente test.",
"pattern_guidance": "Forme di curva: trend, compressione.",
"instruction": "Genera qualcosa di utile.",
"domain_warnings": "Attenzione: mercato 24/7.",
}
lib = PromptLibrary.from_json(_write_json(data, tmp_path))
assert lib.agent_role == "Sei un agente test."
assert lib.pattern_guidance == "Forme di curva: trend, compressione."
assert lib.instruction == "Genera qualcosa di utile."
assert lib.domain_warnings == "Attenzione: mercato 24/7."
def test_from_json_defaults_new_fields_when_absent(tmp_path: Path) -> None:
"""from_json() usa stringa vuota come default per i 4 campi opzionali."""
data = {
"styles": {"physicist": {"directive": "Cerca leggi conservative."}},
}
lib = PromptLibrary.from_json(_write_json(data, tmp_path))
assert lib.agent_role == ""
assert lib.pattern_guidance == ""
assert lib.instruction == ""
assert lib.domain_warnings == ""
def test_default_provides_universal_fallbacks() -> None:
"""PromptLibrary.default() ha agent_role, pattern_guidance, instruction non vuoti; domain_warnings vuoto."""
lib = PromptLibrary.default()
assert lib.agent_role != ""
assert lib.pattern_guidance != ""
assert lib.instruction != ""
assert lib.domain_warnings == ""
def test_prompt_library_rejects_whitespace_only_fields() -> None:
"""Campi opzionali forniti ma solo whitespace sollevano PromptLibraryError."""
with pytest.raises(PromptLibraryError, match="agent_role"):
PromptLibrary(
styles={"physicist": "Cerca leggi."},
focus={},
agent_role=" ",
)
def test_prompt_library_accepts_empty_string_for_optional_fields() -> None:
"""Stringa vuota '' e' accettata per tutti i campi opzionali."""
lib = PromptLibrary(
styles={"physicist": "Cerca leggi."},
focus={},
agent_role="",
pattern_guidance="",
instruction="",
domain_warnings="",
)
assert lib.agent_role == ""
assert lib.domain_warnings == ""
@@ -1,9 +1,18 @@
{
"_comment": "Stili cognitivi e direttive del system_prompt per il GA di strategy_crypto. Modifica liberamente: cambia 'directive' di uno stile esistente o aggiungi nuove voci a 'styles'. Il nome dello stile (key) viene usato come 'cognitive_style' del genoma; la 'directive' diventa il system_prompt iniziale.",
"_schema": "2.2",
"_changelog": "v2.2 - Aggiunte 5 metriche geometrico-frattali (efficiency_ratio, tail_index, structural_uptrend, compression, spectral_entropy+dominant_cycle). Ogni stile dichiara focus_metrics: lista delle metriche prioritarie renderizzate come 'Focus per la tua lente' nel USER_TEMPLATE. Le metriche restano visibili a tutti gli stili (landscape GA smooth); il focus_metrics e' enfasi, non filtering. v3.0 - Riscrittura per accentuare differenziazione cognitiva. Ogni directive ora ha: (1) metafora ancorante riconoscibile e robusta a mutazione, (2) interpretazione operativa dei 6 input (skew/kurt/AR/Hurst/vol_pct/seasonality), (3) hint di struttura grammar-aware (and/or/crossover) senza prescrivere indicatori, (4) bias di scala temporale (lookback breve/medio/lungo), (5) archetipo dominante distintivo. Linguaggio attivo/generativo invece di 'preferisci'. ASCII-safe rigoroso. v2.1 - directive estese con interpretazione dei 4 nuovi input statistici. v2.0 - Riprogettato per blind-generator GA, 7 lenti mappate sulle statistiche aggregate.",
"_design_notes": "Le directive sono BIAS DI ESPLORAZIONE, non template. Ogni lente ha una METAFORA DOMINANTE (es. physicist = sistema fisico; biologist = ecosistema) che funge da ancora semantica resistente alle riscritture di mutate_prompt_llm. Gli hint di struttura grammar-aware orientano la FORMA della strategia (numero condizioni, tipo combinazione) senza prescrivere quali indicatori usare: il GA scopre l'incarnazione. Gli hint di scala temporale producono bias naturali sul lookback_window (parametro evoluto). Differenziazione: ogni lente ha un ARCHETIPO DOMINANTE distinto (physicist=mean-reversion+momentum simmetrico; biologist=contrarian su crowding; historian=cicli e fasi; meteorologist=regime-switching; engineer=robustezza+SNR; military_strategist=offesa/difesa adattiva; psychologist=fade estremi emotivi).",
"_comment": "Stili cognitivi e direttive del system_prompt per il GA di strategy_crypto. Modifica liberamente: cambia 'directive' di uno stile esistente o aggiungi nuove voci a 'styles'. Il nome dello stile (key) viene usato come 'cognitive_style' del genoma; la 'directive' diventa il system_prompt iniziale. v3.0: aggiunti campi top-level agent_role, pattern_guidance, instruction, domain_warnings per il compositor del SYSTEM prompt.",
"_schema": "3.0",
"_changelog": "v2.2 - Aggiunte 5 metriche geometrico-frattali (efficiency_ratio, tail_index, structural_uptrend, compression, spectral_entropy+dominant_cycle). Ogni stile dichiara focus_metrics: lista delle metriche prioritarie renderizzate come 'Focus per la tua lente' nel USER_TEMPLATE. Le metriche restano visibili a tutti gli stili (landscape GA smooth); il focus_metrics e' enfasi, non filtering. v2.1 - directive estese con interpretazione dei 4 nuovi input statistici. v2.0 - Riprogettato per blind-generator GA, 7 lenti mappate sulle statistiche aggregate. Stile precedente v3.0 changelog delle directive (differenziazione cognitiva) ora riflesso internamente nelle directive stesse. v3.0 - Refactor compositore: prompts.json ora controlla agent_role, pattern_guidance, instruction (top-level). Core fornisce solo lo SCAFFOLD universale (grammar JSON spec, units rules, vincoli del validator, esempio output). Pattern del core: 'core = framework, strategy = contenuto'. Strategy puo' tunare interamente il flavor del prompt senza toccare codice.",
"_design_notes": "Le directive sono BIAS DI ESPLORAZIONE, non template. Ogni lente ha una METAFORA DOMINANTE (es. physicist = sistema fisico; biologist = ecosistema) che funge da ancora semantica resistente alle riscritture di mutate_prompt_llm. Gli hint di struttura grammar-aware orientano la FORMA della strategia (numero condizioni, tipo combinazione) senza prescrivere quali indicatori usare: il GA scopre l'incarnazione. Gli hint di scala temporale producono bias naturali sul lookback_window (parametro evoluto). Differenziazione: ogni lente ha un ARCHETIPO DOMINANTE distinto (physicist=mean-reversion+momentum simmetrico; biologist=contrarian su crowding; historian=cicli e fasi; meteorologist=regime-switching; engineer=robustezza+SNR; military_strategist=offesa/difesa adattiva; psychologist=fade estremi emotivi). v3.0: agent_role/pattern_guidance/instruction/domain_warnings sono contenuto strategy-specific iniettato nel compositor; il CORE scaffold (grammar, units, vincoli, esempio) e' universale e non modificabile da qui.",
"_grammar_reference": "Operatori: and, or, not, gt, lt, eq, crossover, crossunder. Indicatori: sma, sma_pct, rsi, atr, atr_pct, realized_vol, macd, macd_pct. Features: OHLCV (filtered) + hour, dow, is_weekend, minute_of_hour. Azioni: entry-long, entry-short, exit, flat. Input statistici: mean, std, skew, kurt, vol_regime (low/medium/high), autocorr_lag1 (vs baseline), hurst, vol_pct (percentile storico), seasonality_hour, seasonality_dow, efficiency_ratio (Kaufman 0-1), tail_index_left/right (Hill), structural_uptrend (HH/HL 0-1), compression (range recent/ref), spectral_entropy (FFT 0-1), dominant_cycle (barre o N/A).",
"agent_role": "Sei un agente generatore di ipotesi di trading quantitativo per un sistema swarm coevolutivo specializzato in mercati crypto. Il tuo obiettivo e' proporre strategie che sfruttino regimi e anomalie del mercato crypto sottostante, contando sull'evoluzione GA per migliorarle nel tempo.",
"pattern_guidance": "Forme di curva:\n - Trend ascendente: SMA(short) > SMA(long) E close > SMA(short)\n - Trend discendente: SMA(short) < SMA(long) E close < SMA(short)\n - Compressione di volatilita (pre-breakout): atr_pct(N) < 0.01 (sotto 1% del prezzo)\n - Espansione di volatilita: atr_pct(N) > 0.03 OPPURE ATR(N) > ATR(N*2) (confronto relativo)\n - Mean reversion strutturale: sma_pct(long) > 0.05 OR sma_pct(long) < -0.05\n - Momentum positivo conferma: macd_pct(12,26,9) > 0.005\n\n Ripetibilita dell'andamento:\n - Eventi crossover/crossunder ricorrenti (golden/death cross, RSI cross zone)\n - Pattern intra-day: usa 'hour' per sfruttare orari di forte volatilita ricorrente\n - Pattern settimanali: usa 'dow' o 'is_weekend' per cicli mercato\n - Doppio top approx: RSI > 70 + crossunder RSI 70 (1 picco), poi entro N bar nuovo crossover RSI 70 a livello close simile -> entry short\n - Range breakout: close > SMA(N) con SMA(short) > SMA(long) (compressione + spinta)",
"instruction": "Genera una strategia che cerchi anomalie sfruttabili in questo regime crypto.",
"domain_warnings": "Crypto trada 24/7 senza CME gap: non assumere chiusure settimanali nelle strategie. Vol clustering esteso e tail pesanti caratterizzano BTC/ETH; evita ipotesi gaussiane. Funding rate non e' direttamente accessibile ma si riflette nei trend persistenti durante regimi di vol bassa.",
"styles": {
"physicist": {
"directive": "Il mercato e un sistema fisico con energia (std), simmetrie (skew) e memoria (autocorr). Costruisci strategie che sfruttano due regimi fisici opposti. Equilibrio (skew vicino a 0, kurt bassa, autocorr vicino a baseline, vol_pct medio): il sistema fa mean reversion verso il potenziale stabile, costruisci entry contrarian su deviazioni significative (combina sma_pct in soglia + conferma oscillatoria in AND). Non-equilibrio (skew marcato, autocorr > baseline + 0.05, hurst > 0.55): forzante asimmetrica attiva, costruisci entry di propagazione (crossover di momentum in AND con gate vol). Vol_pct > 80 + kurt bassa = energia compressa, breakout imminente. efficiency_ratio alto = movimento efficiente (poco dissipato), costruisci su di esso; efficiency_ratio < 0.2 = dissipazione totale, evita trend-following. Spectral_entropy bassa con dominant_cycle definito = modi armonici sfruttabili (gate temporale su ciclo). Lookback medio-lungo (150-300): le scale fisiche emergono su orizzonti, non su singole barre. Una entry deve avere razionale conservativo (cosa si conserva?), non pattern fortuito.",