types: calculate MaximumConsecutiveLosses and MaximumConsecutiveProfits

This commit is contained in:
c9s 2022-07-27 19:09:27 +08:00
parent 151d907457
commit 9f06be14aa
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
2 changed files with 98 additions and 27 deletions

15
pkg/fixedpoint/helpers.go Normal file
View File

@ -0,0 +1,15 @@
package fixedpoint
func Sum(values []Value) (s Value) {
s = Zero
for _, value := range values {
s = s.Add(value)
}
return s
}
func Avg(values []Value) (avg Value) {
s := Sum(values)
avg = s.Div(NewFromInt(int64(len(values))))
return avg
}

View File

@ -1,6 +1,7 @@
package types
import (
"math"
"time"
"gopkg.in/yaml.v3"
@ -95,18 +96,41 @@ func (s IntervalProfitCollector) MarshalYAML() (interface{}, error) {
// See https://www.metatrader5.com/en/terminal/help/algotrading/testing_report
type TradeStats struct {
Symbol string `json:"symbol"`
WinningRatio fixedpoint.Value `json:"winningRatio" yaml:"winningRatio"`
NumOfLossTrade int `json:"numOfLossTrade" yaml:"numOfLossTrade"`
NumOfProfitTrade int `json:"numOfProfitTrade" yaml:"numOfProfitTrade"`
GrossProfit fixedpoint.Value `json:"grossProfit" yaml:"grossProfit"`
GrossLoss fixedpoint.Value `json:"grossLoss" yaml:"grossLoss"`
Profits []fixedpoint.Value `json:"profits" yaml:"profits"`
Losses []fixedpoint.Value `json:"losses" yaml:"losses"`
MostProfitableTrade fixedpoint.Value `json:"mostProfitableTrade" yaml:"mostProfitableTrade"`
MostLossTrade fixedpoint.Value `json:"mostLossTrade" yaml:"mostLossTrade"`
LargestProfitTrade fixedpoint.Value `json:"largestProfitTrade,omitempty" yaml:"largestProfitTrade"`
LargestLossTrade fixedpoint.Value `json:"largestLossTrade,omitempty" yaml:"largestLossTrade"`
AverageProfitTrade fixedpoint.Value `json:"averageProfitTrade" yaml:"averageProfitTrade"`
AverageLossTrade fixedpoint.Value `json:"averageLossTrade" yaml:"averageLossTrade"`
ProfitFactor fixedpoint.Value `json:"profitFactor" yaml:"profitFactor"`
TotalNetProfit fixedpoint.Value `json:"totalNetProfit" yaml:"totalNetProfit"`
IntervalProfits map[Interval]*IntervalProfitCollector `jons:"intervalProfits,omitempty" yaml: "intervalProfits,omitempty"`
// MaximumConsecutiveWins - (counter) the longest series of winning trades
MaximumConsecutiveWins int `json:"maximumConsecutiveWins" yaml:"maximumConsecutiveWins"`
// MaximumConsecutiveLosses - (counter) the longest series of losing trades
MaximumConsecutiveLosses int `json:"maximumConsecutiveLosses" yaml:"maximumConsecutiveLosses"`
// MaximumConsecutiveProfit - ($) the longest series of winning trades and their total profit;
MaximumConsecutiveProfit fixedpoint.Value `json:"maximumConsecutiveProfit" yaml:"maximumConsecutiveProfit"`
// MaximumConsecutiveLoss - ($) the longest series of losing trades and their total loss;
MaximumConsecutiveLoss fixedpoint.Value `json:"maximumConsecutiveLoss" yaml:"maximumConsecutiveLoss"`
consecutiveSide int
consecutiveCounter int
consecutiveAmount fixedpoint.Value
}
func NewTradeStats(symbol string) *TradeStats {
@ -119,7 +143,7 @@ func (s *TradeStats) SetIntervalProfitCollector(c *IntervalProfitCollector) {
}
func (s *TradeStats) Add(profit *Profit) {
if profit.Symbol != s.Symbol {
if s.Symbol != "" && profit.Symbol != s.Symbol {
return
}
@ -134,12 +158,42 @@ func (s *TradeStats) add(pnl fixedpoint.Value) {
s.NumOfProfitTrade++
s.Profits = append(s.Profits, pnl)
s.GrossProfit = s.GrossProfit.Add(pnl)
s.MostProfitableTrade = fixedpoint.Max(s.MostProfitableTrade, pnl)
s.LargestProfitTrade = fixedpoint.Max(s.LargestProfitTrade, pnl)
// consecutive same side (made profit last time)
if s.consecutiveSide == 0 || s.consecutiveSide == 1 {
s.consecutiveSide = 1
s.consecutiveCounter++
s.consecutiveAmount = s.consecutiveAmount.Add(pnl)
} else { // was loss, now profit, store the last loss and the loss amount
s.MaximumConsecutiveLosses = int(math.Max(float64(s.MaximumConsecutiveLosses), float64(s.consecutiveCounter)))
s.MaximumConsecutiveLoss = fixedpoint.Min(s.MaximumConsecutiveLoss, s.consecutiveAmount)
s.consecutiveSide = 1
s.consecutiveCounter = 0
s.consecutiveAmount = pnl
}
} else {
s.NumOfLossTrade++
s.Losses = append(s.Losses, pnl)
s.GrossLoss = s.GrossLoss.Add(pnl)
s.MostLossTrade = fixedpoint.Min(s.MostLossTrade, pnl)
s.LargestLossTrade = fixedpoint.Min(s.LargestLossTrade, pnl)
// consecutive same side (made loss last time)
if s.consecutiveSide == 0 || s.consecutiveSide == -1 {
s.consecutiveSide = -1
s.consecutiveCounter++
s.consecutiveAmount = s.consecutiveAmount.Add(pnl)
} else { // was profit, now loss, store the last win and profit
s.MaximumConsecutiveWins = int(math.Max(float64(s.MaximumConsecutiveWins), float64(s.consecutiveCounter)))
s.MaximumConsecutiveProfit = fixedpoint.Max(s.MaximumConsecutiveProfit, s.consecutiveAmount)
s.consecutiveSide = -1
s.consecutiveCounter = 0
s.consecutiveAmount = pnl
}
}
s.TotalNetProfit = s.TotalNetProfit.Add(pnl)
@ -153,6 +207,8 @@ func (s *TradeStats) add(pnl fixedpoint.Value) {
}
s.ProfitFactor = s.GrossProfit.Div(s.GrossLoss.Abs())
s.AverageProfitTrade = fixedpoint.Avg(s.Profits)
s.AverageLossTrade = fixedpoint.Avg(s.Losses)
}
// Output TradeStats without Profits and Losses
@ -164,8 +220,8 @@ func (s *TradeStats) BriefString() string {
NumOfProfitTrade: s.NumOfProfitTrade,
GrossProfit: s.GrossProfit,
GrossLoss: s.GrossLoss,
MostProfitableTrade: s.MostProfitableTrade,
MostLossTrade: s.MostLossTrade,
LargestProfitTrade: s.LargestProfitTrade,
LargestLossTrade: s.LargestLossTrade,
ProfitFactor: s.ProfitFactor,
TotalNetProfit: s.TotalNetProfit,
IntervalProfits: s.IntervalProfits,