# charter_core

**Package Python che centralizza le regole Charter del Trading Engine AI di Mattia.**

FASE 1-4 del piano di consolidamento 2026-07-14. **Installabile via `pip install -e .`** (FASE 4).

## Installazione (FASE 4)

```powershell
# Dalla root del workspace (G:\AI TRADING ENGINE)
pip install -e .

# Verifica
python -c "import charter_core; print(charter_core.__version__)"
# Output atteso: 1.1.0
```

Una volta installato, charter_core è disponibile da QUALSIASI working directory:

```powershell
# Da qualsiasi posizione
python -c "from charter_core import SETUPS, CHARTER, Bot, BybitClient; print('OK')"
```

Dipendenze OPZIONALI (non richieste per il core):

```powershell
# Per compute_indicators_pine_faithful (pandas, numpy)
pip install "charter_core[analysis]"

# Per BybitClient con bybit_demo_client (requests, python-dotenv)
pip install "charter_core[bybit]"

# Tutte e due
pip install "charter_core[all]"
```

## Cos'è

Prima di `charter_core`, le regole Charter (business rules) erano sparse in
più file del workspace `AI TRADING ENGINE`:

| File originale | Cosa conteneva |
|---|---|
| `live_deploy/live_engine.py` (righe 59-93) | Dizionario `SETUPS` per live (3 setup Charter) |
| `web_solver/web_solver_v6.py` (righe 30-37) | Dict `CHARTER` per backtest |
| `market_intelligence/market_profile.py` | Definizione 8 tipi CD (Candle Direction) |
| `live_deploy/live_loop_aligned.py` | `CLOSING_HOURS = [2,6,10,14,18,22]` |
| Magic number sparsi ovunque | `tp1_pct=0.03`, `sl_clamp=-0.03`, `fee=0.0006`, ecc. |

**Ora tutto vive in `charter_core/`.** Singola fonte di verità.

## Cosa contiene

```
charter_core/
├── __init__.py             # re-export di tutto il package
├── charter_config.py       # SETUPS (3) + CHARTER (backtest) + globali + loop
├── cd_types.py             # 8 tipi Candle Direction + parse_cd_mask + compute_cd
├── indicators.py           # Parametri BB/ROC/ATR/EMA/ADX (Pine-faithful)
├── paletti.py              # 14 "paletti" Mattia (regole business hard)
├── tests/
│   ├── __init__.py
│   └── test_smoke.py       # 9 smoke test
├── AGENTS.md               # AI-consultable
└── README.md               # questo file
```

## Quick start

```python
from charter_core import SETUPS, CHARTER, CLOSING_HOURS, total_paletti_count

# Lookup setup
zec = next(s for s in SETUPS if s["name"] == "ZEC_4H")
print(f"ZEC margin: {zec['margin_usdt']} USDT, leverage: {zec['leverage']}x")

# Fee Pine IMPLICITA (mai 0.1% Bybit std)
print(f"Fee: {CHARTER['commission_pct']*100:.4f}%")  # 0.0600%

# Schedule loop
print(f"Loop 4H: {CLOSING_HOURS}")  # [2, 6, 10, 14, 18, 22]

# Paletti totali
print(f"Paletti hard: {total_paletti_count()}")
```

## Test

```powershell
cd "G:\AI TRADING ENGINE"
python -m charter_core.tests.test_smoke
```

Output atteso: `TUTTI I 9 TEST SONO PASSATI`.

## Regola Mattia 2026-07-14

Quando un sub-repo (mavis, solver, verifier) ha bisogno di una regola
Charter (mask CD, fee, leva, TP, SL, regime filter, ecc.), **importa
da `charter_core`**. Non duplicare i magic number localmente.

Esempio di migrazione futura:

```python
# PRIMA (duplicato in live_engine.py + web_solver_v6.py)
SETUPS = [{"name": "ZEC_4H", "margin_usdt": 500, ...}, ...]
CHARTER = {"commission_pct": 0.0006, "tp1_pct": 0.03, ...}

# DOPO (import singolo)
from charter_core import SETUPS, CHARTER
```

## Roadmap

- **FASE 1** ✅: creare `charter_core/` con config, CD types,
  indicators, paletti, smoke test
- **FASE 2** ✅: migrare `live_engine.py` e `web_solver_v6.py`
  per importare da `charter_core` invece di ridefinire
- **FASE 3** ✅: aggiungere `charter_engine.py` con `class Bot, Setup, BybitClient`
  come wrapper riusabile
- **FASE 4**: package su PyPI interno o git submodule per distribuzione
  (opzionale, dipende da bisogni di distribuzione)

## File NON sostituiti (per ora)

- `live_deploy/live_engine.py` → continuerà a definire la logica pesante
  (place_entry_with_tpsl, MIL dynamic sizing, signal enhancer). FASE 3
  ha aggiunto `charter_engine` come AFFIANCAMENTO, non sostituzione.
- `web_solver/web_solver_v6.py` → continuerà a gestire la UI Flask
  del backtest.

Questo approccio "blocco unico additivo" permette refactoring graduale
senza rompere il sistema live.

## charter_engine usage (FASE 3)

```python
from charter_core import SETUPS
from charter_core.charter_engine import (
    Bot, BybitClient, Setup, Signal, setups_from_list,
    detect_signal_pine_faithful, check_regime_pine_faithful,
)
import pandas as pd

# Crea oggetti dai dict esistenti
setups = setups_from_list(SETUPS)  # list[Setup]

# Init client Bybit (lato live: carica env vars)
client = BybitClient()
if not client.is_available:
    raise SystemExit("Setta BYBIT_DEMO_API_KEY e BYBIT_DEMO_SECRET_KEY")

# Crea bot
bot = Bot(client=client, setups=setups)
status = bot.get_status()
print(f"Bot attivo su {len(status['setups'])} setup Charter")

# Processa 1 setup (con OHLCV DataFrame)
df = pd.read_csv("ZECUSDT_4H.csv")
setup = bot.get_setup("ZEC_4H")
signal = detect_signal_pine_faithful(df, setup)
if signal:
    ok, msg = check_regime_pine_faithful(df, signal)
    signal.regime_ok = ok
    signal.regime_msg = msg
    if ok:
        result = bot.place_entry(signal)
        print(f"Entry Charter eseguita: {result}")
```

## Test con mock (no API keys reali)

```python
# Mock BybitClient per test
class MockBybitClient:
    def set_leverage(self, symbol, lev, side): return {"retCode": 0, "bypassed": True}
    def round_qty(self, symbol, qty): return round(qty, 3)
    def get_qty_step(self, symbol): return 0.001
    def create_market_order(self, symbol, side, qty): return {"orderId": "mock-1"}
    def create_limit_order(self, symbol, side, qty, price, reduce_only=True): return {"orderId": "mock-2"}
    def set_trading_stop(self, symbol, sl_price): return {"retCode": 0}
    def fetch_positions(self, symbol=None): return []

mock = MockBybitClient()
client = BybitClient(client=mock)  # injection
bot = Bot(client=client, setups=setups)
# ... usa bot normalmente, le chiamate vanno al mock
```
