// Copyright 2022 The Coln Group Ltd
// SPDX-License-Identifier: MIT

package perf

import (
	"math"

	"gonum.org/v1/gonum/floats"

	"github.com/thecolngroup/alphakit/broker"
	"github.com/thecolngroup/gou/num"
)

// TradeReport is a report on the trade metrics.
// It is generated by the NewTradeReport function using the roundturns and roundturn data generated by an algo.
type TradeReport struct {
	TradeCount     float64
	RoundTurnCount float64

	TotalNetProfit float64
	AvgNetProfit   float64
	GrossProfit    float64
	GrossLoss      float64
	ProfitFactor   float64
	PRR            float64

	PercentProfitable  float64
	MaxProfit, MaxLoss float64

	AvgProfit float64
	AvgLoss   float64

	MaxLossStreak int

	Kelly    float64
	OptimalF float64
	StatN    float64

	TotalTimeInMarketSec float64
	AvgHoldSec           float64

	RoundTurns []broker.RoundTurn `csv:"-"`

	winningCount, winningPct float64
	losingCount, losingPct   float64
}

// NewTradeReport creates a new TradeReport from a sequence of roundturns.
func NewTradeReport(roundturns []broker.RoundTurn) *TradeReport {
	if len(roundturns) == 0 {
		return nil
	}

	var report TradeReport
	report.RoundTurns = roundturns

	var lossStreak int

	profits := make([]float64, len(roundturns))

	for i := range roundturns {
		rt := roundturns[i]
		report.TotalTimeInMarketSec += rt.HoldPeriod.Seconds()
		report.TradeCount += float64(rt.TradeCount)

		profit := rt.Profit.InexactFloat64()
		profits[i] = profit
		switch {
		case profit > 0:
			report.winningCount++
			report.GrossProfit += profit
			if lossStreak > report.MaxLossStreak {
				report.MaxLossStreak = lossStreak
			}
			lossStreak = 0
		case profit < 0:
			report.losingCount++
			report.GrossLoss += math.Abs(profit)
			lossStreak++
		}
	}
	report.MaxProfit = floats.Max(profits)
	report.MaxLoss = math.Abs(floats.Min(profits))

	report.RoundTurnCount = report.winningCount + report.losingCount

	report.TotalNetProfit = report.GrossProfit - report.GrossLoss
	report.AvgNetProfit = report.TotalNetProfit / report.RoundTurnCount
	report.ProfitFactor = report.GrossProfit / num.NNZ(report.GrossLoss, 1)
	report.PRR = PRR(report.GrossProfit, report.GrossLoss, report.winningCount, report.losingCount)

	report.AvgProfit = report.GrossProfit / num.NNZ(report.winningCount, 1)
	report.AvgLoss = report.GrossLoss / num.NNZ(report.losingCount, 1)

	report.winningPct = report.winningCount / report.RoundTurnCount
	report.losingPct = 1 - report.winningPct
	report.PercentProfitable = report.winningPct

	report.AvgHoldSec = report.TotalTimeInMarketSec / report.RoundTurnCount

	report.Kelly = KellyCriterion(report.ProfitFactor, report.winningPct)
	report.OptimalF = OptimalF(profits)

	report.StatN = StatN(profits)

	return &report
}
