Skip to content

Commit 11c16dd

Browse files
committed
cleaned portfolio, finalized and simplified risk manager
1 parent 2dad4ad commit 11c16dd

4 files changed

Lines changed: 515 additions & 1050 deletions

File tree

docs/portfolio-risk_manager.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
Perfetto! Ti preparo uno **schema concettuale** che mostra come le classi `Portfolio`, `Position` e `Trade` interagiscono con le tabelle del DB e con il modulo `risk_manager.py`.
2+
3+
---
4+
5+
## **Schema concettuale del sistema**
6+
7+
```
8+
┌─────────────────────┐
9+
│ Portfolio │
10+
│─────────────────────│
11+
│ name │
12+
│ date │
13+
│ backtest │
14+
│ _snapshot │
15+
│ _positions {ticker: Position} │
16+
└─────────────────────┘
17+
18+
----------------─┼-----------------
19+
│ │
20+
▼ ▼
21+
┌───────────┐ ┌───────────┐
22+
│ Position │ │ Trade │
23+
│───────────│ │───────────│
24+
│ ticker │ │ id │
25+
│ shares │ │ date │
26+
│ avg_cost │ │ portfolio_name │
27+
│ current_price │ ticker │
28+
│ stop_loss │ │ operation │
29+
│ profit_target │ quantity │
30+
│ portfolio │ │ price │
31+
└───────────┘ │ commission │
32+
│ notes │
33+
│ portfolio │
34+
└───────────┘
35+
```
36+
37+
---
38+
39+
### **Relazioni con il Database**
40+
41+
| Classe | Tabella DB | Campi principali |
42+
| --------- | -------------------------------- | ------------------------------------------------------------------------------------------------------- |
43+
| Portfolio | `portfolio_snapshots(_backtest)` | `date`, `portfolio_name`, `total_value`, `cash_balance`, `positions_count`, metriche performance |
44+
| Position | `portfolio_positions(_backtest)` | `date`, `portfolio_name`, `ticker`, `shares`, `avg_cost`, `current_price`, `stop_loss`, `profit_target` |
45+
| Trade | `portfolio_trades(_backtest)` | `id`, `date`, `portfolio_name`, `ticker`, `operation`, `quantity`, `price`, `commission`, `notes` |
46+
47+
* **Backtest flag**: ogni query controlla `_backtest` per decidere se leggere/scrivere dalle tabelle ordinarie o da quelle di backtest.
48+
* **Portfolio → Position**: `_positions` è un dizionario di oggetti `Position`.
49+
* **Portfolio → Trade**: `_save_trade()` salva un oggetto `Trade` correlato.
50+
51+
---
52+
53+
### **Flusso con il Risk Manager**
54+
55+
1. Il **risk\_manager** chiama `get_signals(strategy_fn, date, portfolio_name)`.
56+
2. `Portfolio` viene caricato per la data richiesta.
57+
3. Il risk manager valuta le posizioni correnti tramite `portfolio.get_position(ticker)`.
58+
4. Per un segnale BUY:
59+
60+
* Calcola la size usando ATR (`_calculate_atr`)
61+
* Aggiorna `Position` e crea `Trade` tramite `portfolio.execute_trade()`
62+
5. Per un segnale SELL:
63+
64+
* Controlla stop loss e segnali SELL
65+
* Aggiorna `Position` e crea `Trade` corrispondente
66+
67+
---
68+
69+
### **Calcoli e Metriche**
70+
71+
| Oggetto | Metodi principali | Descrizione |
72+
| --------- | ---------------------------------------------- | ----------------------------------------------- |
73+
| Portfolio | `get_total_value()`, `get_cash_balance()` | Valore totale portafoglio, cash disponibile |
74+
| Portfolio | `get_position_percentages()` | Percentuale composizione per ciascun ticker |
75+
| Position | `get_unrealized_pnl()`, `get_current_value()` | PnL, valore posizione |
76+
| Position | `is_stop_loss_hit()`, `is_profit_target_hit()` | Logica risk manager |
77+
| Trade | `get_net_value()` | Valore netto del trade considerando commissioni |
78+
79+
---
80+
81+
### **Sintesi del flusso dati**
82+
83+
```
84+
Risk Manager
85+
86+
87+
Portfolio (caricato per date)
88+
89+
├──> _positions: Position objects (posizioni correnti)
90+
│ │
91+
│ └──> calcoli di rischio e performance
92+
93+
└──> execute_trade() → crea/aggiorna Trade objects
94+
95+
└──> Salvataggio in DB (portfolio_trades)
96+
```
97+
98+
---
99+
100+
💡 **Note operative**
101+
102+
* Tutte le modifiche di posizioni o trade aggiornano anche il `portfolio_snapshots` in DB.
103+
* `get_position_percentages()` fornisce un reporting immediato senza salvare dati nel DB.
104+
* Il backtest flag permette di separare simulazioni da portafogli reali senza duplicare codice.
105+
106+
---
107+
108+
Se vuoi, posso anche prepararti **una piccola tabella riassuntiva con tutti i metodi principali di ogni classe e quali campi DB usano**, così avrai **una guida rapida** pronta per sviluppo o test.
109+
110+
Vuoi che faccia anche quella?

