3939]
4040
4141
42- def generate_equity_curve_data (trades ):
42+ def generate_equity_curve_data (trades , account_config ):
4343 """
4444 Generate equity curve data from trades
45+
46+ Shows true equity (initial_capital + cumulative P&L) instead of just cumulative P&L
47+ Color is green if final equity > initial, red otherwise
4548
4649 Args:
4750 trades (list): List of trade dictionaries sorted by date
51+ account_config (dict): Account configuration with starting balance, deposits, withdrawals
4852
4953 Returns:
5054 dict: Chart.js compatible data structure
5155 """
56+ # Get initial capital
57+ starting_balance = account_config .get ("starting_balance" , 0 )
58+ deposits = account_config .get ("deposits" , [])
59+ withdrawals = account_config .get ("withdrawals" , [])
60+
61+ total_deposits = sum (d .get ("amount" , 0 ) for d in deposits )
62+ total_withdrawals = sum (w .get ("amount" , 0 ) for w in withdrawals )
63+ initial_capital = starting_balance + total_deposits - total_withdrawals
64+
5265 if not trades :
5366 return {
5467 "labels" : [],
@@ -68,14 +81,14 @@ def generate_equity_curve_data(trades):
6881 trades , key = lambda t : t .get ("exit_date" , t .get ("entry_date" , "" ))
6982 )
7083
71- # Calculate cumulative P&L
84+ # Calculate cumulative equity (initial_capital + cumulative P&L)
7285 labels = []
73- cumulative_pnl = []
74- running_total = 0
86+ equity_values = []
87+ running_pnl = 0
7588
7689 for trade in sorted_trades :
7790 pnl = trade .get ("pnl_usd" , 0 )
78- running_total += pnl
91+ running_pnl += pnl
7992
8093 # Use exit date for the equity point
8194 date_str = trade .get ("exit_date" , trade .get ("entry_date" , "" ))
@@ -85,22 +98,220 @@ def generate_equity_curve_data(trades):
8598 except (ValueError , TypeError ):
8699 labels .append (date_str )
87100
88- cumulative_pnl .append (round (running_total , 2 ))
101+ # True equity = initial capital + cumulative P&L
102+ equity = initial_capital + running_pnl
103+ equity_values .append (round (equity , 2 ))
104+
105+ # Determine color based on final vs initial
106+ final_equity = equity_values [- 1 ] if equity_values else initial_capital
107+ if final_equity >= initial_capital :
108+ border_color = "#00ff88" # Green
109+ background_color = "rgba(0, 255, 136, 0.1)"
110+ point_color = "#00ff88"
111+ else :
112+ border_color = "#ff4757" # Red
113+ background_color = "rgba(255, 71, 87, 0.1)"
114+ point_color = "#ff4757"
89115
90116 # Chart.js format
91117 chartjs_data = {
92118 "labels" : labels ,
93119 "datasets" : [
94120 {
95121 "label" : "Equity Curve" ,
96- "data" : cumulative_pnl ,
97- "borderColor" : "#00ff88" ,
98- "backgroundColor" : "rgba(0, 255, 136, 0.1)" ,
122+ "data" : equity_values ,
123+ "borderColor" : border_color ,
124+ "backgroundColor" : background_color ,
99125 "fill" : True ,
100126 "tension" : 0.4 ,
101127 "pointRadius" : 4 ,
102128 "pointHoverRadius" : 6 ,
103- "pointBackgroundColor" : "#00ff88" ,
129+ "pointBackgroundColor" : point_color ,
130+ "pointBorderColor" : "#0a0e27" ,
131+ "pointBorderWidth" : 2 ,
132+ }
133+ ],
134+ }
135+
136+ return chartjs_data
137+
138+
139+ def generate_drawdown_over_time_data (trades , account_config ):
140+ """
141+ Generate classic drawdown over time data as percentage from peak
142+
143+ Converts the drawdown series from dollars to percentage of peak equity
144+ Red border/fill below 0 to indicate drawdowns
145+
146+ Args:
147+ trades (list): List of trade dictionaries sorted by date
148+ account_config (dict): Account configuration with starting balance, deposits, withdrawals
149+
150+ Returns:
151+ dict: Chart.js compatible data structure with drawdown % series
152+ """
153+ # Get initial capital
154+ starting_balance = account_config .get ("starting_balance" , 0 )
155+ deposits = account_config .get ("deposits" , [])
156+ withdrawals = account_config .get ("withdrawals" , [])
157+
158+ total_deposits = sum (d .get ("amount" , 0 ) for d in deposits )
159+ total_withdrawals = sum (w .get ("amount" , 0 ) for w in withdrawals )
160+ initial_capital = starting_balance + total_deposits - total_withdrawals
161+
162+ if not trades :
163+ return {
164+ "labels" : [],
165+ "datasets" : [
166+ {
167+ "label" : "Drawdown %" ,
168+ "data" : [],
169+ "borderColor" : "#ff4757" ,
170+ "backgroundColor" : "rgba(255, 71, 87, 0.2)" ,
171+ "fill" : True ,
172+ "tension" : 0.4 ,
173+ }
174+ ],
175+ }
176+
177+ # Sort trades by exit date
178+ sorted_trades = sorted (
179+ trades , key = lambda t : t .get ("exit_date" , t .get ("entry_date" , "" ))
180+ )
181+
182+ # Calculate drawdown % from peak
183+ labels = []
184+ drawdown_percentages = []
185+ running_pnl = 0
186+ peak = 0
187+ peak_equity = initial_capital
188+
189+ for trade in sorted_trades :
190+ pnl = trade .get ("pnl_usd" , 0 )
191+ running_pnl += pnl
192+
193+ # Update peak
194+ if running_pnl > peak :
195+ peak = running_pnl
196+ peak_equity = initial_capital + peak
197+
198+ # Calculate drawdown from peak
199+ drawdown_dollars = running_pnl - peak
200+ drawdown_percent = (drawdown_dollars / peak_equity * 100 ) if peak_equity > 0 else 0
201+
202+ # Date label
203+ date_str = trade .get ("exit_date" , trade .get ("entry_date" , "" ))
204+ try :
205+ date_obj = datetime .fromisoformat (str (date_str ).split ("T" )[0 ])
206+ labels .append (date_obj .strftime ("%m/%d" ))
207+ except :
208+ labels .append (date_str )
209+
210+ drawdown_percentages .append (round (drawdown_percent , 2 ))
211+
212+ # Chart.js format
213+ chartjs_data = {
214+ "labels" : labels ,
215+ "datasets" : [
216+ {
217+ "label" : "Drawdown %" ,
218+ "data" : drawdown_percentages ,
219+ "borderColor" : "#ff4757" ,
220+ "backgroundColor" : "rgba(255, 71, 87, 0.2)" ,
221+ "fill" : True ,
222+ "tension" : 0.4 ,
223+ "pointRadius" : 3 ,
224+ "pointHoverRadius" : 5 ,
225+ "pointBackgroundColor" : "#ff4757" ,
226+ "pointBorderColor" : "#0a0e27" ,
227+ "pointBorderWidth" : 2 ,
228+ }
229+ ],
230+ }
231+
232+ return chartjs_data
233+
234+
235+ def generate_inception_drawdown_data (trades , account_config ):
236+ """
237+ Generate inception drawdown data as percentage from initial capital
238+
239+ Shows drawdown from initial investment rather than from peak
240+ Red border/fill below 0 to indicate drawdowns
241+
242+ Args:
243+ trades (list): List of trade dictionaries sorted by date
244+ account_config (dict): Account configuration with starting balance, deposits, withdrawals
245+
246+ Returns:
247+ dict: Chart.js compatible data structure with inception drawdown % series
248+ """
249+ # Get initial capital
250+ starting_balance = account_config .get ("starting_balance" , 0 )
251+ deposits = account_config .get ("deposits" , [])
252+ withdrawals = account_config .get ("withdrawals" , [])
253+
254+ total_deposits = sum (d .get ("amount" , 0 ) for d in deposits )
255+ total_withdrawals = sum (w .get ("amount" , 0 ) for w in withdrawals )
256+ initial_capital = starting_balance + total_deposits - total_withdrawals
257+
258+ if not trades :
259+ return {
260+ "labels" : [],
261+ "datasets" : [
262+ {
263+ "label" : "Inception Drawdown %" ,
264+ "data" : [],
265+ "borderColor" : "#ff4757" ,
266+ "backgroundColor" : "rgba(255, 71, 87, 0.2)" ,
267+ "fill" : True ,
268+ "tension" : 0.4 ,
269+ }
270+ ],
271+ }
272+
273+ # Sort trades by exit date
274+ sorted_trades = sorted (
275+ trades , key = lambda t : t .get ("exit_date" , t .get ("entry_date" , "" ))
276+ )
277+
278+ # Calculate inception drawdown %
279+ labels = []
280+ inception_drawdown_percentages = []
281+ running_pnl = 0
282+
283+ for trade in sorted_trades :
284+ pnl = trade .get ("pnl_usd" , 0 )
285+ running_pnl += pnl
286+
287+ # Calculate equity and drawdown from initial
288+ equity_t = initial_capital + running_pnl
289+ dd_percent = ((equity_t - initial_capital ) / initial_capital * 100 ) if initial_capital > 0 else 0
290+
291+ # Date label
292+ date_str = trade .get ("exit_date" , trade .get ("entry_date" , "" ))
293+ try :
294+ date_obj = datetime .fromisoformat (str (date_str ).split ("T" )[0 ])
295+ labels .append (date_obj .strftime ("%m/%d" ))
296+ except :
297+ labels .append (date_str )
298+
299+ inception_drawdown_percentages .append (round (dd_percent , 2 ))
300+
301+ # Chart.js format
302+ chartjs_data = {
303+ "labels" : labels ,
304+ "datasets" : [
305+ {
306+ "label" : "Inception Drawdown %" ,
307+ "data" : inception_drawdown_percentages ,
308+ "borderColor" : "#ff4757" ,
309+ "backgroundColor" : "rgba(255, 71, 87, 0.2)" ,
310+ "fill" : True ,
311+ "tension" : 0.4 ,
312+ "pointRadius" : 3 ,
313+ "pointHoverRadius" : 5 ,
314+ "pointBackgroundColor" : "#ff4757" ,
104315 "pointBorderColor" : "#0a0e27" ,
105316 "pointBorderWidth" : 2 ,
106317 }
@@ -1210,7 +1421,7 @@ def main():
12101421 print ("Generating Chart.js data files..." )
12111422
12121423 # 1. Equity Curve
1213- equity_data = generate_equity_curve_data (trades )
1424+ equity_data = generate_equity_curve_data (trades , account_config )
12141425 save_json_file ("index.directory/assets/charts/equity-curve-data.json" , equity_data )
12151426 print (" ✓ Equity curve data saved" )
12161427
@@ -1234,7 +1445,17 @@ def main():
12341445 save_json_file ("index.directory/assets/charts/time-of-day-performance-data.json" , time_of_day_data )
12351446 print (" ✓ Time of day performance data saved" )
12361447
1237- # 6. Portfolio Value Charts (all timeframes)
1448+ # 6. Drawdown Over Time
1449+ drawdown_data = generate_drawdown_over_time_data (trades , account_config )
1450+ save_json_file ("index.directory/assets/charts/drawdown-over-time-data.json" , drawdown_data )
1451+ print (" ✓ Drawdown over time data saved" )
1452+
1453+ # 7. Inception Drawdown
1454+ inception_dd_data = generate_inception_drawdown_data (trades , account_config )
1455+ save_json_file ("index.directory/assets/charts/inception-drawdown-data.json" , inception_dd_data )
1456+ print (" ✓ Inception drawdown data saved" )
1457+
1458+ # 8. Portfolio Value Charts (all timeframes)
12381459 print ("\n Generating Portfolio Value charts..." )
12391460 generate_portfolio_value_charts (trades , account_config )
12401461
0 commit comments