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 package types
import ( import (
"math"
"time" "time"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -94,19 +95,42 @@ func (s IntervalProfitCollector) MarshalYAML() (interface{}, error) {
// TODO: Add more stats from the reference: // TODO: Add more stats from the reference:
// See https://www.metatrader5.com/en/terminal/help/algotrading/testing_report // See https://www.metatrader5.com/en/terminal/help/algotrading/testing_report
type TradeStats struct { type TradeStats struct {
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
WinningRatio fixedpoint.Value `json:"winningRatio" yaml:"winningRatio"`
NumOfLossTrade int `json:"numOfLossTrade" yaml:"numOfLossTrade"` WinningRatio fixedpoint.Value `json:"winningRatio" yaml:"winningRatio"`
NumOfProfitTrade int `json:"numOfProfitTrade" yaml:"numOfProfitTrade"` NumOfLossTrade int `json:"numOfLossTrade" yaml:"numOfLossTrade"`
GrossProfit fixedpoint.Value `json:"grossProfit" yaml:"grossProfit"` NumOfProfitTrade int `json:"numOfProfitTrade" yaml:"numOfProfitTrade"`
GrossLoss fixedpoint.Value `json:"grossLoss" yaml:"grossLoss"`
Profits []fixedpoint.Value `json:"profits" yaml:"profits"` GrossProfit fixedpoint.Value `json:"grossProfit" yaml:"grossProfit"`
Losses []fixedpoint.Value `json:"losses" yaml:"losses"` GrossLoss fixedpoint.Value `json:"grossLoss" yaml:"grossLoss"`
MostProfitableTrade fixedpoint.Value `json:"mostProfitableTrade" yaml:"mostProfitableTrade"`
MostLossTrade fixedpoint.Value `json:"mostLossTrade" yaml:"mostLossTrade"` Profits []fixedpoint.Value `json:"profits" yaml:"profits"`
ProfitFactor fixedpoint.Value `json:"profitFactor" yaml:"profitFactor"` Losses []fixedpoint.Value `json:"losses" yaml:"losses"`
TotalNetProfit fixedpoint.Value `json:"totalNetProfit" yaml:"totalNetProfit"`
IntervalProfits map[Interval]*IntervalProfitCollector `jons:"intervalProfits,omitempty" yaml: "intervalProfits,omitempty"` 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 { func NewTradeStats(symbol string) *TradeStats {
@ -119,7 +143,7 @@ func (s *TradeStats) SetIntervalProfitCollector(c *IntervalProfitCollector) {
} }
func (s *TradeStats) Add(profit *Profit) { func (s *TradeStats) Add(profit *Profit) {
if profit.Symbol != s.Symbol { if s.Symbol != "" && profit.Symbol != s.Symbol {
return return
} }
@ -134,12 +158,42 @@ func (s *TradeStats) add(pnl fixedpoint.Value) {
s.NumOfProfitTrade++ s.NumOfProfitTrade++
s.Profits = append(s.Profits, pnl) s.Profits = append(s.Profits, pnl)
s.GrossProfit = s.GrossProfit.Add(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 { } else {
s.NumOfLossTrade++ s.NumOfLossTrade++
s.Losses = append(s.Losses, pnl) s.Losses = append(s.Losses, pnl)
s.GrossLoss = s.GrossLoss.Add(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) s.TotalNetProfit = s.TotalNetProfit.Add(pnl)
@ -153,22 +207,24 @@ func (s *TradeStats) add(pnl fixedpoint.Value) {
} }
s.ProfitFactor = s.GrossProfit.Div(s.GrossLoss.Abs()) 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 // Output TradeStats without Profits and Losses
func (s *TradeStats) BriefString() string { func (s *TradeStats) BriefString() string {
out, _ := yaml.Marshal(&TradeStats{ out, _ := yaml.Marshal(&TradeStats{
Symbol: s.Symbol, Symbol: s.Symbol,
WinningRatio: s.WinningRatio, WinningRatio: s.WinningRatio,
NumOfLossTrade: s.NumOfLossTrade, NumOfLossTrade: s.NumOfLossTrade,
NumOfProfitTrade: s.NumOfProfitTrade, NumOfProfitTrade: s.NumOfProfitTrade,
GrossProfit: s.GrossProfit, GrossProfit: s.GrossProfit,
GrossLoss: s.GrossLoss, GrossLoss: s.GrossLoss,
MostProfitableTrade: s.MostProfitableTrade, LargestProfitTrade: s.LargestProfitTrade,
MostLossTrade: s.MostLossTrade, LargestLossTrade: s.LargestLossTrade,
ProfitFactor: s.ProfitFactor, ProfitFactor: s.ProfitFactor,
TotalNetProfit: s.TotalNetProfit, TotalNetProfit: s.TotalNetProfit,
IntervalProfits: s.IntervalProfits, IntervalProfits: s.IntervalProfits,
}) })
return string(out) return string(out)
} }