Skip to content

Commit

Permalink
ショートトレードのアナリティクス実装
Browse files Browse the repository at this point in the history
  • Loading branch information
T.K committed Feb 8, 2024
1 parent db533a6 commit 09b6718
Show file tree
Hide file tree
Showing 16 changed files with 296 additions and 124 deletions.
9 changes: 5 additions & 4 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@


#バックテストの基本設定
assetName: "ATOMUSDT"
duration: "5m"
assetName: "SOLUSDT"
duration: "30m"
start: "20200801"
end: "20230730"
simpleInterest: true
end: "20210930"
simpleInterest: true
positionPersentage: 0.3
7 changes: 3 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ func main() {
// strategey.RunEmaOptimize()
// strategey.RunSTOptimize()
// strategey.RunDonchainOptimize()

// strategey.RunBetterRsiOptimize()

strategey.DonchainBacktest()
strategey.EmaBacktest()
strategey.SuperTrendBacktest()
// strategey.DonchainBacktest()
// strategey.EmaBacktest()
// strategey.SuperTrendBacktest()
strategey.RSIBryyrtBacktest()

// strategey.EmaBacktest()
Expand Down
57 changes: 0 additions & 57 deletions pkg/analytics/calc_winrate.go

This file was deleted.

108 changes: 80 additions & 28 deletions pkg/analytics/drawdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,54 +73,106 @@ func MaxDrawdownRatio(s *execute.SignalEvents) float64 {
}
return lossDrawdown
}