scripts/database.py

Lines changed: 72 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -167,82 +167,72 @@ def create_portfolio_tables():
167167
# ===============================
168168

169169
snapshots_query = """
170-
CREATE TABLE IF NOT EXISTS portfolio_snapshots (
170+
CREATE TABLE portfolio_snapshots (
171171
date DATE NOT NULL,
172-
portfolio_name VARCHAR(50) NOT NULL DEFAULT 'default',
173-
174-
-- Valori base portafoglio
172+
portfolio_name VARCHAR(50) NOT NULL,
173+
174+
-- Stato base del portafoglio
175175
total_value DECIMAL(12,2) NOT NULL,
176176
cash_balance DECIMAL(12,2) NOT NULL,
177177
positions_count INTEGER DEFAULT 0,
178-
179-
-- Metriche performance (stored in DB)
180-
daily_return_pct DECIMAL(8,4),
178+
179+
-- Metriche performance
181180
total_return_pct DECIMAL(8,4),
182-
volatility_pct DECIMAL(8,4),
183-
current_drawdown_pct DECIMAL(8,4),
184181
max_drawdown_pct DECIMAL(8,4),
182+
volatility_pct DECIMAL(8,4),
185183
sharpe_ratio DECIMAL(8,4),
186184
win_rate_pct DECIMAL(8,4),
187-
peak_value DECIMAL(12,2),
188-
185+
189186
-- Metadata
190187
created_at TIMESTAMP DEFAULT NOW(),
191188
updated_at TIMESTAMP DEFAULT NOW(),
192-
189+
193190
PRIMARY KEY (date, portfolio_name)
191+
194192
);
195193
"""
196194

197195
positions_query = """
198-
CREATE TABLE IF NOT EXISTS portfolio_positions (
199-
date DATE NOT NULL,
200-
portfolio_name VARCHAR(50) NOT NULL DEFAULT 'default',
201-
ticker VARCHAR(10) NOT NULL,
202-
203-
-- Dati posizione
204-
shares INTEGER NOT NULL,
205-
avg_cost DECIMAL(10,4) NOT NULL,
206-
current_price DECIMAL(10,4) NOT NULL,
207-
current_value DECIMAL(12,2) NOT NULL,
196+
CREATE TABLE portfolio_positions (
197+
date DATE NOT NULL,
198+
portfolio_name VARCHAR(50) NOT NULL,
199+
ticker VARCHAR(10) NOT NULL,
200+
201+
-- Dati base
202+
shares INTEGER NOT NULL,
203+
avg_cost DECIMAL(10,4) NOT NULL,
204+
current_price DECIMAL(10,4) NOT NULL,
205+
206+
-- Risk management
207+
stop_loss DECIMAL(10,4),
208+
profit_target DECIMAL(10,4),
209+
210+
-- Metadata
211+
created_at TIMESTAMP DEFAULT NOW(),
212+
updated_at TIMESTAMP DEFAULT NOW(),
213+
214+
PRIMARY KEY (date, portfolio_name, ticker),
215+
FOREIGN KEY (date, portfolio_name) REFERENCES portfolio_snapshots(date, portfolio_name) ON DELETE CASCADE
216+
);
208217
209-
-- Risk management
210-
stop_loss DECIMAL(10,4),
211-
first_target DECIMAL(10,4),
212-
breakeven DECIMAL(10,4),
213-
first_half_sold BOOLEAN DEFAULT FALSE,
214-
entry_atr DECIMAL(6,4),
215-
216-
-- Metriche posizione
217-
position_weight_pct DECIMAL(8,4),
218-
position_pnl_pct DECIMAL(8,4),
219-
position_volatility DECIMAL(8,4),
220-
221-
-- Metadata
222-
created_at TIMESTAMP DEFAULT NOW(),
223-
updated_at TIMESTAMP DEFAULT NOW(),
224-
225-
PRIMARY KEY (date, portfolio_name, ticker),
226-
FOREIGN KEY (date, portfolio_name) REFERENCES portfolio_snapshots(date, portfolio_name) ON DELETE CASCADE
227-
);
228218
"""
229219

