mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-08-01 20:59:33 +00:00
Add "Total Traded" statistic
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -42,6 +43,23 @@ func Backtest(trader *Trader) {
|
||||
stats := trader.Stats()
|
||||
// log.Println(trader.Stats().Dated.String())
|
||||
|
||||
var totalTraded float64
|
||||
stats.Dated.Series("Trades").ForEach(func(i int, val any) {
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
switch typ := val.(type) {
|
||||
case []TradeStat:
|
||||
for _, trade := range typ {
|
||||
if trade.Exit { // Only count entry trades.
|
||||
continue
|
||||
}
|
||||
totalTraded += trade.Price * math.Abs(trade.Units)
|
||||
}
|
||||
default:
|
||||
panic("unknown type when calculating totalTraded")
|
||||
}
|
||||
})
|
||||
// Divide net profit by maximum drawdown to get the profit factor.
|
||||
var maxDrawdown float64
|
||||
stats.Dated.Series("Drawdown").ForEach(func(i int, val any) {
|
||||
@@ -59,6 +77,7 @@ func Backtest(trader *Trader) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintf(w, "Timespan:\t%s\t\n", stats.Dated.Date(-1).Sub(stats.Dated.Date(0)).Round(time.Second))
|
||||
fmt.Fprintf(w, "Total Traded:\t$%.2f\t\n", totalTraded)
|
||||
fmt.Fprintf(w, "Net Profit:\t$%.2f (%.2f%%)\t\n", profit, 100*profit/stats.Dated.Float("Equity", 0))
|
||||
fmt.Fprintf(w, "Profit Factor:\t%.2f\t\n", profitFactor)
|
||||
fmt.Fprintf(w, "Max Drawdown:\t$%.2f (%.2f%%)\t\n", maxDrawdown, maxDrawdownPct)
|
||||
@@ -600,7 +619,7 @@ func (p *TestPosition) close(atPrice float64, closeType OrderCloseType) {
|
||||
p.closePrice = atPrice
|
||||
p.closeType = closeType
|
||||
p.broker.Cash += p.Value() // Return the value of the position to the broker.
|
||||
p.broker.spreadCollectedUSD += p.broker.Spread * p.units
|
||||
p.broker.spreadCollectedUSD += p.broker.Spread * math.Abs(p.units) * p.closePrice
|
||||
p.broker.SignalEmit("PositionClosed", p)
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ func (s *IchimokuStrategy) Next(t *auto.Trader) {
|
||||
laggingTime := data.Date(-s.leadingPeriods - 1)
|
||||
|
||||
// Extract ichimoku elements
|
||||
ichimoku := auto.Ichimoku(data, s.convPeriod, s.basePeriod, s.leadingPeriods, time.Minute*15)
|
||||
ichimoku := auto.Ichimoku(data, s.convPeriod, s.basePeriod, s.leadingPeriods, time.Minute*1)
|
||||
conv := ichimoku.Series("Conversion")
|
||||
base := ichimoku.Series("Base")
|
||||
leadA := ichimoku.Series("LeadingA")
|
||||
@@ -91,7 +91,7 @@ func main() {
|
||||
Broker: auto.NewTestBroker(broker, nil, 10000, 50, 0.0002, 0),
|
||||
Strategy: &IchimokuStrategy{convPeriod: 9, basePeriod: 26, leadingPeriods: 52},
|
||||
Symbol: "EUR_USD",
|
||||
Frequency: "M15",
|
||||
Frequency: "M1", // If the frequency is changed, update the call to Ichimoku() above.
|
||||
CandlesToKeep: 2500,
|
||||
}))
|
||||
}
|
||||
|
13
trader.go
13
trader.go
@@ -33,6 +33,7 @@ func (t *Trader) Data() *IndexedFrame[UnixTime] {
|
||||
}
|
||||
|
||||
type TradeStat struct {
|
||||
Price float64 // Price is the price at which the trade was executed. If Exit is true, this is the exit price. Otherwise, this is the entry price.
|
||||
Units float64 // Units is the signed number of units bought or sold.
|
||||
Exit bool // Exit is true if the trade was to exit a previous position.
|
||||
}
|
||||
@@ -165,14 +166,18 @@ func (t *Trader) Buy(units, stopLoss, takeProfit float64) {
|
||||
t.CloseOrdersAndPositions()
|
||||
t.Log.Printf("Buy %v units", units)
|
||||
t.Broker.Order(Market, t.Symbol, units, 0, stopLoss, takeProfit)
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{units, false})
|
||||
|
||||
tradeStat := TradeStat{t.Broker.Ask(t.Symbol), units, false}
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, tradeStat)
|
||||
}
|
||||
|
||||
func (t *Trader) Sell(units, stopLoss, takeProfit float64) {
|
||||
t.CloseOrdersAndPositions()
|
||||
t.Log.Printf("Sell %v units", units)
|
||||
t.Broker.Order(Market, t.Symbol, -units, 0, stopLoss, takeProfit)
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{-units, false})
|
||||
|
||||
tradeStat := TradeStat{t.Broker.Bid(t.Symbol), units, false}
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, tradeStat)
|
||||
}
|
||||
|
||||
func (t *Trader) CloseOrdersAndPositions() {
|
||||
@@ -186,7 +191,9 @@ func (t *Trader) CloseOrdersAndPositions() {
|
||||
if position.Symbol() == t.Symbol {
|
||||
t.Log.Printf("Closing position: %v units, $%.2f PL", position.Units(), position.PL())
|
||||
position.Close()
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{position.Units(), true})
|
||||
|
||||
tradeStat := TradeStat{t.Broker.Price(t.Symbol, position.Units() < 0), position.Units(), true}
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, tradeStat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user