@@ -193,6 +193,7 @@ type TradeStats struct {
193
193
// MaximumConsecutiveLoss - ($) the longest series of losing trades and their total loss;
194
194
MaximumConsecutiveLoss fixedpoint.Value `json:"maximumConsecutiveLoss" yaml:"maximumConsecutiveLoss"`
195
195
196
+ lastOrderID uint64
196
197
consecutiveSide int
197
198
consecutiveCounter int
198
199
consecutiveAmount fixedpoint.Value
@@ -252,7 +253,7 @@ func (s *TradeStats) Add(profit *Profit) {
252
253
s .orderProfits [profit .OrderID ] = append (s .orderProfits [profit .OrderID ], profit )
253
254
}
254
255
255
- s .add (profit . Profit )
256
+ s .add (profit )
256
257
257
258
for _ , v := range s .IntervalProfits {
258
259
v .Update (profit )
@@ -275,8 +276,13 @@ func grossProfitReducer(prev, curr fixedpoint.Value) fixedpoint.Value {
275
276
return prev
276
277
}
277
278
278
- // update the trade stats fields from the orderProfits
279
- func (s * TradeStats ) update () {
279
+ // Recalculate the trade stats fields from the orderProfits
280
+ // this is for live-trading, one order may have many trades, and we need to merge them.
281
+ func (s * TradeStats ) Recalculate () {
282
+ if len (s .orderProfits ) == 0 {
283
+ return
284
+ }
285
+
280
286
var profitsByOrder []fixedpoint.Value
281
287
var netProfitsByOrder []fixedpoint.Value
282
288
for _ , profits := range s .orderProfits {
@@ -291,92 +297,107 @@ func (s *TradeStats) update() {
291
297
netProfitsByOrder = append (netProfitsByOrder , sumNetProfit )
292
298
}
293
299
294
- s .NumOfProfitTrade = fixedpoint .Count (profitsByOrder , func (a fixedpoint.Value ) bool {
295
- return a .Sign () > 0
296
- })
297
- s .NumOfLossTrade = fixedpoint .Count (profitsByOrder , func (a fixedpoint.Value ) bool {
298
- return a .Sign () < 0
299
- })
300
+ s .NumOfProfitTrade = fixedpoint .Count (profitsByOrder , fixedpoint .PositiveTester )
301
+ s .NumOfLossTrade = fixedpoint .Count (profitsByOrder , fixedpoint .NegativeTester )
300
302
s .TotalNetProfit = fixedpoint .Reduce (profitsByOrder , fixedpoint .SumReducer )
301
303
s .GrossProfit = fixedpoint .Reduce (profitsByOrder , grossProfitReducer )
302
304
s .GrossLoss = fixedpoint .Reduce (profitsByOrder , grossLossReducer )
303
305
304
306
sort .Sort (fixedpoint .Descending (profitsByOrder ))
305
307
sort .Sort (fixedpoint .Descending (netProfitsByOrder ))
306
308
307
- s .Losses = fixedpoint .Filter (profitsByOrder , fixedpoint .NegativeTester )
308
309
s .Profits = fixedpoint .Filter (profitsByOrder , fixedpoint .PositiveTester )
310
+ s .Losses = fixedpoint .Filter (profitsByOrder , fixedpoint .NegativeTester )
309
311
s .LargestProfitTrade = profitsByOrder [0 ]
310
312
s .LargestLossTrade = profitsByOrder [len (profitsByOrder )- 1 ]
311
313
if s .LargestLossTrade .Sign () > 0 {
312
314
s .LargestLossTrade = fixedpoint .Zero
313
315
}
316
+
317
+ s .ProfitFactor = s .GrossProfit .Div (s .GrossLoss .Abs ())
318
+ if len (s .Profits ) > 0 {
319
+ s .AverageProfitTrade = fixedpoint .Avg (s .Profits )
320
+ }
321
+ if len (s .Losses ) > 0 {
322
+ s .AverageLossTrade = fixedpoint .Avg (s .Losses )
323
+ }
324
+
325
+ s .updateWinningRatio ()
314
326
}
315
327
316
- func (s * TradeStats ) add (pnl fixedpoint.Value ) {
317
- if pnl .Sign () > 0 {
318
- s .NumOfProfitTrade ++
319
- s .Profits = append (s .Profits , pnl )
320
- s .GrossProfit = s .GrossProfit .Add (pnl )
321
- s .LargestProfitTrade = fixedpoint .Max (s .LargestProfitTrade , pnl )
322
-
323
- // consecutive same side (made profit last time)
324
- if s .consecutiveSide == 0 || s .consecutiveSide == 1 {
325
- s .consecutiveSide = 1
326
- s .consecutiveCounter ++
327
- s .consecutiveAmount = s .consecutiveAmount .Add (pnl )
328
- } else { // was loss, now profit, store the last loss and the loss amount
329
- s .MaximumConsecutiveLosses = int (math .Max (float64 (s .MaximumConsecutiveLosses ), float64 (s .consecutiveCounter )))
330
- s .MaximumConsecutiveLoss = fixedpoint .Min (s .MaximumConsecutiveLoss , s .consecutiveAmount )
331
-
332
- s .consecutiveSide = 1
333
- s .consecutiveCounter = 0
334
- s .consecutiveAmount = pnl
328
+ func (s * TradeStats ) add (profit * Profit ) {
329
+ pnl := profit .Profit
330
+
331
+ // order id changed
332
+ if s .lastOrderID != profit .OrderID {
333
+ if pnl .Sign () > 0 {
334
+ s .NumOfProfitTrade ++
335
+ s .GrossProfit = s .GrossProfit .Add (pnl )
336
+
337
+ if s .consecutiveSide == 0 {
338
+ s .consecutiveSide = 1
339
+ s .consecutiveCounter = 1
340
+ s .consecutiveAmount = pnl
341
+ } else if s .consecutiveSide == 1 {
342
+ s .consecutiveCounter ++
343
+ s .consecutiveAmount = s .consecutiveAmount .Add (pnl )
344
+ s .MaximumConsecutiveWins = int (math .Max (float64 (s .MaximumConsecutiveWins ), float64 (s .consecutiveCounter )))
345
+ s .MaximumConsecutiveProfit = fixedpoint .Max (s .MaximumConsecutiveProfit , s .consecutiveAmount )
346
+ } else {
347
+ s .MaximumConsecutiveLosses = int (math .Max (float64 (s .MaximumConsecutiveLosses ), float64 (s .consecutiveCounter )))
348
+ s .MaximumConsecutiveLoss = fixedpoint .Min (s .MaximumConsecutiveLoss , s .consecutiveAmount )
349
+ s .consecutiveSide = 1
350
+ s .consecutiveCounter = 1
351
+ s .consecutiveAmount = pnl
352
+ }
353
+ } else {
354
+ s .NumOfLossTrade ++
355
+ s .GrossLoss = s .GrossLoss .Add (pnl )
356
+
357
+ if s .consecutiveSide == 0 {
358
+ s .consecutiveSide = - 1
359
+ s .consecutiveCounter = 1
360
+ s .consecutiveAmount = pnl
361
+ } else if s .consecutiveSide == - 1 {
362
+ s .consecutiveCounter ++
363
+ s .consecutiveAmount = s .consecutiveAmount .Add (pnl )
364
+ s .MaximumConsecutiveLosses = int (math .Max (float64 (s .MaximumConsecutiveLosses ), float64 (s .consecutiveCounter )))
365
+ s .MaximumConsecutiveLoss = fixedpoint .Min (s .MaximumConsecutiveLoss , s .consecutiveAmount )
366
+ } else { // was profit, now loss, store the last win and profit
367
+ s .MaximumConsecutiveWins = int (math .Max (float64 (s .MaximumConsecutiveWins ), float64 (s .consecutiveCounter )))
368
+ s .MaximumConsecutiveProfit = fixedpoint .Max (s .MaximumConsecutiveProfit , s .consecutiveAmount )
369
+ s .consecutiveSide = - 1
370
+ s .consecutiveCounter = 1
371
+ s .consecutiveAmount = pnl
372
+ }
335
373
}
336
-
337
374
} else {
338
- s .NumOfLossTrade ++
339
- s .Losses = append (s .Losses , pnl )
340
- s .GrossLoss = s .GrossLoss .Add (pnl )
341
- s .LargestLossTrade = fixedpoint .Min (s .LargestLossTrade , pnl )
342
-
343
- // consecutive same side (made loss last time)
344
- if s .consecutiveSide == 0 || s .consecutiveSide == - 1 {
345
- s .consecutiveSide = - 1
346
- s .consecutiveCounter ++
347
- s .consecutiveAmount = s .consecutiveAmount .Add (pnl )
348
- } else { // was profit, now loss, store the last win and profit
349
- s .MaximumConsecutiveWins = int (math .Max (float64 (s .MaximumConsecutiveWins ), float64 (s .consecutiveCounter )))
350
- s .MaximumConsecutiveProfit = fixedpoint .Max (s .MaximumConsecutiveProfit , s .consecutiveAmount )
351
-
352
- s .consecutiveSide = - 1
353
- s .consecutiveCounter = 0
354
- s .consecutiveAmount = pnl
355
- }
356
-
375
+ s .consecutiveAmount = s .consecutiveAmount .Add (pnl )
357
376
}
377
+
378
+ s .lastOrderID = profit .OrderID
358
379
s .TotalNetProfit = s .TotalNetProfit .Add (pnl )
380
+ s .ProfitFactor = s .GrossProfit .Div (s .GrossLoss .Abs ())
381
+
382
+ s .updateWinningRatio ()
383
+ }
359
384
385
+ func (s * TradeStats ) updateWinningRatio () {
360
386
// The win/loss ratio is your wins divided by your losses.
361
387
// In the example, suppose for the sake of simplicity that 60 trades were winners, and 40 were losers.
362
388
// Your win/loss ratio would be 60/40 = 1.5. That would mean that you are winning 50% more often than you are losing.
363
- if s .NumOfLossTrade == 0 && s .NumOfProfitTrade > 0 {
389
+ if s .NumOfLossTrade == 0 && s .NumOfProfitTrade == 0 {
390
+ s .WinningRatio = fixedpoint .Zero
391
+ } else if s .NumOfLossTrade == 0 && s .NumOfProfitTrade > 0 {
364
392
s .WinningRatio = fixedpoint .One
365
393
} else {
366
394
s .WinningRatio = fixedpoint .NewFromFloat (float64 (s .NumOfProfitTrade ) / float64 (s .NumOfLossTrade ))
367
395
}
368
-
369
- s .ProfitFactor = s .GrossProfit .Div (s .GrossLoss .Abs ())
370
- if len (s .Profits ) > 0 {
371
- s .AverageProfitTrade = fixedpoint .Avg (s .Profits )
372
- }
373
- if len (s .Losses ) > 0 {
374
- s .AverageLossTrade = fixedpoint .Avg (s .Losses )
375
- }
376
396
}
377
397
378
398
// Output TradeStats without Profits and Losses
379
399
func (s * TradeStats ) BriefString () string {
400
+ s .Recalculate ()
380
401
out , _ := yaml .Marshal (& TradeStats {
381
402
Symbol : s .Symbol ,
382
403
WinningRatio : s .WinningRatio ,
@@ -400,6 +421,7 @@ func (s *TradeStats) BriefString() string {
400
421
}
401
422
402
423
func (s * TradeStats ) String () string {
424
+ s .Recalculate ()
403
425
out , _ := yaml .Marshal (s )
404
426
return string (out )
405
427
}
0 commit comments