230220
trades_query = """
231-
CREATE TABLE IF NOT EXISTS portfolio_trades (
232-
id SERIAL PRIMARY KEY,
233-
date DATE NOT NULL,
234-
portfolio_name VARCHAR(50) NOT NULL,
235-
ticker VARCHAR(10) NOT NULL,
236-
operation VARCHAR(4) NOT NULL CHECK (operation IN ('BUY', 'SELL')),
237-
quantity INTEGER NOT NULL,
238-
price DECIMAL(10,4) NOT NULL,
239-
total_value DECIMAL(12,2) NOT NULL,
240-
commission DECIMAL(8,2) DEFAULT 0.00,
241-
notes TEXT,
242-
created_at TIMESTAMP DEFAULT NOW(),
243-
244-
FOREIGN KEY (date, portfolio_name) REFERENCES portfolio_snapshots(date, portfolio_name) ON DELETE CASCADE
245-
);
221+
CREATE TABLE portfolio_trades (
222+
id SERIAL PRIMARY KEY,
223+
date DATE NOT NULL,
224+
portfolio_name VARCHAR(50) NOT NULL,
225+
ticker VARCHAR(10) NOT NULL,
226+
227+
operation VARCHAR(4) NOT NULL CHECK (operation IN ('BUY', 'SELL')),
228+
quantity INTEGER NOT NULL,
229+
price DECIMAL(10,4) NOT NULL,
230+
commission DECIMAL(8,2) DEFAULT 0.00,
231+
232+
notes TEXT,
233+
234+
created_at TIMESTAMP DEFAULT NOW()
235+
);
246236
"""
247237

248238
# ===============================
@@ -252,61 +242,49 @@ def create_portfolio_tables():
252242
snapshots_backtest_query = """
253243
CREATE TABLE IF NOT EXISTS portfolio_snapshots_backtest (
254244
date DATE NOT NULL,
255-
portfolio_name VARCHAR(50) NOT NULL DEFAULT 'default',
256-
257-
-- Valori base portafoglio
245+
portfolio_name VARCHAR(50) NOT NULL,
246+
247+
-- Stato base del portafoglio
258248
total_value DECIMAL(12,2) NOT NULL,
259249
cash_balance DECIMAL(12,2) NOT NULL,
260250
positions_count INTEGER DEFAULT 0,
261-
262-
-- Metriche performance (stored in DB)
263-
daily_return_pct DECIMAL(8,4),
251+
252+
-- Metriche performance
264253
total_return_pct DECIMAL(8,4),
265-
volatility_pct DECIMAL(8,4),
266-
current_drawdown_pct DECIMAL(8,4),
267254
max_drawdown_pct DECIMAL(8,4),
255+
volatility_pct DECIMAL(8,4),
268256
sharpe_ratio DECIMAL(8,4),
269257
win_rate_pct DECIMAL(8,4),
270-
peak_value DECIMAL(12,2),
271-
258+
272259
-- Metadata
273260
created_at TIMESTAMP DEFAULT NOW(),
274261
updated_at TIMESTAMP DEFAULT NOW(),
275-
262+
276263
PRIMARY KEY (date, portfolio_name)
277264
);
278265
"""
279266

280267
positions_backtest_query = """
281268
CREATE TABLE IF NOT EXISTS portfolio_positions_backtest (
282269
date DATE NOT NULL,
283-
portfolio_name VARCHAR(50) NOT NULL DEFAULT 'default',
270+
portfolio_name VARCHAR(50) NOT NULL,
284271
ticker VARCHAR(10) NOT NULL,
285-
286-
-- Dati posizione
272+
273+
-- Dati base
287274
shares INTEGER NOT NULL,
288275
avg_cost DECIMAL(10,4) NOT NULL,
289276
current_price DECIMAL(10,4) NOT NULL,
290-
current_value DECIMAL(12,2) NOT NULL,
291277
292-
-- Risk management
278+
-- Risk management
293279
stop_loss DECIMAL(10,4),
294-
first_target DECIMAL(10,4),
295-
breakeven DECIMAL(10,4),
296-
first_half_sold BOOLEAN DEFAULT FALSE,
297-
entry_atr DECIMAL(6,4),
298-
299-
-- Metriche posizione
300-
position_weight_pct DECIMAL(8,4),
301-
position_pnl_pct DECIMAL(8,4),
302-
position_volatility DECIMAL(8,4),
303-
280+
profit_target DECIMAL(10,4),
281+
304282
-- Metadata
305283
created_at TIMESTAMP DEFAULT NOW(),
306284
updated_at TIMESTAMP DEFAULT NOW(),
307-
285+
308286
PRIMARY KEY (date, portfolio_name, ticker),
309-
FOREIGN KEY (date, portfolio_name) REFERENCES portfolio_snapshots_backtest(date, portfolio_name) ON DELETE CASCADE
287+
FOREIGN KEY (date, portfolio_name) REFERENCES portfolio_snapshots(date, portfolio_name) ON DELETE CASCADE
310288
);
311289
"""
312290

