-
Notifications
You must be signed in to change notification settings - Fork 0
/
baregone.go
153 lines (126 loc) · 3.42 KB
/
baregone.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package baregone
import (
"log"
"time"
lop "github.com/samber/lo"
)
type BacktestArgs struct {
marketData []BarData
option BackTestOptions
strategy Strategy
}
func Backtest(args BacktestArgs) BacktestContext {
strategy := args.strategy
isDebug := args.option.debug
prices := args.marketData
initCapital := args.option.capital
position := &Position{}
currentBar := &BarData{}
context := &BacktestContext{
trades: nil,
capital: initCapital,
}
logger := func(str string, others ...int) {
if isDebug {
log.Print(str, others)
}
}
refreshVariables := func() {
position = &Position{
// tradeType: nil,
entryTime: time.Time{},
exitTime: time.Time{},
entryPrice: 0,
profit: 0,
profitAmount: 0,
profitPct: 0,
isOpen: false,
virtualEntryPrice: 0,
virtualEntryTime: time.Time{},
virtualProfit: 0,
}
currentBar = &BarData{
date: time.Time{},
close: 0,
volume: 0,
}
}
recordPosition := func(bar *BarData, position *Position) {
tradeType := position.tradeType
// profitToSave, profitPercentage
profitToSave := 0
profitPercentage := 0
closePrice := bar.close
entryPrice := position.entryPrice
if tradeType == "SELL" {
profitPercentage = GetPercentageGain(closePrice, entryPrice)
profitToSave = entryPrice - closePrice
} else {
profitPercentage = GetPercentageGain(entryPrice, closePrice)
profitToSave = closePrice - entryPrice
}
logger("profitPercentage ------------>", profitPercentage)
logger("profitToSave ------------>", profitToSave)
position.SetProfit(profitToSave)
position.SetProfitPct(profitPercentage)
}
exitPosition := func() {
if !position.isOpen {
logger("Position is not open")
}
recordPosition(currentBar, position)
entryPrice := position.entryPrice
exitTime := currentBar.date
closePrice := currentBar.close
profitOfCapitalAmount := 0
if position.tradeType == "SELL" {
profitOfCapitalAmount = GetTotalProfitAmount(closePrice, entryPrice, initCapital)
} else {
profitOfCapitalAmount = GetTotalProfitAmount(entryPrice, closePrice, initCapital)
}
position.SetExitTime(exitTime)
position.SetIsOpen(false)
position.SetProfitAmount(profitOfCapitalAmount)
logger(`CLOSE ---> ${profit}`, profitOfCapitalAmount)
context.trades = append(context.trades, *position)
refreshVariables()
}
enterPosition := func(tradeType TradeType) {
position = &Position{
tradeType: tradeType, // default is buy by default
entryPrice: currentBar.close,
entryTime: currentBar.date,
exitTime: time.Time{},
profit: 0,
profitAmount: 0,
profitPct: 0,
isOpen: true,
}
}
finishTrading := func() BacktestContext {
if position.isOpen {
exitPosition()
}
return BacktestContext{
trades: context.trades,
capital: context.capital,
profit: lop.SumBy(context.trades, func(trade Position) int {
return trade.profit
}),
totalTrades: len(context.trades),
}
}
for _, price := range prices {
currentBar = &price
isOpen := position.isOpen
if isOpen {
recordPosition(currentBar, position)
strategy.analysePosition(AnalysePositionArgs{bar: currentBar, position: *position, exitPosition: exitPosition})
} else {
strategy.onMarketTick(OnMarketTickArgs{bar: currentBar, enterPosition: enterPosition})
}
}
// ... logic here
// return nil
return finishTrading()
}