func MaxDrawdownUSD(s *execute.SignalEvents) float64 {
func MaxDrawdownPercent(s *execute.SignalEvents) float64 {
if s == nil || s.Signals == nil || len(s.Signals) == 0 {
return 0.0
}
// Sort the signals by time
sort.Slice(s.Signals, func(i, j int) bool {
return s.Signals[i].Time.Before(s.Signals[j].Time)
})
var lossDrawdown float64 = 0.0
var buyPrice float64 = 0.0 // Initialize buyPrice as zero
var loss float64 = 0.0
var maxEquity float64 = 0.0 // Initialize maxEquity as zero
var peak float64 = 0.0 // Initialize peak value
var maxDrawdown float64 = 0.0 // Initialize max drawdown
var drawdown float64 = 0.0 // Initialize drawdown

for _, signal := range s.Signals {

if signal.Side != "BUY" && signal.Side != "SELL" {
// TODO: Handle other sides if necessary
continue
}
if signal.Side == "BUY" {
if signal.Price > buyPrice || buyPrice == 0 { // Update buy price only when it is higher than the previous one or zero
buyPrice = signal.Price
}
if signal.AccountBalance > maxEquity { // Update maxEquity only when it is higher than the previous one
maxEquity = signal.AccountBalance
}
loss = 0.0 // Reset loss when buy signal occurs
} else if signal.Side == "SELL" && buyPrice != 0 {
if signal.Price < buyPrice && loss == 0 { // Calculate loss only when the price is lower than the buy price and loss is zero
if signal.AccountBalance > peak { // Update peak value if account balance is higher
peak = signal.AccountBalance
}
drawdown = (peak - signal.AccountBalance) / peak // Calculate drawdown for each signal
if drawdown > maxDrawdown {
maxDrawdown = drawdown // Update max drawdown if drawdown is higher
}
}
return maxDrawdown // Return max drawdown in percentage
}

loss = (buyPrice - signal.Price) * signal.Size // Calculate loss
func MaxDrawdownUSD(s *execute.SignalEvents) float64 {
if s == nil || s.Signals == nil || len(s.Signals) == 0 {
return 0.0
}
// Sort the signals by time
sort.Slice(s.Signals, func(i, j int) bool {
return s.Signals[i].Time.Before(s.Signals[j].Time)
})
var peak float64 = 0.0 // Initialize peak value
var maxDrawdown float64 = 0.0 // Initialize max drawdown
var drawdown float64 = 0.0 // Initialize drawdown

}
for _, signal := range s.Signals {

// drawdown := loss / maxEquity // Calculate drawdown using maxEquity
// if drawdown > lossDrawdown {
// lossDrawdown = drawdown // Update loss drawdown
// }
// Replace the above lines with the following lines
if loss > lossDrawdown {
lossDrawdown = loss // Update loss drawdown in USD
}
buyPrice = 0.0 // Reset buyPrice when sell signal occurs
if signal.Side != "BUY" && signal.Side != "SELL" {
// TODO: Handle other sides if necessary
continue
}
if signal.AccountBalance > peak { // Update peak value if account balance is higher
peak = signal.AccountBalance
}
drawdown = (peak - signal.AccountBalance) / peak // Calculate drawdown for each signal
if drawdown > maxDrawdown {
maxDrawdown = drawdown // Update max drawdown if drawdown is higher
}
}
return lossDrawdown
return maxDrawdown * peak // Return max drawdown in USD
}

// func MaxDrawdownUSD(s *execute.SignalEvents) float64 {
// if s == nil || s.Signals == nil || len(s.Signals) == 0 {
// return 0.0
// }
// // Sort the signals by time
// sort.Slice(s.Signals, func(i, j int) bool {
// return s.Signals[i].Time.Before(s.Signals[j].Time)
// })
// var lossDrawdown float64 = 0.0
// var buyPrice float64 = 0.0
// var loss float64 = 0.0
// var maxEquity float64 = 0.0

// for _, signal := range s.Signals {

// if signal.Side != "BUY" && signal.Side != "SELL" {
// continue
// }
// if signal.Side == "BUY" {
// if signal.Price > buyPrice || buyPrice == 0 { // Update buy price only when it is higher than the previous one or zero
// buyPrice = signal.Price
// }
// if signal.AccountBalance > maxEquity { // Update maxEquity only when it is higher than the previous one
// maxEquity = signal.AccountBalance
// }
// loss = 0.0 // Reset loss when buy signal occurs
// } else if signal.Side == "SELL" && buyPrice != 0 {
// if signal.Price < buyPrice && loss == 0 { // Calculate loss only when the price is lower than the buy price and loss is zero

// loss = (buyPrice - signal.Price) * signal.Size // Calculate loss

// }

// if loss > lossDrawdown {
// lossDrawdown = loss // Update loss drawdown in USD
// }
// buyPrice = 0.0 // Reset buyPrice when sell signal occurs
// }
// }
// return lossDrawdown
// }

// MaxDrawdown returns the maximum drawdown and its duration of a strategy
// based on the signal events and the initial capital
func MaxDrawdown(s *execute.SignalEvents) (float64, int) {
Expand Down
6 changes: 3 additions & 3 deletions pkg/analytics/pay_off_ratio.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ func AveregeProfit(s *execute.SignalEvents) float64 {
}
winningTrades := WinningTrades(s)

totalProfit := Profit(s)
totalProfit := LongProfit(s)

averegeProfit := totalProfit / float64(winningTrades)

Expand All @@ -21,7 +21,7 @@ func AveregeProfitRatio(s *execute.SignalEvents) float64 {
if s == nil {
return 0.0
}
totalProfit := Profit(s)
totalProfit := LongProfit(s)

// USDの金額ベースから%表記に変換
averageProfitPercentage := (totalProfit / s.Signals[0].AccountBalance)
Expand Down Expand Up @@ -51,7 +51,7 @@ func AveregeTradeProfit(s *execute.SignalEvents) float64 {
}
totalTrade := TotalTrades(s)

netProfit := NetProfit(s)
netProfit := LongNetProfit(s)

averegeTradeProfit := netProfit / float64(totalTrade)

Expand Down
87 changes: 82 additions & 5 deletions pkg/analytics/profit_and_loss.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func Profi2(s *execute.SignalEvents) float64 {
return profit
}

func Profit(s *execute.SignalEvents) float64 {
func LongProfit(s *execute.SignalEvents) float64 {
if s == nil {
return 0.0
}
Expand Down Expand Up @@ -126,21 +126,41 @@ func Loss(s *execute.SignalEvents) float64 {
return loss
}

func NetProfit(s *execute.SignalEvents) float64 {
func LongNetProfit(s *execute.SignalEvents) float64 {
if s == nil {
return 0.0
}
totalProfit := Profit(s)
totalProfit := LongProfit(s)
totalLoss := Loss(s)

return totalProfit - totalLoss
}

func ShortNetProfit(s *execute.SignalEvents) float64 {
if s == nil {
return 0.0
}
totalProfit := ShortProfit(s)
totalLoss := ShortLoss(s)

return totalProfit - totalLoss
}

func TotalNetProfit(s *execute.SignalEvents) float64 {
if s == nil {
return 0.0
}
longProfit := LongNetProfit(s)
shortProfit := ShortNetProfit(s)

return longProfit + shortProfit
}

func ProfitFactor(s *execute.SignalEvents) float64 {
if s == nil {
return 0.0
}
totalProfit := Profit(s)
totalProfit := LongProfit(s)
totalLoss := Loss(s)

// if totalLoss == 0 {
Expand All @@ -161,7 +181,7 @@ func FinalBalance(s *execute.SignalEvents) (float64, float64) {
return 0, 0
}

finalBlanceValue := accountBalance + NetProfit(s)
finalBlanceValue := accountBalance + LongNetProfit(s)
finalBlanceRatio := finalBlanceValue / accountBalance

return finalBlanceValue, finalBlanceRatio
Expand Down Expand Up @@ -277,3 +297,60 @@ func ReturnProfitLoss(s *execute.SignalEvents) []float64 {

return pl
}

func ShortProfit(s *execute.SignalEvents) float64 {
if s == nil {
return 0.0
}
var profit float64 = 0.0
var sellPrice float64

if s.Signals == nil || len(s.Signals) == 0 {
return 0.0
}
for _, signal := range s.Signals {

if signal.Side != "BUY" && signal.Side != "SELL" {
return 0.0
}
if signal.Side == "SELL" {
sellPrice = signal.Price
} else if signal.Side == "BUY" && sellPrice != 0 {
if signal.Price < sellPrice {
profit += (sellPrice - signal.Price) * signal.Size
}
sellPrice = 0 // Reset sell price after a buy
}
}

return profit
}

func ShortLoss(s *execute.SignalEvents) float64 {

if s == nil {
return 0.0
}
var loss float64 = 0.0
var sellPrice float64

if s.Signals == nil || len(s.Signals) == 0 {
return 0.0
}
for _, signal := range s.Signals {

if signal.Side != "BUY" && signal.Side != "SELL" {
return 0.0
}
if signal.Side == "SELL" {
sellPrice = signal.Price
} else if signal.Side == "BUY" && sellPrice != 0 {
if signal.Price > sellPrice {
loss += (signal.Price - sellPrice) * signal.Size
}
sellPrice = 0 // Reset sell price after a buy
}
}

return loss
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ func TestProfit(t *testing.T) {
}

// Call the Profit function and store the result
profit := analytics.Profit(s)
profit := analytics.LongProfit(s)

// Define the expected value
want := 150.0

// Check if the result is equal to the expected value
if profit != want {
// If not, report an error to the testing framework
t.Errorf("Profit(s) = %v, want %v", profit, want)
t.Errorf("LongProfit(s) = %v, want %v", profit, want)
}
}
Loading

0 comments on commit 09b6718

Please sign in to comment.