@@ -316,15 +294,15 @@ def create_portfolio_tables():
316294
date DATE NOT NULL,
317295
portfolio_name VARCHAR(50) NOT NULL,
318296
ticker VARCHAR(10) NOT NULL,
297+
319298
operation VARCHAR(4) NOT NULL CHECK (operation IN ('BUY', 'SELL')),
320299
quantity INTEGER NOT NULL,
321300
price DECIMAL(10,4) NOT NULL,
322-
total_value DECIMAL(12,2) NOT NULL,
323301
commission DECIMAL(8,2) DEFAULT 0.00,
302+
324303
notes TEXT,
325-
created_at TIMESTAMP DEFAULT NOW(),
326-
327-
FOREIGN KEY (date, portfolio_name) REFERENCES portfolio_snapshots_backtest(date, portfolio_name) ON DELETE CASCADE
304+
305+
created_at TIMESTAMP DEFAULT NOW()
328306
);
329307
"""
330308

@@ -338,54 +316,39 @@ def create_portfolio_tables():
338316
ON portfolio_snapshots(date);
339317
CREATE INDEX IF NOT EXISTS idx_portfolio_snapshots_name_date
340318
ON portfolio_snapshots(portfolio_name, date DESC);
341-
CREATE INDEX IF NOT EXISTS idx_portfolio_snapshots_total_value
342-
ON portfolio_snapshots(total_value DESC);
343-
319+
344320
-- Indici portfolio_positions
345321
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_ticker
346322
ON portfolio_positions(ticker);
347-
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_weight
348-
ON portfolio_positions(position_weight_pct DESC);
349-
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_pnl
350-
ON portfolio_positions(position_pnl_pct DESC);
351323
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_date_name
352324
ON portfolio_positions(date, portfolio_name);
353-
325+
354326
-- Indici portfolio_trades
355327
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_date
356328
ON portfolio_trades(date DESC);
357329
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_ticker
358330
ON portfolio_trades(ticker);
359-
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_operation
360-
ON portfolio_trades(operation);
361331
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_portfolio_date
362332
ON portfolio_trades(portfolio_name, date DESC);
363-
333+
364334
-- Indici backtest (stessa struttura)
365335
CREATE INDEX IF NOT EXISTS idx_portfolio_snapshots_backtest_date
366336
ON portfolio_snapshots_backtest(date);
367337
CREATE INDEX IF NOT EXISTS idx_portfolio_snapshots_backtest_name_date
368338
ON portfolio_snapshots_backtest(portfolio_name, date DESC);
369-
CREATE INDEX IF NOT EXISTS idx_portfolio_snapshots_backtest_total_value
370-
ON portfolio_snapshots_backtest(total_value DESC);
371-
339+
372340
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_backtest_ticker
373341
ON portfolio_positions_backtest(ticker);
374-
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_backtest_weight
375-
ON portfolio_positions_backtest(position_weight_pct DESC);
376-
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_backtest_pnl
377-
ON portfolio_positions_backtest(position_pnl_pct DESC);
378342
CREATE INDEX IF NOT EXISTS idx_portfolio_positions_backtest_date_name
379343
ON portfolio_positions_backtest(date, portfolio_name);
380-
344+
381345
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_backtest_date
382346
ON portfolio_trades_backtest(date DESC);
383347
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_backtest_ticker
384348
ON portfolio_trades_backtest(ticker);
385-
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_backtest_operation
386-
ON portfolio_trades_backtest(operation);
387349
CREATE INDEX IF NOT EXISTS idx_portfolio_trades_backtest_portfolio_date
388350
ON portfolio_trades_backtest(portfolio_name, date DESC);
351+
389352
"""
390353

391354
# ===============================

0 commit comments

Comments
 (0)