func Sell() string { if PrevTrade == "sell" { return "0" } // compute the price price, _, warning := getSellPrice() // compute the amount Available_coin := GetAvailable_coin() if Available_coin < 0.01 { warning = "没有足够的币" logger.Infoln(warning) PrevTrade = "sell" PrevBuyPirce = 0 return "0" } amount := Option["tradeAmount"] nAmount, err := strconv.ParseFloat(amount, 64) if err != nil { logger.Infoln("amount is not float") return "0" } if nAmount > Available_coin { nAmount = Available_coin amount = fmt.Sprintf("%02f", nAmount) } sellID := sell(price, amount) if sellID == "-1" { warning += " [模拟]" } else if sellID == "0" { warning += "[委托失败]" } else { warning += "[委托成功]" + sellID } logger.Infoln(warning) var coin string if Option["symbol"] == "btc_cny" { coin = "比特币" } else { coin = "莱特币" } if sellID != "0" { if !GetBacktest() { logger.Tradef("在%s,根据策略%s周期%s,以价格%s卖出%s个%s\n", Option["tradecenter"], Option["strategy"], Option["tick_interval"], price, amount, coin) go email.TriggerTrender(warning) } else { t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%s卖出%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], price, amount, coin) } } return sellID }
func (strategy *KDJStrategy) kdjSell(price float64) { price = price - 0.5 strategy.PrevPrice = price priceStr := fmt.Sprintf("%f", price) amount := Option["tradeAmount"] sell(priceStr, amount) strategy.PrevTrade = "sell" t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%f卖出%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], price, amount, "比特币") }
func (threelineStrategy *ThreelineStrategy) Sell(price float64) { price = price - StringToFloat(Option["slippage"]) threelineStrategy.BuyPrice = 0 priceStr := fmt.Sprintf("%f", price) amount := Option["tradeAmount"] sell(priceStr, amount) threelineStrategy.PrevTrade = "sell" t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%f卖出%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], price, amount, coinName()) }
func backtesting() { fmt.Println("back testing begin...") SetBacktest(true) SimAccount = make(map[string]string) SimAccount["CNY"] = IntegerToString(BackTestCNY) SimAccount["BTC"] = "0" SimAccount["LTC"] = "0" SaveSimulate() btEnd := time.Now() btEnd = time.Date(btEnd.Year(), btEnd.Month(), btEnd.Day(), btEnd.Hour(), btEnd.Minute(), 0, 0, time.Local) //btEnd = btEnd.AddDate(0, 0, -1) //btStart := btEnd.AddDate(0, -3, 0) btEnd = btEnd.Add(0 - time.Second*3600*2) btStart := btEnd.Add(0 - time.Second*3600*24*30) day := time.Second * 3600 * 24 dayshift := 0 btEnd = btEnd.Add(0 - day*time.Duration(dayshift)) btStart = btStart.Add(0 - day*time.Duration(dayshift)) var symbolId string if Option["symbol"] == "btc_cny" { symbolId = "btccny" } else { symbolId = "ltccny" } fmt.Println("回测开始时间", btStart.Format("2006-01-02 15:04:05")) fmt.Println("回测结束时间", btEnd.Format("2006-01-02 15:04:05")) period := StringToInteger(Option["tick_interval"]) var records []Record var err error datacenter := Option["datacenter"] if datacenter == "huobi" { records, err = GetKLine(btStart, btEnd, period, symbolId) } else { records, err = OkcoinKLine(period, symbolId) } if err != nil { fmt.Println(err) return } else { fmt.Println(len(records)) } if Option["tick_interval"] != "1" { // check exception data for i := 0; i < len(records)-1; i++ { if strategy.CheckException(records[i], records[i+1]) == false { logger.Errorln("detect exception data ", records[i].Close, records[i+1].Close, records[i+1].Volumn) return } } } filepath := btStart.Format("2006-01-02") + "_" + btEnd.Format("2006-01-02") filepath = "\\test\\" + filepath + "_" + IntegerToString(period) + "_" + symbolId filepath = filepath + "_" + Option["strategy"] + ".txt" logger.SetBacktestFile(filepath) DeleteFile(ROOT + filepath) for i := ShiftNumber; i < len(records); i++ { var rec []Record if i < MaxKLineLength { rec = records[0:i] } else { rec = records[i-MaxKLineLength : i] } SetBtTime(rec[len(rec)-1].Time) SetBtPrice(records[i].Close) strategy.Tick(simulate.NewSimulate(), rec) } var coin float64 var total float64 if symbolId == "btccny" { coin = StringToFloat(SimAccount["BTC"]) total = coin*records[len(records)-1].Close + StringToFloat(SimAccount["CNY"]) } else { coin = StringToFloat(SimAccount["LTC"]) total = coin*records[len(records)-1].Close + StringToFloat(SimAccount["CNY"]) } logger.Backtestf("资金总计:%f 盈亏比:%f \n", total, (total-BackTestCNY)/BackTestCNY*100) //peroids := []int{1, 5, 15, 30, 60, 100} //for _, peroid := range peroids { // if huobi.AnalyzeKLinePeroid("btc_cny", peroid) == true { // } else { // logger.Errorln("TradeKLine failed.") // } //} //fmt.Println("生成 1/5/15/30/60分钟及1天 周期的后向测试报告于log/reportxxx.log文件中,请查看") fmt.Println("back testing end ...") }
// KDJ-EX strategy func (kdjex *KDJexStrategy) Tick(records []Record) bool { const btcslap = 0.2 const ltcslap = 0.01 const timeout = 300 // 秒 const ordercount = 5 tradeAmount := Option["tradeAmount"] numTradeAmount, err := strconv.ParseFloat(Option["tradeAmount"], 64) if err != nil { logger.Errorln("config item tradeAmount is not float") return false } var slappage float64 symbol := Option["symbol"] if symbol == "btc_cny" { slappage = btcslap } else { slappage = ltcslap } var coin string if Option["symbol"] == "btc_cny" { coin = "比特币" } else { coin = "莱特币" } nSplitTradeAmount := numTradeAmount / float64(ordercount) splitTradeAmount := fmt.Sprintf("%f", nSplitTradeAmount) stoploss, err := strconv.ParseFloat(Option["stoploss"], 64) if err != nil { logger.Errorln("config item stoploss is not float") return false } var Time []string var Price []float64 var Volumn []float64 for _, v := range records { Time = append(Time, v.TimeStr) Price = append(Price, v.Close) Volumn = append(Volumn, v.Volumn) } length := len(records) if kdjex.PrevTime == records[length-1].TimeStr && kdjex.PrevPrice == records[length-1].Close { return false } // K线为白,D线为黄,J线为红,K in middle k, d, j := getKDJ(records) if kdjex.PrevTime != records[length-1].TimeStr || kdjex.PrevPrice != records[length-1].Close { kdjex.PrevTime = records[length-1].TimeStr kdjex.PrevPrice = records[length-1].Close logger.Infoln(records[length-1].TimeStr, records[length-1].Close) logger.Infof("d(黄线)%0.0f\tk(白线)%0.0f\tj(红线)%0.0f\n", d[length-2], k[length-2], j[length-2]) logger.Infof("d(黄线)%0.0f\tk(白线)%0.0f\tj(红线)%0.0f\n", d[length-1], k[length-1], j[length-1]) if j[length-1] > k[length-1] { logger.Infoln("KDJ up trend") } else { logger.Infoln("KDJ down trend") } } if (j[length-2] < k[length-2] && k[length-2] < d[length-2]) && (j[length-1] > k[length-1] && k[length-1] > d[length-1]) { logger.Infoln("----------------->KDJ up cross", kdjex.PrevKDJTrade, d[length-2]) if kdjex.PrevKDJTrade != "buy" && j[length-2] <= 20 { // do buy ret, orderbook := GetOrderBook() if !ret { logger.Infoln("get orderbook failed 1") ret, orderbook = GetOrderBook() // try again if !ret { logger.Infoln("get orderbook failed 2") return false } } logger.Infoln("卖一", (orderbook.Asks[len(orderbook.Asks)-1])) logger.Infoln("买一", orderbook.Bids[0]) logger.Infoln("X 两根K线最低价", records[length-2].Low, records[length-1].Low) logger.Infoln("X 两根K线最高价", records[length-2].High, records[length-1].High) avgLow := (records[length-2].Close + records[length-1].Low) / 2.0 logger.Infoln("X 两根K线的最低平均价", avgLow) warning := "KDJ up cross, 买入buy In<----限价单" for i := 1; i <= ordercount; i++ { warning := "KDJ up cross, 买入buy In<----限价单" tradePrice := fmt.Sprintf("%f", avgLow+slappage*float64(i)) buyID := buy(tradePrice, splitTradeAmount) if buyID != "0" { warning += "[委托成功]" kdjex.BuyId = append(kdjex.BuyId, buyID) if !GetBacktest() { logger.Tradef("在%s,根据策略%s周期%s,以价格%s买入%s个%s\n", Option["tradecenter"], Option["strategy"], Option["tick_interval"], tradePrice, splitTradeAmount, coin) } else { t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%s买入%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], tradePrice, splitTradeAmount, coin) } } else { warning += "[委托失败]" } logger.Infoln(warning) } kdjex.BuyBegin = time.Now() kdjex.PrevKDJTrade = "buy" kdjex.PrevBuyPirce = avgLow logger.Infoln("------------>>>stoploss price", kdjex.PrevBuyPirce*(1-stoploss*0.01)) _, ret = GetAccount() if !ret { logger.Infoln("GetAccount failed") } SendEmail(warning) } } if (j[length-2] > k[length-2] && k[length-2] > d[length-2]) && (j[length-1] < k[length-1] && k[length-1] < d[length-1]) { logger.Infoln("<----------------------KDJ down cross", kdjex.PrevKDJTrade, d[length-2]) if kdjex.PrevKDJTrade != "sell" && j[length-2] >= 80 { // do sell ret, orderbook := GetOrderBook() if !ret { logger.Infoln("get orderbook failed 1") ret, orderbook = GetOrderBook() // try again if !ret { logger.Infoln("get orderbook failed 2") return false } } logger.Infoln("卖一", (orderbook.Asks[len(orderbook.Asks)-1])) logger.Infoln("买一", orderbook.Bids[0]) logger.Infoln("X 两根K线最低价", records[length-2].Low, records[length-1].Low) logger.Infoln("X 两根K线最高价", records[length-2].High, records[length-1].High) avgHigh := (records[length-2].Close + records[length-1].High) / 2.0 logger.Infoln("X 两根K线的最高平均价", avgHigh) warning := "KDJ down cross, 卖出Sell Out---->限价单" for i := 1; i <= ordercount; i++ { warning := "KDJ down cross, 卖出Sell Out---->限价单" tradePrice := fmt.Sprintf("%f", avgHigh-slappage*float64(i)) sellID := sell(tradePrice, splitTradeAmount) if sellID != "0" { warning += "[委托成功]" kdjex.SellId = append(kdjex.SellId, sellID) if !GetBacktest() { logger.Tradef("在%s,根据策略%s周期%s,以价格%s卖出%s个%s\n", Option["tradecenter"], Option["strategy"], Option["tick_interval"], tradePrice, splitTradeAmount, coin) } else { t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%s卖出%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], tradePrice, splitTradeAmount, coin) } } else { warning += "[委托失败]" } logger.Infoln(warning) } kdjex.SellBegin = time.Now() kdjex.PrevKDJTrade = "sell" _, ret = GetAccount() if !ret { logger.Infoln("GetAccount failed") } SendEmail(warning) } } // do sell when price is below stoploss point if Price[length-1] < kdjex.PrevBuyPirce*(1-stoploss*0.01) { if Option["disable_trading"] != "1" && kdjex.PrevKDJTrade != "sell" { kdjex.PrevKDJTrade = "sell" kdjex.PrevBuyPirce = 0 warning := "!<------------------stop loss, 卖出Sell Out---->市价" + getTradePrice("", Price[length-1]) + ",委托价" + getTradePrice("sell", Price[length-1]) logger.Infoln(warning) tradePrice := getTradePrice("sell", Price[length-1]) if sell(tradePrice, tradeAmount) != "0" { warning += "[委托成功]" if !GetBacktest() { logger.Tradef("在%s,根据策略%s周期%s,以价格%s卖出%s个%s\n", Option["tradecenter"], Option["strategy"], Option["tick_interval"], tradePrice, tradeAmount, coin) } else { t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%s卖出%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], tradePrice, tradeAmount, coin) } } else { warning += "[委托失败]" for i := 1; i <= ordercount; i++ { warning := "stop loss, 卖出Sell Out---->限价单" tradePrice := getTradePrice("sell", Price[length-1]) sellID := sell(tradePrice, splitTradeAmount) if sellID != "0" { warning += "[委托成功]" kdjex.SellId = append(kdjex.SellId, sellID) if !GetBacktest() { logger.Tradef("在%s,根据策略%s周期%s,以价格%s卖出%s个%s\n", Option["tradecenter"], Option["strategy"], Option["tick_interval"], tradePrice, splitTradeAmount, coin) } else { t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%s卖出%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], tradePrice, splitTradeAmount, coin) } } else { warning += "[委托失败]" } logger.Infoln(warning) } } kdjex.SellBegin = time.Now() kdjex.PrevKDJTrade = "sell" _, ret := GetAccount() if !ret { logger.Infoln("GetAccount failed") } SendEmail(warning) } } // check timeout trade now := time.Now() logger.Infoln("time go ", int64(now.Sub(kdjex.BuyBegin)/time.Second)) logger.Infoln("BuyId len", len(kdjex.BuyId), cap(kdjex.BuyId)) logger.Infoln("SellId len", len(kdjex.SellId), cap(kdjex.SellId)) if len(kdjex.BuyId) != 0 && int64(now.Sub(kdjex.BuyBegin)/time.Second) > timeout { // todo for _, BuyId := range kdjex.BuyId { warning := "<--------------buy order timeout, cancel-------------->" + BuyId if CancelOrder(BuyId) { warning += "[Cancel委托成功]" } else { warning += "[Cancel委托失败]" } logger.Infoln(warning) time.Sleep(1 * time.Second) time.Sleep(500 * time.Microsecond) } kdjex.BuyId = kdjex.BuyId[:0] } if len(kdjex.SellId) != 0 && int64(now.Sub(kdjex.SellBegin)/time.Second) > timeout { // todo for _, SellId := range kdjex.SellId { warning := "<--------------sell order timeout, cancel------------->" + SellId if CancelOrder(SellId) { warning += "[Cancel委托成功]" } else { warning += "[Cancel委托失败]" } logger.Infoln(warning) time.Sleep(1 * time.Second) time.Sleep(500 * time.Microsecond) } kdjex.SellId = kdjex.SellId[:0] } return true }
func Buy() string { if PrevTrade == "buy" { return "0" } // init isStoploss = false // compute the price price, nPrice, warning := getBuyPrice() // compute the amount amount := Option["tradeAmount"] nAmount, err := strconv.ParseFloat(amount, 64) if err != nil { logger.Infoln("amount is not float") return "0" } Available_cny := GetAvailable_cny() if Available_cny < nPrice*nAmount { var nMinTradeAmount float64 nAmount = Available_cny / nPrice symbol := Option["symbol"] if symbol == "btc_cny" { nMinTradeAmount = 0.1 } else { nMinTradeAmount = 0.01 } if nAmount < nMinTradeAmount { warning += "没有足够的法币余额" logger.Infoln(warning) PrevTrade = "buy" PrevBuyPirce = nPrice return "0" } amount = fmt.Sprintf("%02f", nAmount) } warning += "---->数量" + amount buyID := buy(price, amount) if buyID == "-1" { warning += " [模拟]" } else if buyID == "0" { warning += "[委托失败]" } else { warning += "[委托成功]" + buyID } logger.Infoln(warning) var coin string if Option["symbol"] == "btc_cny" { coin = "比特币" } else { coin = "莱特币" } if buyID != "0" { if !GetBacktest() { logger.Tradef("在%s,根据策略%s周期%s,以价格%s买入%s个%s\n", Option["tradecenter"], Option["strategy"], Option["tick_interval"], price, amount, coin) go email.TriggerTrender(warning) } else { t := time.Unix(GetBtTime(), 0) logger.Backtestf("%s 在simulate,根据策略%s周期%s,以价格%s买入%s个%s\n", t.Format("2006-01-02 15:04:05"), Option["strategy"], Option["tick_interval"], price, amount, coin) } } return buyID }