From b05ab903254c1fe20c5b4dc86aaf8c7d87f6befd Mon Sep 17 00:00:00 2001 From: tktk4751 Date: Mon, 29 Jan 2024 23:02:44 +0900 Subject: [PATCH] add AccounBlanceCalc --- cmd/backtest/main.go | 212 ++++++++++++++++++++ cmd/bb_backtest/main.go | 210 ++++++++++++++++++++ cmd/main.go | 94 +++++---- pkg/analytics/profit.go | 125 ------------ pkg/analytics/profit_and_loss.go | 187 ++++++++++++++++++ pkg/db/models/base.go | 86 ++++----- pkg/execute/signal.go | 48 ++--- pkg/strategey/bb.go | 103 +++++++++- pkg/strategey/donchain.go | 129 +++++++------ pkg/strategey/ema.go | 88 +++++++++ pkg/strategey/peper_acount.go | 43 +++++ pkg/strategey/positionsize.go | 129 ++++++++----- pkg/strategey/rsi.go | 322 +++++++++++++++++++++++++------ pkg/strategey/strategy.go | 49 +++++ web/src/lib/Chart.svelte | 68 ------- 15 files changed, 1417 insertions(+), 476 deletions(-) create mode 100644 cmd/backtest/main.go create mode 100755 cmd/bb_backtest/main.go delete mode 100644 pkg/analytics/profit.go create mode 100644 pkg/analytics/profit_and_loss.go create mode 100644 pkg/strategey/ema.go create mode 100644 pkg/strategey/peper_acount.go delete mode 100644 web/src/lib/Chart.svelte diff --git a/cmd/backtest/main.go b/cmd/backtest/main.go new file mode 100644 index 0000000..3be74a2 --- /dev/null +++ b/cmd/backtest/main.go @@ -0,0 +1,212 @@ +package main + +import ( + "fmt" + "log" + + // "net/http" + + "v1/pkg/analytics" + chart "v1/pkg/charts" + "v1/pkg/execute" + "v1/pkg/strategey" + // "v1/pkg/analytics/metrics" + // "v1/pkg/db/models" + // p "v1/pkg/management/position" + // data "v1/pkg/data/utils" +) + +// var path = "/home/lux/dev/go_trading_bot/pkg/data/spot/monthly/klines" + +// var close []float64 = utils.GetClosePrice(path) + +// var hloc = utils.GetCandleData(path) + +// var side = randam_side() + +// func randam_side() string { +// // Declare a local variable result to store the random side +// var result string + +// for i := 0; i < len(close); i++ { + +// n := rand.Intn(2) +// // Assign "BUY" or "SELL" to result +// if n == 0 { +// result = "BUY" +// } else { +// // Otherwise, assign "SELL" to result +// result = "SELL" +// } + +// } +// // Return the value of result +// return result +// } + +// func logRequest(handler http.Handler) http.Handler { +// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL) +// handler.ServeHTTP(w, r) +// }) +// } +func main() { + + strategyName := "EMA" + assetName := "AVAXUSDT" + duration := "1h" + tableName := strategyName + "_" + assetName + "_" + duration + + _, err := execute.CreateDBTable(tableName) + if err != nil { + log.Fatal(err) + } + + // df, _ := strategey.GetCandleData(assetName, duration) + + // profit, period := df.OptimizeProfitDonchain() + + // if profit > 0 { + + // df.Signal = df.DonchainStrategy(period) + + // } + + // winrate, period := df.OptimizeWinRateDonchain() + + // if winrate > 0 { + + // df.Signal = df.DonchainStrategy(period) + + // } + + df, _ := strategey.GetCandleData(assetName, duration) + + profit, bestPeriod1, bestPeriod2 := df.OptimizeEma() + + if profit > 0 { + + df.Signal = df.EmaStrategy(bestPeriod1, bestPeriod2, accountBlance) + + } + + l, lr := analytics.FinalBalance(df.Signal) + d := analytics.MaxDrawdown(df.Signal) + dr := d * 100 + + fmt.Println(df.Signal) + fmt.Println("最高利益", profit, "最適なピリオド1", bestPeriod1, "最適なピリオド2", bestPeriod2) + + fmt.Println(tableName) + fmt.Println("初期残高", analytics.AccountBalance) + fmt.Println("最終残高", l, "比率", lr) + fmt.Println("勝率", analytics.WinRate(df.Signal)) + fmt.Println("総利益", analytics.Profit(df.Signal)) + fmt.Println("総損失", analytics.Loss(df.Signal)) + fmt.Println("プロフィットファクター", analytics.ProfitFactor(df.Signal)) + fmt.Println("最大ドローダウン", dr, "% ") + fmt.Println("純利益", analytics.NetProfit(df.Signal)) + fmt.Println("シャープレシオ", analytics.SharpeRatio(df.Signal, 0.06)) + fmt.Println("トータルトレード回数", analytics.TotalTrades(df.Signal)) + fmt.Println("勝ちトレード回数", analytics.WinningTrades(df.Signal)) + fmt.Println("負けトレード回数", analytics.LosingTrades(df.Signal)) + fmt.Println("平均利益", analytics.AveregeProfit(df.Signal)) + fmt.Println("平均損失", analytics.AveregeLoss(df.Signal)) + fmt.Println("ペイオフレシオ", analytics.PayOffRatio(df.Signal)) + // fmt.Println("バルサラの破産確率", analytics.BalsaraAxum(df.Signal)) + + // s := execute.NewSignalEvents() + + // p, _ := query.GetCandleData(assetName, duration) + + // c1 := p[3].Close + // // c2 := p[300].Close + // by := s.Buy(strategyName, assetName, duration, p[40].Date, c1, 1.0, true) + // fmt.Println(by) + + defer fmt.Println("メイン関数終了") + + // チャート呼び出し + var c chart.CandleStickChart + c.CandleStickChart() + + // query.GetCloseData("BTCUSDT", "4h") + + // var assets_names []string = []string{"RUNEUSDT", "BTCUSDT", "AAVEUSDT", "ORDIUSDT", "SANUSDT", "LTCUSDT", "OKBUSDT", "ASTRUSDT", "MNTUSDT", "FTMUSDT", "SNXUSDT", "DYDXUSDT", "BONKUSDT", "LUNAUSDT", "MAGICUSDT", "XLMUSDT", "DOGEUSDT", "TRSUSDT", "LINKUSDT", "TONUSDT", "ISPUSDT", "BONKUSDT", "GMXUSDT", "INJUSDT", "ETHUSDT", "SOLUSDT", "AVAXUSDT", "MATICUSDT", "ATOMUSDT", "UNIUSDT", "ARBUSDT", "OPUSDT", "PEPEUSDT", "SEIUSDT", "SUIUSDT", "TIAUSDT", "WLDUSDT", "XRPUSDT", "NEARUSDT", "DOTUSDT", "APTUSDT", "XMRUSDT", "LDOUSDT", "FILUSDT", "KASUSDT", "STXUSDT", "RNDRUSDT", "GRTUSDT"} + + // var durations []string = []string{"1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h"} + // paths := data.GetRelativePaths() + + // groupedPaths := data.GroupAssetNamePaths(paths) + + // asset_data, err := data.LoadOHLCV(groupedPaths, assets_names, durations) + // if err != nil { + // log.Fatalf("Error loading OHLCV data: %v", err) + // } + + // // data.SaveAssetDatasCSV(asset_data) + + // // DBに接続する関数を呼び出し + // db, err := data.ConnectDB("./db/kline.db") + // if err != nil { + // log.Fatal(err) + // } + // // DBをクローズするのを遅延実行 + // defer db.Close() + // // データをDBに保存する関数を呼び出し + // err = data.SaveAssetDatasDB(db, asset_data) + // if err != nil { + // log.Fatal(err) + // } + + // for key, paths := range groupedPaths { + + // fmt.Printf("{%s: %v}\n", key, paths) + + // } + + // for _, assetData := range asset_data { + // fmt.Printf("Asset: %s, Duration: %s, OHLCV: %+v\n", assetData.AssetName, assetData.Duration, assetData.Data) + // } + + // fmt.Println(asset_data) + + // fs := http.FileServer(http.Dir("pkg/charts/html")) + // log.Println("running server at http://localhost:8089") + // log.Fatal(http.ListenAndServe("localhost:8089", logRequest(fs))) + +} + +// data.GetAbsolutePaths() + +// db := models.DbConnection + +// env := config.GetEnv() + +// var wr = metrics.Winrate_arg{ +// Totall_wintrade: 100, +// Totall_trade: 200, +// } +// var winrate float64 = metrics.Calc_winrate(wr.Totall_wintrade, wr.Totall_trade) + +// w := 0.4044 +// r := 4.699 +// d := 0.33 + +// position := p.PositionSizeCalculator{} + +// risk_size := position.Risk_size_calculator(w, r, d) * 100 + +// sl := position.Stop_loss_price_calc(close, side) + +// // management := money_management.PositionSizeCalculator{} +// // sl := management.Stop_loss_price_calc() + +// // Call the KellyCriterion function and print the result +// fmt.Println(risk_size, "%") +// fmt.Println(env.TradeDuration, "DURATION") +// fmt.Println(sl, side, "EXITPRICE") +// // fmt.Println(env.ApiKey) +// fmt.Println(winrate) +// fmt.Println(db) +// fmt.Println(hloc) diff --git a/cmd/bb_backtest/main.go b/cmd/bb_backtest/main.go new file mode 100755 index 0000000..c7dea07 --- /dev/null +++ b/cmd/bb_backtest/main.go @@ -0,0 +1,210 @@ +package main + +import ( + "fmt" + "log" + + // "net/http" + + "v1/pkg/analytics" + chart "v1/pkg/charts" + "v1/pkg/execute" + "v1/pkg/strategey" + // "v1/pkg/analytics/metrics" + // "v1/pkg/db/models" + // p "v1/pkg/management/position" + // data "v1/pkg/data/utils" +) + +// var path = "/home/lux/dev/go_trading_bot/pkg/data/spot/monthly/klines" + +// var close []float64 = utils.GetClosePrice(path) + +// var hloc = utils.GetCandleData(path) + +// var side = randam_side() + +// func randam_side() string { +// // Declare a local variable result to store the random side +// var result string + +// for i := 0; i < len(close); i++ { + +// n := rand.Intn(2) +// // Assign "BUY" or "SELL" to result +// if n == 0 { +// result = "BUY" +// } else { +// // Otherwise, assign "SELL" to result +// result = "SELL" +// } + +// } +// // Return the value of result +// return result +// } + +// func logRequest(handler http.Handler) http.Handler { +// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL) +// handler.ServeHTTP(w, r) +// }) +// } +func main() { + strategyName := "BB" + assetName := "BTCUSDT" + duration := "15m" + tableName := strategyName + "_" + assetName + "_" + duration + + _, err := execute.CreateDBTable(tableName) + if err != nil { + log.Fatal(err) + } + + // df, _ := strategey.GetCandleData(assetName, duration) + + // profit, period := df.OptimizeProfitDonchain() + + // if profit > 0 { + + // df.Signal = df.DonchainStrategy(period) + + // } + + // winrate, period := df.OptimizeWinRateDonchain() + + // if winrate > 0 { + + // df.Signal = df.DonchainStrategy(period) + + // } + + df, _ := strategey.GetCandleData(assetName, duration) + + performance, bestN, bestK := df.OptimizeBbLoss() + + if performance > 0 { + + df.Signal = df.BBStrategy(bestN, bestK) + + } + + l, lr := analytics.FinalBalance(df.Signal) + d := analytics.MaxDrawdown(df.Signal) + dr := d * 100 + + // fmt.Println(df.Signal) + + fmt.Println(tableName) + fmt.Println("初期残高", analytics.AccountBalance) + fmt.Println("最終残高", l, "比率", lr) + fmt.Println("勝率", analytics.WinRate(df.Signal)*100, "%") + fmt.Println("総利益", analytics.Profit(df.Signal)) + fmt.Println("総損失", analytics.Loss(df.Signal)) + fmt.Println("プロフィットファクター", analytics.ProfitFactor(df.Signal)) + fmt.Println("最大ドローダウン", dr, "% ") + fmt.Println("純利益", analytics.NetProfit(df.Signal)) + fmt.Println("シャープレシオ", analytics.SharpeRatio(df.Signal, 0.06)) + fmt.Println("トータルトレード回数", analytics.TotalTrades(df.Signal)) + fmt.Println("勝ちトレード回数", analytics.WinningTrades(df.Signal)) + fmt.Println("負けトレード回数", analytics.LosingTrades(df.Signal)) + fmt.Println("平均利益", analytics.AveregeProfit(df.Signal)) + fmt.Println("平均損失", analytics.AveregeLoss(df.Signal)) + fmt.Println("ペイオフレシオ", analytics.PayOffRatio(df.Signal)) + // fmt.Println("バルサラの破産確率", analytics.BalsaraAxum(df.Signal)) + + // s := execute.NewSignalEvents() + + // p, _ := query.GetCandleData(assetName, duration) + + // c1 := p[3].Close + // // c2 := p[300].Close + // by := s.Buy(strategyName, assetName, duration, p[40].Date, c1, 1.0, true) + // fmt.Println(by) + + defer fmt.Println("メイン関数終了") + + // チャート呼び出し + var c chart.CandleStickChart + c.CandleStickChart() + + // query.GetCloseData("BTCUSDT", "4h") + + // var assets_names []string = []string{"RUNEUSDT", "BTCUSDT", "AAVEUSDT", "ORDIUSDT", "SANUSDT", "LTCUSDT", "OKBUSDT", "ASTRUSDT", "MNTUSDT", "FTMUSDT", "SNXUSDT", "DYDXUSDT", "BONKUSDT", "LUNAUSDT", "MAGICUSDT", "XLMUSDT", "DOGEUSDT", "TRSUSDT", "LINKUSDT", "TONUSDT", "ISPUSDT", "BONKUSDT", "GMXUSDT", "INJUSDT", "ETHUSDT", "SOLUSDT", "AVAXUSDT", "MATICUSDT", "ATOMUSDT", "UNIUSDT", "ARBUSDT", "OPUSDT", "PEPEUSDT", "SEIUSDT", "SUIUSDT", "TIAUSDT", "WLDUSDT", "XRPUSDT", "NEARUSDT", "DOTUSDT", "APTUSDT", "XMRUSDT", "LDOUSDT", "FILUSDT", "KASUSDT", "STXUSDT", "RNDRUSDT", "GRTUSDT"} + + // var durations []string = []string{"1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h"} + // paths := data.GetRelativePaths() + + // groupedPaths := data.GroupAssetNamePaths(paths) + + // asset_data, err := data.LoadOHLCV(groupedPaths, assets_names, durations) + // if err != nil { + // log.Fatalf("Error loading OHLCV data: %v", err) + // } + + // // data.SaveAssetDatasCSV(asset_data) + + // // DBに接続する関数を呼び出し + // db, err := data.ConnectDB("./db/kline.db") + // if err != nil { + // log.Fatal(err) + // } + // // DBをクローズするのを遅延実行 + // defer db.Close() + // // データをDBに保存する関数を呼び出し + // err = data.SaveAssetDatasDB(db, asset_data) + // if err != nil { + // log.Fatal(err) + // } + + // for key, paths := range groupedPaths { + + // fmt.Printf("{%s: %v}\n", key, paths) + + // } + + // for _, assetData := range asset_data { + // fmt.Printf("Asset: %s, Duration: %s, OHLCV: %+v\n", assetData.AssetName, assetData.Duration, assetData.Data) + // } + + // fmt.Println(asset_data) + + // fs := http.FileServer(http.Dir("pkg/charts/html")) + // log.Println("running server at http://localhost:8089") + // log.Fatal(http.ListenAndServe("localhost:8089", logRequest(fs))) + +} + +// data.GetAbsolutePaths() + +// db := models.DbConnection + +// env := config.GetEnv() + +// var wr = metrics.Winrate_arg{ +// Totall_wintrade: 100, +// Totall_trade: 200, +// } +// var winrate float64 = metrics.Calc_winrate(wr.Totall_wintrade, wr.Totall_trade) + +// w := 0.4044 +// r := 4.699 +// d := 0.33 + +// position := p.PositionSizeCalculator{} + +// risk_size := position.Risk_size_calculator(w, r, d) * 100 + +// sl := position.Stop_loss_price_calc(close, side) + +// // management := money_management.PositionSizeCalculator{} +// // sl := management.Stop_loss_price_calc() + +// // Call the KellyCriterion function and print the result +// fmt.Println(risk_size, "%") +// fmt.Println(env.TradeDuration, "DURATION") +// fmt.Println(sl, side, "EXITPRICE") +// // fmt.Println(env.ApiKey) +// fmt.Println(winrate) +// fmt.Println(db) +// fmt.Println(hloc) diff --git a/cmd/main.go b/cmd/main.go index d60e9cf..90c716d 100755 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,13 +2,10 @@ package main import ( "fmt" - "log" // "net/http" - "v1/pkg/analytics" chart "v1/pkg/charts" - "v1/pkg/execute" "v1/pkg/strategey" // "v1/pkg/analytics/metrics" // "v1/pkg/db/models" @@ -51,57 +48,72 @@ import ( // }) // } func main() { - strategyName := "DBO" - assetName := "SEIUSDT" - duration := "1h" - tableName := strategyName + "_" + assetName + "_" + duration - _, err := execute.CreateDBTable(tableName) - if err != nil { - log.Fatal(err) - } + strategey.RunBacktestDonchain() + // strategyName := "RSI" + // assetName := "SEIUSDT" + // duration := "30m" + // tableName := strategyName + "_" + assetName + "_" + duration - df, _ := strategey.GetCandleData(assetName, duration) + // _, err := execute.CreateDBTable(tableName) + // if err != nil { + // log.Fatal(err) + // } + + // // df, _ := strategey.GetCandleData(assetName, duration) - profit, period := df.OptimizeProfitDonchain() + // // profit, period := df.OptimizeProfitDonchain() - if profit > 0 { + // // if profit > 0 { - df.Signal = df.DonchainStrategy(period) + // // df.Signal = df.DonchainStrategy(period) - } + // // } - winrate, period := df.OptimizeWinRateDonchain() + // // winrate, period := df.OptimizeWinRateDonchain() - if winrate > 0 { + // // if winrate > 0 { - df.Signal = df.DonchainStrategy(period) + // // df.Signal = df.DonchainStrategy(period) - } + // // } - l, lr := analytics.FinalBalance(df.Signal) - d := analytics.MaxDrawdown(df.Signal) - dr := d * 100 + // df, _ := strategey.GetCandleData(assetName, duration) - fmt.Println(df.Signal) + // profit, bestPeriod, bestBuyThread, bestSellThread := df.OptimizeDonchainProfit() + + // if profit > 0 { + + // df.Signal = df.RsiStrategy(bestPeriod, bestBuyThread, bestSellThread) + + // } - fmt.Println(tableName) - fmt.Println("初期残高", analytics.AccountBalance) - fmt.Println("最終残高", l, "比率", lr) - fmt.Println("勝率", analytics.WinRate(df.Signal)) - fmt.Println("総利益", analytics.Profit(df.Signal)) - fmt.Println("総損失", analytics.Loss(df.Signal)) - fmt.Println("プロフィットファクター", analytics.ProfitFactor(df.Signal)) - fmt.Println("最大ドローダウン", dr, "% ") - fmt.Println("純利益", analytics.NetProfit(df.Signal)) - fmt.Println("シャープレシオ", analytics.SharpeRatio(df.Signal, 0.06)) - fmt.Println("トータルトレード回数", analytics.TotalTrades(df.Signal)) - fmt.Println("勝ちトレード回数", analytics.WinningTrades(df.Signal)) - fmt.Println("負けトレード回数", analytics.LosingTrades(df.Signal)) - fmt.Println("平均利益", analytics.AveregeProfit(df.Signal)) - fmt.Println("平均損失", analytics.AveregeLoss(df.Signal)) - fmt.Println("ペイオフレシオ", analytics.PayOffRatio(df.Signal)) - // fmt.Println("バルサラの破産確率", analytics.BalsaraAxum(df.Signal)) + // l, lr := analytics.FinalBalance(df.Signal) + // d := analytics.MaxDrawdown(df.Signal) + // dr := d * 100 + + // ml, mt := analytics.MaxLossTrade(df.Signal) + + // // fmt.Println(df.Signal) + + // fmt.Println(tableName) + // fmt.Println("初期残高", analytics.AccountBalance) + // fmt.Println("最終残高", l, "比率", lr) + // fmt.Println("勝率", analytics.WinRate(df.Signal)*100, "%") + // fmt.Println("総利益", analytics.Profit(df.Signal)) + // fmt.Println("総損失", analytics.Loss(df.Signal)) + // fmt.Println("プロフィットファクター", analytics.ProfitFactor(df.Signal)) + // fmt.Println("最大ドローダウン", dr, "% ") + // fmt.Println("純利益", analytics.NetProfit(df.Signal)) + // fmt.Println("シャープレシオ", analytics.SharpeRatio(df.Signal, 0.02)) + // fmt.Println("トータルトレード回数", analytics.TotalTrades(df.Signal)) + // fmt.Println("勝ちトレード回数", analytics.WinningTrades(df.Signal)) + // fmt.Println("負けトレード回数", analytics.LosingTrades(df.Signal)) + // fmt.Println("平均利益", analytics.AveregeProfit(df.Signal)) + // fmt.Println("平均損失", analytics.AveregeLoss(df.Signal)) + // fmt.Println("ペイオフレシオ", analytics.PayOffRatio(df.Signal)) + // fmt.Println("1トレードの最大損失と日時", ml, mt) + // // fmt.Println("バルサラの破産確率", analytics.BalsaraAxum(df.Signal)) // s := execute.NewSignalEvents() diff --git a/pkg/analytics/profit.go b/pkg/analytics/profit.go deleted file mode 100644 index 898dea6..0000000 --- a/pkg/analytics/profit.go +++ /dev/null @@ -1,125 +0,0 @@ -package analytics - -import ( - "math" - "v1/pkg/execute" -) - -// 課題 エグジットフラグメントを実装して、空売りにも対応するProfit関数を作ろう - -func Profit(s *execute.SignalEvents) float64 { - if s == nil { - return 0.0 - } - total := 0.0 - beforeSell := 0.0 - isHolding := false - for i, signalEvent := range s.Signals { - if i == 0 && signalEvent.Side == "SELL" { - continue - } - if signalEvent.Side == "BUY" { - total -= signalEvent.Price * signalEvent.Size - isHolding = true - } - if signalEvent.Side == "SELL" { - total += signalEvent.Price * signalEvent.Size - isHolding = false - beforeSell = total - } - } - if isHolding { - return beforeSell - } - return total -} - -func Loss(s *execute.SignalEvents) float64 { - - if s == nil { - return 0.0 - } - var loss float64 = 0.0 - var buyPrice 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 == "BUY" { - buyPrice = signal.Price - } else if signal.Side == "SELL" && buyPrice != 0 { - if signal.Price < buyPrice { - loss += (buyPrice - signal.Price) * signal.Size - } - buyPrice = 0 // Reset buy price after a sell - } - } - - return loss -} - -// // TotalProfit returns the total profit of a series of signal events -// func TotalProfit(s *execute.SignalEvents) float64 { -// var totalProfit float64 = 0.0 -// for _, signal := range s.Signals { -// if signal.Side == "SELL" { -// totalProfit += Profit(s) -// } -// } -// return totalProfit -// } - -// // TotalLoss returns the total loss of a series of signal events -// func TotalLoss(s *execute.SignalEvents) float64 { -// var totalLoss float64 = 0.0 -// for _, signal := range s.Signals { -// if signal.Side == "BUY" { -// totalLoss -= Profit(s) -// } -// } -// return totalLoss -// } - -func NetProfit(s *execute.SignalEvents) float64 { - if s == nil { - return 0.0 - } - totalProfit := Profit(s) - totalLoss := Loss(s) - - return totalProfit - totalLoss -} - -func ProfitFactor(s *execute.SignalEvents) float64 { - if s == nil { - return 0.0 - } - totalProfit := Profit(s) - totalLoss := Loss(s) - - if totalLoss == 0 { - return math.Inf(1) - } - - return totalProfit / totalLoss -} - -func FinalBalance(s *execute.SignalEvents) (float64, float64) { - if s == nil { - return 0.0, 0.0 - } - - if AccountBalance == 0 { - return 0, 0 - } - - finalBlanceValue := AccountBalance + NetProfit(s) - finalBlanceRatio := finalBlanceValue / AccountBalance - - return finalBlanceValue, finalBlanceRatio -} diff --git a/pkg/analytics/profit_and_loss.go b/pkg/analytics/profit_and_loss.go new file mode 100644 index 0000000..622f439 --- /dev/null +++ b/pkg/analytics/profit_and_loss.go @@ -0,0 +1,187 @@ +package analytics + +import ( + "math" + "time" + "v1/pkg/execute" +) + +// 課題 エグジットフラグメントを実装して、空売りにも対応するProfit関数を作ろう + +// func Profit(s *execute.SignalEvents) float64 { +// if s == nil { +// return 0.0 +// } +// total := 0.0 +// beforeSell := 0.0 +// isHolding := false +// for i, signalEvent := range s.Signals { +// if i == 0 && signalEvent.Side == "SELL" { +// continue +// } +// if signalEvent.Side == "BUY" { +// total -= signalEvent.Price * signalEvent.Size +// isHolding = true +// } +// if signalEvent.Side == "SELL" { +// total += signalEvent.Price * signalEvent.Size +// isHolding = false +// beforeSell = total +// } +// } +// if isHolding { +// return beforeSell +// } +// return total +// } + +func Profit(s *execute.SignalEvents) float64 { + if s == nil { + return 0.0 + } + total := 0.0 + beforeSell := 0.0 + isHolding := false + for i, signalEvent := range s.Signals { + if i == 0 && signalEvent.Side == "SELL" { + continue + } + if signalEvent.Side == "BUY" { + total -= signalEvent.Price * signalEvent.Size + isHolding = true + } + if signalEvent.Side == "SELL" { + total += signalEvent.Price * signalEvent.Size + isHolding = false + beforeSell = total + } + } + if isHolding { + return beforeSell + } + return total +} + +func Loss(s *execute.SignalEvents) float64 { + + if s == nil { + return 0.0 + } + var loss float64 = 0.0 + var buyPrice 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 == "BUY" { + buyPrice = signal.Price + } else if signal.Side == "SELL" && buyPrice != 0 { + if signal.Price < buyPrice { + loss += (buyPrice - signal.Price) * signal.Size + } + buyPrice = 0 // Reset buy price after a sell + } + } + + return loss +} + +func NetProfit(s *execute.SignalEvents) float64 { + if s == nil { + return 0.0 + } + totalProfit := Profit(s) + totalLoss := Loss(s) + + return totalProfit - totalLoss +} + +func ProfitFactor(s *execute.SignalEvents) float64 { + if s == nil { + return 0.0 + } + totalProfit := Profit(s) + totalLoss := Loss(s) + + if totalLoss == 0 { + return math.Inf(1) + } + + return totalProfit / totalLoss +} + +func FinalBalance(s *execute.SignalEvents) (float64, float64) { + if s == nil { + return 0.0, 0.0 + } + + accountBalance := 1000.00 + + if accountBalance == 0 { + return 0, 0 + } + + finalBlanceValue := accountBalance + NetProfit(s) + finalBlanceRatio := finalBlanceValue / accountBalance + + return finalBlanceValue, finalBlanceRatio +} + +func MaxLossTrade(s *execute.SignalEvents) (float64, time.Time) { + if s == nil || s.Signals == nil || len(s.Signals) == 0 { + return 0.0, time.Time{} + } + var maxLossTrade float64 = 0.0 + var lossTime time.Time + var buyPrice float64 + for _, signal := range s.Signals { + if signal.Side != "BUY" && signal.Side != "SELL" { + continue + } + if signal.Side == "BUY" { + buyPrice = signal.Price + } else if signal.Side == "SELL" && buyPrice != 0 { + if signal.Price < buyPrice { + loss := (buyPrice - signal.Price) * signal.Size + if loss > maxLossTrade { + maxLossTrade = loss + lossTime = signal.Time + } + } + buyPrice = 0 // Reset buy price after a sell + } + } + return maxLossTrade, lossTime +} + +func MaxProfitTrade(s *execute.SignalEvents) (float64, time.Time) { + if s == nil || s.Signals == nil || len(s.Signals) == 0 { + return 0.0, time.Time{} + } + var maxProfitTrade float64 = 0.0 + var profitTime time.Time + var total float64 = 0.0 + var buyPrice float64 + for _, signal := range s.Signals { + if signal.Side != "BUY" && signal.Side != "SELL" { + continue + } + if signal.Side == "BUY" { + total -= signal.Price * signal.Size + buyPrice = signal.Price + } else if signal.Side == "SELL" && buyPrice != 0 { + total += signal.Price * signal.Size + profit := (signal.Price - buyPrice) * signal.Size + if profit > maxProfitTrade { + maxProfitTrade = profit + profitTime = signal.Time + } + } + } + return maxProfitTrade, profitTime +} diff --git a/pkg/db/models/base.go b/pkg/db/models/base.go index e036885..d66a2aa 100755 --- a/pkg/db/models/base.go +++ b/pkg/db/models/base.go @@ -1,51 +1,51 @@ package models -import ( - "database/sql" - "fmt" - "log" - "time" - "v1/pkg/config" +// import ( +// "database/sql" +// "fmt" +// "log" +// "time" +// "v1/pkg/config" - _ "github.com/mattn/go-sqlite3" -) +// _ "github.com/mattn/go-sqlite3" +// ) -const ( - tableNameSignalEvents = "signal_events" -) +// const ( +// tableNameSignalEvents = "signal_events" +// ) -var DbConnection *sql.DB +// var DbConnection *sql.DB -func GetCandleTableName(productCode []string, duration time.Duration) string { - return fmt.Sprintf("%s_%s", productCode, duration) -} +// func GetCandleTableName(productCode []string, duration time.Duration) string { +// return fmt.Sprintf("%s_%s", productCode, duration) +// } -func init() { - var err error - config := config.GetEnv() - DbConnection, err = sql.Open(config.SQLDriver, config.DbName) - if err != nil { - log.Fatalln(err) - } - cmd := fmt.Sprintf(` - CREATE TABLE IF NOT EXISTS %s ( - time DATETIME PRIMARY KEY NOT NULL, - product_code STRING, - side STRING, - price FLOAT, - size FLOAT)`, tableNameSignalEvents) - DbConnection.Exec(cmd) +// func init() { +// var err error +// config := config.GetEnv() +// DbConnection, err = sql.Open(config.SQLDriver, config.DbName) +// if err != nil { +// log.Fatalln(err) +// } +// cmd := fmt.Sprintf(` +// CREATE TABLE IF NOT EXISTS %s ( +// time DATETIME PRIMARY KEY NOT NULL, +// product_code STRING, +// side STRING, +// price FLOAT, +// size FLOAT)`, tableNameSignalEvents) +// DbConnection.Exec(cmd) - for _, duration := range config.Durations { - tableName := GetCandleTableName(config.ProductCode, duration) - c := fmt.Sprintf(` - CREATE TABLE IF NOT EXISTS %s ( - time DATETIME PRIMARY KEY NOT NULL, - open FLOAT, - close FLOAT, - high FLOAT, - low FLOAT, - volume FLOAT)`, tableName) - DbConnection.Exec(c) - } -} +// for _, duration := range config.Durations { +// tableName := GetCandleTableName(config.ProductCode, duration) +// c := fmt.Sprintf(` +// CREATE TABLE IF NOT EXISTS %s ( +// time DATETIME PRIMARY KEY NOT NULL, +// open FLOAT, +// close FLOAT, +// high FLOAT, +// low FLOAT, +// volume FLOAT)`, tableName) +// DbConnection.Exec(c) +// } +// } diff --git a/pkg/execute/signal.go b/pkg/execute/signal.go index 0653348..cabbb80 100755 --- a/pkg/execute/signal.go +++ b/pkg/execute/signal.go @@ -179,33 +179,33 @@ func (s *SignalEvents) CanShort(t time.Time) bool { return false } -func WinRate(s *SignalEvents) float64 { - var winCount, totalCount float64 - var buyPrice float64 - - for _, signal := range s.Signals { - if signal.Side == "BUY" { - buyPrice = signal.Price - } else if signal.Side == "SELL" { - totalCount++ - if signal.Price > buyPrice { - winCount++ - } - buyPrice = 0 // Reset buy price after a sell - } - } +// func WinRate(s *SignalEvents) float64 { +// var winCount, totalCount float64 +// var buyPrice float64 - if totalCount == 0 { - return 0 - } +// for _, signal := range s.Signals { +// if signal.Side == "BUY" { +// buyPrice = signal.Price +// } else if signal.Side == "SELL" { +// totalCount++ +// if signal.Price > buyPrice { +// winCount++ +// } +// buyPrice = 0 // Reset buy price after a sell +// } +// } - return winCount / totalCount -} +// if totalCount == 0 { +// return 0 +// } + +// return winCount / totalCount +// } -func (s *SignalEvents) Buy(strategyName string, assetName string, duration string, date time.Time, price, size float64, save bool) float64 { +func (s *SignalEvents) Buy(strategyName string, assetName string, duration string, date time.Time, price, size float64, save bool) bool { if !s.CanLong(date) { - return 0 + return false } signalEvent := SignalEvent{ @@ -223,11 +223,11 @@ func (s *SignalEvents) Buy(strategyName string, assetName string, duration strin } else { - return 0 + return false } s.Signals = append(s.Signals, signalEvent) - return size // sizeを返す + return true } func (s *SignalEvents) Sell(strategyName string, assetName string, duration string, date time.Time, price, size float64, save bool) bool { diff --git a/pkg/strategey/bb.go b/pkg/strategey/bb.go index f2c762f..1e8f8bb 100644 --- a/pkg/strategey/bb.go +++ b/pkg/strategey/bb.go @@ -1,33 +1,118 @@ package strategey // import ( -// "v1/pkg/data/query" +// "fmt" +// "math" +// "v1/pkg/analytics" // "v1/pkg/execute" // "github.com/markcheno/go-talib" // ) -// var df, err = query.GetCandleData("SOLUSDT", "4h") +// func (df *DataFrameCandle) BBStrategy(n int, k float64, account *Account) *execute.SignalEvents { -// func BackTestBb(n int, k float64) *execute.SignalEvents { -// cd +// var StrategyName = "BB" +// lenCandles := len(df.Candles) // if lenCandles <= n { // return nil // } -// signalEvents := &execute.SignalEvents{} +// signalEvents := execute.NewSignalEvents() // bbUp, _, bbDown := talib.BBands(df.Closes(), n, k, k, 0) + +// buySize := 0.0 +// isBuyHolding := false // for i := 1; i < lenCandles; i++ { // if i < n { // continue // } -// if bbDown[i-1] > df.Candles[i-1].Close && bbDown[i] <= df.Candles[i].Close { -// signalEvents.Buy(df.ProductCode, df.Candles[i].Time, df.Candles[i].Close, 1.0, false) +// if bbDown[i-1] > df.Candles[i-1].Close && bbDown[i] <= df.Candles[i].Close && !isBuyHolding { +// buySize = account.TradeSize(0.9) / df.Candles[i].Close +// signalEvents.Buy(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) +// isBuyHolding = true // } -// if bbUp[i-1] < df.Candles[i-1].Close && bbUp[i] >= df.Candles[i].Close { -// signalEvents.Sell(df.ProductCode, df.Candles[i].Time, df.Candles[i].Close, 1.0, false) +// if bbUp[i-1] < df.Candles[i-1].Close && bbUp[i] >= df.Candles[i].Close && isBuyHolding { +// signalEvents.Sell(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) +// isBuyHolding = false // } // } // return signalEvents // } + +// func (df *DataFrameCandle) OptimizeBbProfit() (performance float64, bestN int, bestK float64) { +// bestN = 20 +// bestK = 2.0 + +// for n := 13; n < 200; n++ { +// for k := 2.0; k < 3.5; k += 0.1 { +// signalEvents := df.BBStrategy(n, k, accountBlance) +// if signalEvents == nil { +// continue +// } +// profit := analytics.Profit(signalEvents) +// if performance < profit { +// performance = profit +// bestN = n +// bestK = k +// } +// } +// } + +// fmt.Println("最高利益", performance, "最適なピリオド", bestN, "最適な標準偏差", bestK) + +// return performance, bestN, bestK +// } + +// func (df *DataFrameCandle) OptimizeBbLoss() (performance float64, bestN int, bestK float64) { +// bestN = 20 +// bestK = 2.0 +// performance = math.MaxFloat64 + +// for n := 5; n < 120; n++ { +// for k := 1.8; k < 3.8; k += 0.1 { +// signalEvents := df.BBStrategy(n, k, accountBlance) +// if signalEvents == nil { +// continue +// } +// loss := analytics.Loss(signalEvents) +// if performance < loss { +// performance = loss +// bestN = n +// bestK = k +// } +// } +// } + +// fmt.Println("損失", performance, "最適なピリオド", bestN, "最適な標準偏差", bestK) + +// return performance, bestN, bestK +// } + +// func (df *DataFrameCandle) OptimizeBbWinRate() (performance float64, bestN int, bestK float64) { +// bestN = 20 +// bestK = 2.0 + +// for n := 13; n < 200; n++ { +// for k := 2.0; k < 3.5; k += 0.1 { +// signalEvents := df.BBStrategy(n, k, accountBlance) +// if signalEvents == nil { +// continue +// } + +// if analytics.TotalTrades(signalEvents) < 5 { +// continue +// } +// winrate := analytics.WinRate(signalEvents) +// if performance < winrate { +// performance = winrate +// bestN = n +// bestK = k +// } +// } +// } + +// fmt.Println("最高勝率", performance, "最適なピリオド", bestN, "最適な標準偏差", bestK) + +// return performance, bestN, bestK +// } diff --git a/pkg/strategey/donchain.go b/pkg/strategey/donchain.go index 185242d..80db61d 100644 --- a/pkg/strategey/donchain.go +++ b/pkg/strategey/donchain.go @@ -2,21 +2,22 @@ package strategey import ( "fmt" + "log" "v1/pkg/analytics" "v1/pkg/execute" "v1/pkg/indicator/indicators" ) -const StrategyName = "DBO" +var AccountBalance = NewAccount(1000.00) -func TradeSize(persetege float64) float64 { - - size := AccountBalance * persetege - return size +func GetStrageyName() string { + return "DBO" } -func (df *DataFrameCandle) DonchainStrategy(period int) *execute.SignalEvents { +func (df *DataFrameCandle) DonchainStrategy(period int, account *Account) *execute.SignalEvents { + var StrategyName = "DBO" + // fmt.Println("アカウントバランス", account.Balance) lenCandles := len(df.Candles) if lenCandles <= period { return nil @@ -37,22 +38,26 @@ func (df *DataFrameCandle) DonchainStrategy(period int) *execute.SignalEvents { continue } if close[i] > donchain.High[i-1] && !isHolding { - buySize = TradeSize(0.2) / df.Candles[i].Close - signalEvents.Buy(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) - isHolding = true + buySize = account.TradeSize(0.2) / df.Candles[i].Close + if account.Buy(df.Candles[i].Close, buySize) { + signalEvents.Buy(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) + isHolding = true + } } if close[i] < donchain.Low[i-1] && isHolding { - signalEvents.Sell(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) - isHolding = false + if account.Sell(df.Candles[i].Close) { + signalEvents.Sell(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) + isHolding = false + + } } } - return signalEvents } -func (df *DataFrameCandle) OptimizeProfitDonchain() (performance float64, bestPeriod int) { +func (df *DataFrameCandle) OptimizeDonchainProfit() (performance float64, bestPeriod int) { if df == nil { return 0.0, 0 } @@ -60,7 +65,7 @@ func (df *DataFrameCandle) OptimizeProfitDonchain() (performance float64, bestPe for period := 10; period < 333; period++ { - signalEvents := df.DonchainStrategy(period) + signalEvents := df.DonchainStrategy(period, AccountBalance) if signalEvents == nil { continue } @@ -78,12 +83,12 @@ func (df *DataFrameCandle) OptimizeProfitDonchain() (performance float64, bestPe return performance, bestPeriod } -func (df *DataFrameCandle) OptimizeWinRateDonchain() (performance float64, bestPeriod int) { +func (df *DataFrameCandle) OptimizeDonchainWinRate() (performance float64, bestPeriod int) { bestPeriod = 40 for period := 10; period < 333; period++ { - signalEvents := df.DonchainStrategy(period) + signalEvents := df.DonchainStrategy(period, AccountBalance) if signalEvents == nil { continue } @@ -100,51 +105,47 @@ func (df *DataFrameCandle) OptimizeWinRateDonchain() (performance float64, bestP return performance, bestPeriod } -// func DonchainStrategeyBacktest(assetName string, duration string) ([]bool, []bool, []bool, []bool) { - -// var ohlc, e = query.GetOHLCData(assetName, duration) -// if e != nil { -// log.Fatal(e) -// } - -// var h []float64 -// var l []float64 -// var c []float64 - -// for _, data := range ohlc { -// h = append(h, data.High) -// l = append(l, data.Low) -// c = append(c, data.Close) -// } - -// d := indicators.Donchain(h, l, 40) - -// var buySignals []bool -// var sellSignals []bool -// var shortExitSignals []bool -// var longExitSignals []bool - -// for i := range c { -// var buySignal bool = false -// var sellSignal bool = false -// var shortExitSignal bool = false -// var longExitSignal bool = false - -// if c[i] > d.High[i] { -// buySignal = true -// shortExitSignal = true -// } - -// if c[i] < d.Low[i] { -// sellSignal = true -// longExitSignal = true -// } - -// buySignals = append(buySignals, buySignal) -// sellSignals = append(sellSignals, sellSignal) -// shortExitSignals = append(shortExitSignals, shortExitSignal) -// longExitSignals = append(longExitSignals, longExitSignal) -// } - -// return buySignals, sellSignals, shortExitSignals, longExitSignals -// } +func RunBacktestDonchain() { + + strategyName := GetStrageyName() + assetName := "OPUSDT" + duration := "1h" + + df, _ := GetCandleData(assetName, duration) + + tableName := strategyName + "_" + assetName + "_" + duration + + _, err := execute.CreateDBTable(tableName) + if err != nil { + log.Fatal(err) + } + + // df, _ := strategey.GetCandleData(assetName, duration) + + // profit, period := df.OptimizeProfitDonchain() + + // if profit > 0 { + + // df.Signal = df.DonchainStrategy(period) + + // } + + // winrate, period := df.OptimizeWinRateDonchain() + + // if winrate > 0 { + + // df.Signal = df.DonchainStrategy(period) + + // } + + performance, bestPeriod := df.OptimizeDonchainProfit() + + if performance > 0 { + + df.Signal = df.DonchainStrategy(bestPeriod, AccountBalance) + + } + + Result(df.Signal) + +} diff --git a/pkg/strategey/ema.go b/pkg/strategey/ema.go new file mode 100644 index 0000000..6572ad2 --- /dev/null +++ b/pkg/strategey/ema.go @@ -0,0 +1,88 @@ +package strategey + +// import ( +// "fmt" +// "v1/pkg/analytics" +// "v1/pkg/execute" + +// "github.com/markcheno/go-talib" +// ) + +// func (df *DataFrameCandle) EmaStrategy(period1, period2 int, account *Account) *execute.SignalEvents { + +// var StrategyName = "EMA" +// lenCandles := len(df.Candles) +// if lenCandles <= period1 || lenCandles <= period2 { +// return nil +// } +// signalEvents := execute.NewSignalEvents() +// emaValue1 := talib.Ema(df.Closes(), period1) +// emaValue2 := talib.Ema(df.Closes(), period2) +// rsiValue := talib.Rsi(df.Closes(), 14) + +// buySize := 0.0 +// isBuyHolding := false +// for i := 1; i < lenCandles; i++ { +// if i < period1 || i < period2 { +// continue +// } +// if emaValue1[i-1] < emaValue2[i-1] && emaValue1[i] >= emaValue2[i] && !isBuyHolding { +// buySize = account.TradeSize(0.9) / df.Candles[i].Close +// signalEvents.Buy(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) +// isBuyHolding = true +// } +// if emaValue1[i-1] > emaValue2[i-1] && emaValue1[i] <= emaValue2[i] && isBuyHolding || rsiValue[i] < 30.0 { +// signalEvents.Sell(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) +// isBuyHolding = false +// } +// } +// return signalEvents +// } + +// func (df *DataFrameCandle) OptimizeEma() (performance float64, bestPeriod1 int, bestPeriod2 int) { +// bestPeriod1 = 5 +// bestPeriod2 = 21 + +// for period1 := 20; period1 < 120; period1++ { +// for period2 := 50; period2 < 250; period2++ { +// signalEvents := df.EmaStrategy(period1, period2, accountBlance) +// if signalEvents == nil { +// continue +// } +// profit := analytics.Profit(signalEvents) +// if performance < profit { +// performance = profit +// bestPeriod1 = period1 +// bestPeriod2 = period2 +// } +// } +// } + +// fmt.Println("最高利益", performance, "最適なピリオド1", bestPeriod1, "最適なピリオド2", bestPeriod2) + +// return performance, bestPeriod1, bestPeriod2 +// } + +// func (df *DataFrameCandle) OptimizeEmaWinRate() (performance float64, bestPeriod1 int, bestPeriod2 int) { +// bestPeriod1 = 5 +// bestPeriod2 = 21 + +// for period1 := 10; period1 < 100; period1++ { +// for period2 := 20; period2 < 250; period2++ { +// signalEvents := df.EmaStrategy(period1, period2, accountBlance) +// if signalEvents == nil { +// continue +// } +// winrate := analytics.WinRate(signalEvents) +// if performance < winrate { +// performance = winrate +// bestPeriod1 = period1 +// bestPeriod2 = period2 +// } +// } +// } + +// fmt.Println("最高勝率", performance, "最適なピリオド1", bestPeriod1, "最適なピリオド2", bestPeriod2) + +// return performance, bestPeriod1, bestPeriod2 +// } diff --git a/pkg/strategey/peper_acount.go b/pkg/strategey/peper_acount.go new file mode 100644 index 0000000..e26c581 --- /dev/null +++ b/pkg/strategey/peper_acount.go @@ -0,0 +1,43 @@ +package strategey + +type Account struct { + Balance float64 + PositionSize float64 +} + +func NewAccount(initialBalance float64) *Account { + return &Account{Balance: initialBalance, PositionSize: 0.0} +} + +func (a *Account) TradeSize(persetege float64) float64 { + size := a.Balance * persetege + // fmt.Println("トレードサイズ内でのアカウントバランス", a.Balance) + return size +} + +func (a *Account) Buy(price, size float64) bool { + cost := price * size + if cost > a.Balance { + return false + } + a.Balance -= cost + a.PositionSize = size + return true +} + +func (a *Account) Sell(price float64) bool { + if a.PositionSize <= 0 { + return false + } + a.Balance += price * a.PositionSize + a.PositionSize = 0.0 + return true +} + +func (a *Account) GetBalance() float64 { + return a.Balance +} + +func (a *Account) GetPositionSize() float64 { + return a.PositionSize +} diff --git a/pkg/strategey/positionsize.go b/pkg/strategey/positionsize.go index 187b161..9e3f03e 100644 --- a/pkg/strategey/positionsize.go +++ b/pkg/strategey/positionsize.go @@ -1,79 +1,112 @@ package strategey import ( + "math" "v1/pkg/execute" ) -var AccountBalance float64 = 1000.000 +// var AccountBalance float64 = 1000.000 func Profit(s *execute.SignalEvents) float64 { - var profit float64 = 0.0 - var buyPrice, sellPrice float64 - var buySize, sellSize float64 - - for _, signal := range s.Signals { - if signal.Side == "BUY" { - buyPrice = signal.Price - buySize = signal.Size - } else if signal.Side == "SELL" { - sellPrice = signal.Price - sellSize = signal.Size - profit += (sellPrice - buyPrice) * min(buySize, sellSize) + if s == nil { + return 0.0 + } + total := 0.0 + beforeSell := 0.0 + isHolding := false + for i, signalEvent := range s.Signals { + if i == 0 && signalEvent.Side == "SELL" { + continue + } + if signalEvent.Side == "BUY" { + total -= signalEvent.Price * signalEvent.Size + isHolding = true + } + if signalEvent.Side == "SELL" { + total += signalEvent.Price * signalEvent.Size + isHolding = false + beforeSell = total } } - - return profit + if isHolding { + return beforeSell + } + return total } -func TotalProfit(s *execute.SignalEvents) float64 { - var totalProfit float64 = 0.0 - var buyPrice, sellPrice float64 - var buySize, sellSize float64 +func Loss(s *execute.SignalEvents) float64 { + + if s == nil { + return 0.0 + } + var loss float64 = 0.0 + var buyPrice 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 == "BUY" { buyPrice = signal.Price - buySize = signal.Size - } else if signal.Side == "SELL" { - sellPrice = signal.Price - sellSize = signal.Size - profit := (sellPrice - buyPrice) * min(buySize, sellSize) / buyPrice * AccountBalance - if profit > 0 { - totalProfit += profit + } else if signal.Side == "SELL" && buyPrice != 0 { + if signal.Price < buyPrice { + loss += (buyPrice - signal.Price) * signal.Size } + buyPrice = 0 // Reset buy price after a sell } } - return totalProfit + return loss } -func TotalLoss(s *execute.SignalEvents) float64 { - var totalLoss float64 = 0.0 - var buyPrice, sellPrice float64 - var buySize, sellSize float64 +// // TotalProfit returns the total profit of a series of signal events +// func TotalProfit(s *execute.SignalEvents) float64 { +// var totalProfit float64 = 0.0 +// for _, signal := range s.Signals { +// if signal.Side == "SELL" { +// totalProfit += Profit(s) +// } +// } +// return totalProfit +// } - for _, signal := range s.Signals { - if signal.Side == "BUY" { - buyPrice = signal.Price - buySize = signal.Size - } else if signal.Side == "SELL" { - sellPrice = signal.Price - sellSize = signal.Size - profit := (sellPrice - buyPrice) * min(buySize, sellSize) / buyPrice * AccountBalance - if profit < 0 { - totalLoss -= profit - } - } +// // TotalLoss returns the total loss of a series of signal events +// func TotalLoss(s *execute.SignalEvents) float64 { +// var totalLoss float64 = 0.0 +// for _, signal := range s.Signals { +// if signal.Side == "BUY" { +// totalLoss -= Profit(s) +// } +// } +// return totalLoss +// } + +func NetProfit(s *execute.SignalEvents) float64 { + if s == nil { + return 0.0 } + totalProfit := Profit(s) + totalLoss := Loss(s) - return totalLoss + return totalProfit - totalLoss } -func NetProfit(s *execute.SignalEvents) float64 { - totalProfit := TotalProfit(s) - totalLoss := TotalLoss(s) +func ProfitFactor(s *execute.SignalEvents) float64 { + if s == nil { + return 0.0 + } + totalProfit := Profit(s) + totalLoss := Loss(s) - return totalProfit - totalLoss + if totalLoss == 0 { + return math.Inf(1) + } + + return totalProfit / totalLoss } // func RiskSizeCalculator(s *execute.SignalEvents) float64 { diff --git a/pkg/strategey/rsi.go b/pkg/strategey/rsi.go index 3197d9c..bd60b96 100644 --- a/pkg/strategey/rsi.go +++ b/pkg/strategey/rsi.go @@ -1,56 +1,270 @@ package strategey -import ( - "v1/pkg/execute" - - "github.com/markcheno/go-talib" -) - -func (df *DataFrameCandle) RsiStrategy(period int, buyThread, sellThread float64) *execute.SignalEvents { - lenCandles := len(df.Candles) - if lenCandles <= period { - return nil - } - - signalEvents := execute.NewSignalEvents() - values := talib.Rsi(df.Closes(), period) - - buySize := 0.0 - isHolding := false - for i := 1; i < lenCandles; i++ { - if values[i-1] == 0 || values[i-1] == 100 { - continue - } - if values[i-1] < buyThread && values[i] >= buyThread { - buySize = TradeSize(0.2) / df.Candles[i].Close - signalEvents.Buy(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) - isHolding = true - } - - if values[i-1] > sellThread && values[i] <= sellThread { - signalEvents.Sell(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) - isHolding = false - } - } - return signalEvents -} - -func (df *DataFrameCandle) OptimizeRsi() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { - bestPeriod = 14 - bestBuyThread, bestSellThread = 30.0, 70.0 - - for period := 5; period < 25; period++ { - signalEvents := df.RsiStrategy(period, bestBuyThread, bestSellThread) - if signalEvents == nil { - continue - } - profit := Profit(signalEvents) - if performance < profit { - performance = profit - bestPeriod = period - bestBuyThread = bestBuyThread - bestSellThread = bestSellThread - } - } - return performance, bestPeriod, bestBuyThread, bestSellThread -} +// import ( +// "fmt" +// "math" +// "v1/pkg/analytics" +// "v1/pkg/execute" + +// "github.com/markcheno/go-talib" +// ) + +// func (df *DataFrameCandle) RsiStrategy(period int, buyThread float64, sellThread float64, account *Account) *execute.SignalEvents { + +// var StrategyName = "RSI" +// lenCandles := len(df.Candles) +// if lenCandles <= period { +// return nil +// } + +// signalEvents := execute.NewSignalEvents() + +// values := talib.Rsi(df.Closes(), period) + +// buySize := 0.0 +// isBuyHolding := false +// for i := 1; i < lenCandles; i++ { +// if values[i-1] == 0 || values[i-1] == 100 { +// continue +// } +// if values[i-1] < buyThread && values[i] >= buyThread && !isBuyHolding { +// buySize = account.TradeSize(0.9) / df.Candles[i].Close +// signalEvents.Buy(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) +// isBuyHolding = true +// } + +// if values[i-1] > float64(sellThread) && values[i] <= float64(sellThread) && isBuyHolding { +// signalEvents.Sell(StrategyName, df.AssetName, df.Duration, df.Candles[i].Date, df.Candles[i].Close, buySize, true) +// isBuyHolding = false +// } +// } + +// return signalEvents +// } + +// func (df *DataFrameCandle) OptimizeRsi() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { +// bestPeriod = 14 +// bestBuyThread, bestSellThread = 15.0, 80.0 + +// for period := 5; period < 60; period++ { + +// signalEvents := df.RsiStrategy(period, bestBuyThread, bestSellThread, accountBlance) +// if signalEvents == nil { +// continue +// } + +// profit := Profit(signalEvents) +// if performance < profit { +// performance = profit +// bestPeriod = period +// bestBuyThread = bestBuyThread +// bestSellThread = bestSellThread +// } + +// } + +// fmt.Println("最高利益", performance, "最適なピリオド", bestPeriod, "最適な上限ライン", bestBuyThread, "最適な下限ライン", bestSellThread) + +// return performance, bestPeriod, bestBuyThread, bestSellThread +// } + +// func (df *DataFrameCandle) OptimizeRsi2() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { +// bestPeriod = 14 +// bestBuyThread, bestSellThread = 20.0, 80.0 + +// for period := 5; period < 30; period++ { +// for buyThread := 25.0; buyThread > 10; buyThread -= 1 { + +// for sellThread := 75.0; sellThread < 95; sellThread += 1 { +// signalEvents := df.RsiStrategy(period, buyThread, sellThread, accountBlance) +// if signalEvents == nil { +// continue +// } + +// profit := Profit(signalEvents) +// if performance < profit { +// performance = profit +// bestPeriod = period +// bestBuyThread = buyThread +// bestSellThread = sellThread +// } +// } +// } + +// } + +// fmt.Println("最高利益", performance, "最適なピリオド", bestPeriod, "最適な買いライン", bestBuyThread, "最適な売りライン", bestSellThread) + +// return performance, bestPeriod, bestBuyThread, bestSellThread +// } + +// func (df *DataFrameCandle) OptimizeRsiWinRate() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { +// bestPeriod = 14 +// bestBuyThread, bestSellThread = 20.0, 80.0 + +// for period := 5; period < 30; period++ { +// for buyThread := 25.0; buyThread > 10; buyThread -= 1 { + +// for sellThread := 75.0; sellThread < 95; sellThread += 1 { +// signalEvents := df.RsiStrategy(period, buyThread, sellThread, accountBlance) +// if signalEvents == nil { +// continue +// } + +// if analytics.TotalTrades(signalEvents) < 20 { +// continue +// } + +// winrate := analytics.WinRate(signalEvents) +// if performance < winrate { +// performance = winrate +// bestPeriod = period +// bestBuyThread = buyThread +// bestSellThread = sellThread +// } +// } +// } + +// } + +// fmt.Println("最高勝率", performance*100, "%", "最適なピリオド", bestPeriod, "最適な買いライン", bestBuyThread, "最適な売りライン", bestSellThread) + +// return performance, bestPeriod, bestBuyThread, bestSellThread +// } + +// func (df *DataFrameCandle) OptimizeRsiLoss() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { +// bestPeriod = 14 +// bestBuyThread, bestSellThread = 20.0, 80.0 +// performance = math.MaxFloat64 + +// for period := 5; period < 30; period++ { +// for buyThread := 25.0; buyThread > 10; buyThread -= 1 { + +// for sellThread := 75.0; sellThread < 96; sellThread += 1 { +// signalEvents := df.RsiStrategy(period, buyThread, sellThread, accountBlance) +// if signalEvents == nil { +// continue +// } + +// if analytics.TotalTrades(signalEvents) < 20 { +// continue +// } + +// loss := analytics.Loss(signalEvents) +// if performance > loss { +// performance = loss +// bestPeriod = period +// bestBuyThread = buyThread +// bestSellThread = sellThread +// } +// } +// } + +// } + +// fmt.Println("損失", performance, "最適なピリオド", bestPeriod, "最適な買いライン", bestBuyThread, "最適な売りライン", bestSellThread) + +// return performance, bestPeriod, bestBuyThread, bestSellThread +// } + +// func (df *DataFrameCandle) OptimizeRsiProfitFactor() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { +// bestPeriod = 14 +// bestBuyThread, bestSellThread = 20.0, 80.0 + +// for period := 4; period < 30; period++ { +// for buyThread := 30.0; buyThread > 10; buyThread -= 1 { + +// for sellThread := 70.0; sellThread < 96; sellThread += 1 { +// signalEvents := df.RsiStrategy(period, buyThread, sellThread, accountBlance) +// if signalEvents == nil { +// continue +// } + +// if analytics.TotalTrades(signalEvents) < 40 { +// continue +// } + +// profitFactor := analytics.ProfitFactor(signalEvents) +// if performance < profitFactor { +// performance = profitFactor +// bestPeriod = period +// bestBuyThread = buyThread +// bestSellThread = sellThread +// } +// } +// } + +// } + +// fmt.Println("プロフィットファクター", performance, "最適なピリオド", bestPeriod, "最適な買いライン", bestBuyThread, "最適な売りライン", bestSellThread) + +// return performance, bestPeriod, bestBuyThread, bestSellThread +// } + +// func (df *DataFrameCandle) OptimizeRsiPayOffRatio() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { +// bestPeriod = 14 +// bestBuyThread, bestSellThread = 20.0, 80.0 + +// for period := 4; period < 25; period++ { +// for buyThread := 30.0; buyThread > 10; buyThread -= 1 { + +// for sellThread := 75.0; sellThread < 96; sellThread += 1 { +// signalEvents := df.RsiStrategy(period, buyThread, sellThread, accountBlance) +// if signalEvents == nil { +// continue +// } + +// if analytics.TotalTrades(signalEvents) < 40 { +// continue +// } + +// payOffRatio := analytics.PayOffRatio(signalEvents) +// if performance < payOffRatio { +// performance = payOffRatio +// bestPeriod = period +// bestBuyThread = buyThread +// bestSellThread = sellThread +// } +// } +// } + +// } + +// fmt.Println("プロフィットファクター", performance, "最適なピリオド", bestPeriod, "最適な買いライン", bestBuyThread, "最適な売りライン", bestSellThread) + +// return performance, bestPeriod, bestBuyThread, bestSellThread +// } + +// func (df *DataFrameCandle) OptimizeRsiSharpRatio() (performance float64, bestPeriod int, bestBuyThread, bestSellThread float64) { +// bestPeriod = 14 +// bestBuyThread, bestSellThread = 20.0, 80.0 + +// for period := 5; period < 30; period++ { +// for buyThread := 25.0; buyThread > 10; buyThread -= 1 { + +// for sellThread := 75.0; sellThread < 95; sellThread += 1 { +// signalEvents := df.RsiStrategy(period, buyThread, sellThread, accountBlance) +// if signalEvents == nil { +// continue +// } + +// if analytics.TotalTrades(signalEvents) < 40 { +// continue +// } + +// sharpeRatio := analytics.SharpeRatio(signalEvents, 0.06) +// if performance < sharpeRatio { +// performance = sharpeRatio +// bestPeriod = period +// bestBuyThread = buyThread +// bestSellThread = sellThread +// } +// } +// } + +// } + +// fmt.Println("シャープレシオ", performance, "最適なピリオド", bestPeriod, "最適な買いライン", bestBuyThread, "最適な売りライン", bestSellThread) + +// return performance, bestPeriod, bestBuyThread, bestSellThread +// } diff --git a/pkg/strategey/strategy.go b/pkg/strategey/strategy.go index e1cabf3..243248d 100755 --- a/pkg/strategey/strategy.go +++ b/pkg/strategey/strategy.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "time" + "v1/pkg/analytics" "v1/pkg/data" dbquery "v1/pkg/data/query" "v1/pkg/execute" @@ -95,6 +96,14 @@ func GetCandleData(assetName string, duration string) (*DataFrameCandle, error) return dfCandle, nil } +func (df *DataFrameCandle) Time() []time.Time { + s := make([]time.Time, len(df.Candles)) + for i, candle := range df.Candles { + s[i] = candle.Date + } + return s +} + func (df *DataFrameCandle) Closes() []float64 { s := make([]float64, len(df.Candles)) for i, candle := range df.Candles { @@ -126,3 +135,43 @@ func (df *DataFrameCandle) Volume() []float64 { } return s } + +func Result(s *execute.SignalEvents) { + + if s == nil { + return + } + + l, lr := analytics.FinalBalance(s) + d := analytics.MaxDrawdown(s) + dr := d * 100 + + ml, mt := analytics.MaxLossTrade(s) + + // fmt.Println(s) + + n := s.Signals[0] + + name := n.StrategyName + "_" + n.AssetName + "_" + n.Duration + + fmt.Println(name) + fmt.Println("初期残高", AccountBalance.GetBalance()) + fmt.Println("最終残高", l, "比率", lr) + fmt.Println("勝率", analytics.WinRate(s)*100, "%") + fmt.Println("総利益", analytics.Profit(s)) + fmt.Println("総損失", analytics.Loss(s)) + fmt.Println("プロフィットファクター", analytics.ProfitFactor(s)) + fmt.Println("最大ドローダウン", dr, "% ") + fmt.Println("純利益", analytics.NetProfit(s)) + fmt.Println("シャープレシオ", analytics.SharpeRatio(s, 0.02)) + fmt.Println("トータルトレード回数", analytics.TotalTrades(s)) + fmt.Println("勝ちトレード回数", analytics.WinningTrades(s)) + fmt.Println("負けトレード回数", analytics.LosingTrades(s)) + fmt.Println("平均利益", analytics.AveregeProfit(s)) + fmt.Println("平均損失", analytics.AveregeLoss(s)) + fmt.Println("ペイオフレシオ", analytics.PayOffRatio(s)) + fmt.Println("1トレードの最大損失と日時", ml, mt) + // fmt.Println("バルサラの破産確率", analytics.BalsaraAxum(s)) + + fmt.Println(s) +} diff --git a/web/src/lib/Chart.svelte b/web/src/lib/Chart.svelte deleted file mode 100644 index 0e2cda0..0000000 --- a/web/src/lib/Chart.svelte +++ /dev/null @@ -1,68 +0,0 @@ - - - Awesome go-echarts - - - - - - - -
-
-
- - - -
-
-
- - - -
- - -
-
-
- - - -
- - -