mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* deleting the unwanted file created during testing + adding more verbose errors for cli * wip * checking params throughout wip * improving errors * wip * thrasher patch * better err name * whip * testing and fixing errors WIP * upgrades and better errors * broken test * wip * adding some tests * using tempDir * mini improvement * little changes * better time check * fixing error * more glorious changes * end of day wip * shazzy changes * checking error * appveyor * last changes:
1103 lines
32 KiB
Go
1103 lines
32 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid"
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/config"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/database"
|
|
"github.com/thrasher-corp/gocryptotrader/database/drivers"
|
|
"github.com/thrasher-corp/gocryptotrader/database/repository"
|
|
dbexchange "github.com/thrasher-corp/gocryptotrader/database/repository/exchange"
|
|
sqltrade "github.com/thrasher-corp/gocryptotrader/database/repository/trade"
|
|
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/binance"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
|
|
"github.com/thrasher-corp/gocryptotrader/gctrpc"
|
|
"github.com/thrasher-corp/goose"
|
|
)
|
|
|
|
const (
|
|
unexpectedLackOfError = "unexpected lack of error"
|
|
migrationsFolder = "migrations"
|
|
databaseFolder = "database"
|
|
databaseName = "rpctestdb"
|
|
)
|
|
|
|
// Sets up everything required to run any function inside rpcserver
|
|
func RPCTestSetup(t *testing.T) *Engine {
|
|
database.DB.Mu.Lock()
|
|
var err error
|
|
dbConf := database.Config{
|
|
Enabled: true,
|
|
Driver: database.DBSQLite3,
|
|
ConnectionDetails: drivers.ConnectionDetails{
|
|
Database: databaseName,
|
|
},
|
|
}
|
|
engerino := new(Engine)
|
|
engerino.Config = &config.Config{}
|
|
err = engerino.Config.LoadConfig(config.TestFile, true)
|
|
if err != nil {
|
|
t.Fatalf("SetupTest: Failed to load config: %s", err)
|
|
}
|
|
|
|
if engerino.GetExchangeByName(testExchange) == nil {
|
|
err = engerino.LoadExchange(testExchange, false, nil)
|
|
if err != nil {
|
|
t.Fatalf("SetupTest: Failed to load exchange: %s", err)
|
|
}
|
|
}
|
|
engerino.Config.Database = dbConf
|
|
err = engerino.DatabaseManager.Start(engerino)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
path := filepath.Join("..", databaseFolder, migrationsFolder)
|
|
err = goose.Run("up", dbConn.SQL, repository.GetSQLDialect(), path, "")
|
|
if err != nil {
|
|
t.Fatalf("failed to run migrations %v", err)
|
|
}
|
|
uuider, _ := uuid.NewV4()
|
|
err = dbexchange.Insert(dbexchange.Details{Name: testExchange, UUID: uuider})
|
|
if err != nil {
|
|
t.Fatalf("failed to insert exchange %v", err)
|
|
}
|
|
database.DB.Mu.Unlock()
|
|
|
|
return engerino
|
|
}
|
|
|
|
func CleanRPCTest(t *testing.T, engerino *Engine) {
|
|
database.DB.Mu.Lock()
|
|
defer database.DB.Mu.Unlock()
|
|
err := engerino.DatabaseManager.Stop()
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
err = os.Remove(filepath.Join(common.GetDefaultDataDir(runtime.GOOS), databaseFolder, databaseName))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestGetSavedTrades(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
_, err := s.GetSavedTrades(context.Background(), &gctrpc.GetSavedTradesRequest{})
|
|
if err == nil {
|
|
t.Fatal(unexpectedLackOfError)
|
|
}
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Error(err)
|
|
}
|
|
_, err = s.GetSavedTrades(context.Background(), &gctrpc.GetSavedTradesRequest{
|
|
Exchange: "fake",
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
})
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Error(err)
|
|
}
|
|
_, err = s.GetSavedTrades(context.Background(), &gctrpc.GetSavedTradesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
})
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if err.Error() != "request for Bitstamp spot trade data between 2019-11-30 00:00:00 and 2020-01-01 01:01:01 and returned no results" {
|
|
t.Error(err)
|
|
}
|
|
err = sqltrade.Insert(sqltrade.Data{
|
|
Timestamp: time.Date(2020, 0, 0, 0, 0, 1, 0, time.UTC),
|
|
Exchange: testExchange,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
AssetType: asset.Spot.String(),
|
|
Price: 1337,
|
|
Amount: 1337,
|
|
Side: order.Buy.String(),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
_, err = s.GetSavedTrades(context.Background(), &gctrpc.GetSavedTradesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestConvertTradesToCandles(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
// bad param test
|
|
_, err := s.ConvertTradesToCandles(context.Background(), &gctrpc.ConvertTradesToCandlesRequest{})
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Error(err)
|
|
}
|
|
|
|
// bad exchange test
|
|
_, err = s.ConvertTradesToCandles(context.Background(), &gctrpc.ConvertTradesToCandlesRequest{
|
|
Exchange: "faker",
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
})
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Error(err)
|
|
}
|
|
|
|
// no trades test
|
|
_, err = s.ConvertTradesToCandles(context.Background(), &gctrpc.ConvertTradesToCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 2, 2, 2, 2, 2, 2, time.UTC).Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
})
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if err.Error() != "no trades returned from supplied params" {
|
|
t.Error(err)
|
|
}
|
|
|
|
// add a trade
|
|
err = sqltrade.Insert(sqltrade.Data{
|
|
Timestamp: time.Date(2020, 1, 1, 1, 1, 2, 1, time.UTC),
|
|
Exchange: testExchange,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
AssetType: asset.Spot.String(),
|
|
Price: 1337,
|
|
Amount: 1337,
|
|
Side: order.Buy.String(),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
// get candle from one trade
|
|
var candles *gctrpc.GetHistoricCandlesResponse
|
|
candles, err = s.ConvertTradesToCandles(context.Background(), &gctrpc.ConvertTradesToCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 2, 2, 2, 2, 2, 2, time.UTC).Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(candles.Candle) == 0 {
|
|
t.Error("no candles returned")
|
|
}
|
|
|
|
// save generated candle to database
|
|
_, err = s.ConvertTradesToCandles(context.Background(), &gctrpc.ConvertTradesToCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 2, 2, 2, 2, 2, 2, time.UTC).Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
Sync: true,
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// forcefully remove previous candle and insert a new one
|
|
_, err = s.ConvertTradesToCandles(context.Background(), &gctrpc.ConvertTradesToCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 2, 2, 2, 2, 2, 2, time.UTC).Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
Sync: true,
|
|
Force: true,
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// load the saved candle to verify that it was overwritten
|
|
candles, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 2, 2, 2, 2, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
UseDb: true,
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(candles.Candle) != 1 {
|
|
t.Error("expected only one candle")
|
|
}
|
|
}
|
|
|
|
func TestGetHistoricCandles(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
// error checks
|
|
defaultStart := time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC)
|
|
defaultEnd := time.Date(2020, 1, 2, 2, 2, 2, 2, time.UTC)
|
|
cp := currency.NewPair(currency.BTC, currency.USD)
|
|
_, err := s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: "",
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
AssetType: asset.Spot.String(),
|
|
})
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Errorf("expected %v, received %v", errExchangeNotLoaded, err)
|
|
}
|
|
|
|
_, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: testExchange,
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
Pair: nil,
|
|
AssetType: asset.Spot.String(),
|
|
})
|
|
if !errors.Is(err, errCurrencyPairUnset) {
|
|
t.Errorf("expected %v, received %v", errCurrencyPairUnset, err)
|
|
}
|
|
_, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
Start: "2020-01-02 15:04:05",
|
|
End: "2020-01-02 15:04:05",
|
|
})
|
|
if !errors.Is(err, errInvalidTimes) {
|
|
t.Errorf("expected %v, received %v", errInvalidTimes, err)
|
|
}
|
|
var results *gctrpc.GetHistoricCandlesResponse
|
|
// default run
|
|
results, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
AssetType: asset.Spot.String(),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(results.Candle) == 0 {
|
|
t.Error("expected results")
|
|
}
|
|
|
|
// sync run
|
|
results, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
Sync: true,
|
|
ExRequest: true,
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(results.Candle) == 0 {
|
|
t.Error("expected results")
|
|
}
|
|
|
|
// db run
|
|
results, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
UseDb: true,
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(results.Candle) == 0 {
|
|
t.Error("expected results")
|
|
}
|
|
err = trade.SaveTradesToDatabase(trade.Data{
|
|
TID: "test123",
|
|
Exchange: testExchange,
|
|
CurrencyPair: cp,
|
|
AssetType: asset.Spot,
|
|
Price: 1337,
|
|
Amount: 1337,
|
|
Side: order.Buy,
|
|
Timestamp: time.Date(2020, 1, 2, 3, 1, 1, 7, time.UTC),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
// db run including trades
|
|
results, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 2, 4, 2, 2, 2, time.UTC).Format(common.SimpleTimeFormat),
|
|
TimeInterval: int64(kline.OneHour.Duration()),
|
|
UseDb: true,
|
|
FillMissingWithTrades: true,
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if results.Candle[len(results.Candle)-1].Close != 1337 {
|
|
t.Error("expected fancy new candle based off fancy new trade data")
|
|
}
|
|
}
|
|
|
|
func TestFindMissingSavedTradeIntervals(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
// bad request checks
|
|
_, err := s.FindMissingSavedTradeIntervals(context.Background(), &gctrpc.FindMissingTradePeriodsRequest{})
|
|
if err == nil {
|
|
t.Error("expected error")
|
|
return
|
|
}
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
cp := currency.NewPair(currency.BTC, currency.USD)
|
|
// no data found response
|
|
defaultStart := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).UTC()
|
|
defaultEnd := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC).UTC()
|
|
var resp *gctrpc.FindMissingIntervalsResponse
|
|
resp, err = s.FindMissingSavedTradeIntervals(context.Background(), &gctrpc.FindMissingTradePeriodsRequest{
|
|
ExchangeName: testExchange,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Start: defaultStart.UTC().Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.UTC().Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if resp.Status == "" {
|
|
t.Errorf("expected a status message")
|
|
}
|
|
// one trade response
|
|
err = trade.SaveTradesToDatabase(trade.Data{
|
|
TID: "test1234",
|
|
Exchange: testExchange,
|
|
CurrencyPair: cp,
|
|
AssetType: asset.Spot,
|
|
Price: 1337,
|
|
Amount: 1337,
|
|
Side: order.Buy,
|
|
Timestamp: time.Date(2020, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
resp, err = s.FindMissingSavedTradeIntervals(context.Background(), &gctrpc.FindMissingTradePeriodsRequest{
|
|
ExchangeName: testExchange,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Start: defaultStart.In(time.UTC).Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.In(time.UTC).Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(resp.MissingPeriods) != 2 {
|
|
t.Errorf("expected 2 missing period, received: %v", len(resp.MissingPeriods))
|
|
}
|
|
|
|
// two trades response
|
|
err = trade.SaveTradesToDatabase(trade.Data{
|
|
TID: "test123",
|
|
Exchange: testExchange,
|
|
CurrencyPair: cp,
|
|
AssetType: asset.Spot,
|
|
Price: 1337,
|
|
Amount: 1337,
|
|
Side: order.Buy,
|
|
Timestamp: time.Date(2020, 1, 1, 13, 0, 0, 0, time.UTC),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
resp, err = s.FindMissingSavedTradeIntervals(context.Background(), &gctrpc.FindMissingTradePeriodsRequest{
|
|
ExchangeName: testExchange,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Start: defaultStart.In(time.UTC).Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.In(time.UTC).Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(resp.MissingPeriods) != 2 {
|
|
t.Errorf("expected 2 missing periods, received: %v", len(resp.MissingPeriods))
|
|
}
|
|
}
|
|
|
|
func TestFindMissingSavedCandleIntervals(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
// bad request checks
|
|
_, err := s.FindMissingSavedCandleIntervals(context.Background(), &gctrpc.FindMissingCandlePeriodsRequest{})
|
|
if err == nil {
|
|
t.Error("expected error")
|
|
return
|
|
}
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
cp := currency.NewPair(currency.BTC, currency.USD)
|
|
// no data found response
|
|
defaultStart := time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC)
|
|
defaultEnd := time.Date(2020, 1, 2, 2, 2, 2, 2, time.UTC)
|
|
var resp *gctrpc.FindMissingIntervalsResponse
|
|
_, err = s.FindMissingSavedCandleIntervals(context.Background(), &gctrpc.FindMissingCandlePeriodsRequest{
|
|
ExchangeName: testExchange,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Interval: int64(time.Hour),
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil && err.Error() != "no candle data found: Bitstamp BTC USD 3600 spot" {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
// one candle missing periods response
|
|
_, err = kline.StoreInDatabase(&kline.Item{
|
|
Exchange: testExchange,
|
|
Pair: cp,
|
|
Asset: asset.Spot,
|
|
Interval: kline.OneHour,
|
|
Candles: []kline.Candle{
|
|
{
|
|
Time: time.Date(2020, 1, 1, 2, 1, 1, 1, time.UTC),
|
|
Open: 1337,
|
|
High: 1337,
|
|
Low: 1337,
|
|
Close: 1337,
|
|
Volume: 1337,
|
|
},
|
|
},
|
|
}, false)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
_, err = s.FindMissingSavedCandleIntervals(context.Background(), &gctrpc.FindMissingCandlePeriodsRequest{
|
|
ExchangeName: testExchange,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Interval: int64(time.Hour),
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// two candle missing periods response
|
|
_, err = kline.StoreInDatabase(&kline.Item{
|
|
Exchange: testExchange,
|
|
Pair: cp,
|
|
Asset: asset.Spot,
|
|
Interval: kline.OneHour,
|
|
Candles: []kline.Candle{
|
|
{
|
|
Time: time.Date(2020, 1, 1, 3, 1, 1, 1, time.UTC),
|
|
Open: 1337,
|
|
High: 1337,
|
|
Low: 1337,
|
|
Close: 1337,
|
|
Volume: 1337,
|
|
},
|
|
},
|
|
}, false)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
resp, err = s.FindMissingSavedCandleIntervals(context.Background(), &gctrpc.FindMissingCandlePeriodsRequest{
|
|
ExchangeName: testExchange,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Base: cp.Base.String(),
|
|
Quote: cp.Quote.String(),
|
|
},
|
|
Interval: int64(time.Hour),
|
|
Start: defaultStart.Format(common.SimpleTimeFormat),
|
|
End: defaultEnd.Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(resp.MissingPeriods) != 2 {
|
|
t.Errorf("expected 2 missing periods, received: %v", len(resp.MissingPeriods))
|
|
}
|
|
}
|
|
|
|
func TestSetExchangeTradeProcessing(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
_, err := s.SetExchangeTradeProcessing(context.Background(), &gctrpc.SetExchangeTradeProcessingRequest{Exchange: testExchange, Status: true})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
exch := s.GetExchangeByName(testExchange)
|
|
base := exch.GetBase()
|
|
if !base.IsSaveTradeDataEnabled() {
|
|
t.Error("expected true")
|
|
}
|
|
|
|
_, err = s.SetExchangeTradeProcessing(context.Background(), &gctrpc.SetExchangeTradeProcessingRequest{Exchange: testExchange, Status: false})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
exch = s.GetExchangeByName(testExchange)
|
|
base = exch.GetBase()
|
|
if base.IsSaveTradeDataEnabled() {
|
|
t.Error("expected false")
|
|
}
|
|
}
|
|
|
|
func TestGetRecentTrades(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
_, err := s.GetRecentTrades(context.Background(), &gctrpc.GetSavedTradesRequest{})
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Error(err)
|
|
}
|
|
_, err = s.GetRecentTrades(context.Background(), &gctrpc.GetSavedTradesRequest{
|
|
Exchange: "fake",
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
})
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Error(err)
|
|
}
|
|
_, err = s.GetRecentTrades(context.Background(), &gctrpc.GetSavedTradesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestGetHistoricTrades(t *testing.T) {
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
err := s.GetHistoricTrades(&gctrpc.GetSavedTradesRequest{}, nil)
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Error(err)
|
|
}
|
|
err = s.GetHistoricTrades(&gctrpc.GetSavedTradesRequest{
|
|
Exchange: "fake",
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
}, nil)
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Error(err)
|
|
}
|
|
err = s.GetHistoricTrades(&gctrpc.GetSavedTradesRequest{
|
|
Exchange: testExchange,
|
|
Pair: &gctrpc.CurrencyPair{
|
|
Delimiter: currency.DashDelimiter,
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USD.String(),
|
|
},
|
|
AssetType: asset.Spot.String(),
|
|
Start: time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC).Format(common.SimpleTimeFormat),
|
|
End: time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC).Format(common.SimpleTimeFormat),
|
|
}, nil)
|
|
if err == nil {
|
|
t.Error(unexpectedLackOfError)
|
|
return
|
|
}
|
|
if err != common.ErrFunctionNotSupported {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestGetAccountInfo(t *testing.T) {
|
|
bot := CreateTestBot(t)
|
|
s := RPCServer{Engine: bot}
|
|
|
|
r, err := s.GetAccountInfo(context.Background(), &gctrpc.GetAccountInfoRequest{Exchange: fakePassExchange, AssetType: asset.Spot.String()})
|
|
if err != nil {
|
|
t.Fatalf("TestGetAccountInfo: Failed to get account info: %s", err)
|
|
}
|
|
if r.Accounts[0].Currencies[0].TotalValue != 10 {
|
|
t.Fatal("TestGetAccountInfo: Unexpected value of the 'TotalValue'")
|
|
}
|
|
}
|
|
|
|
func TestUpdateAccountInfo(t *testing.T) {
|
|
bot := CreateTestBot(t)
|
|
s := RPCServer{Engine: bot}
|
|
|
|
getResponse, err := s.GetAccountInfo(context.Background(), &gctrpc.GetAccountInfoRequest{Exchange: fakePassExchange, AssetType: asset.Spot.String()})
|
|
if err != nil {
|
|
t.Fatalf("TestGetAccountInfo: Failed to get account info: %s", err)
|
|
}
|
|
|
|
_, err = s.UpdateAccountInfo(context.Background(), &gctrpc.GetAccountInfoRequest{Exchange: fakePassExchange, AssetType: asset.Futures.String()})
|
|
if !errors.Is(err, errAssetTypeDisabled) {
|
|
t.Errorf("expected %v, received %v", errAssetTypeDisabled, err)
|
|
}
|
|
|
|
updateResp, err := s.UpdateAccountInfo(context.Background(), &gctrpc.GetAccountInfoRequest{
|
|
Exchange: fakePassExchange,
|
|
AssetType: asset.Spot.String(),
|
|
})
|
|
if !errors.Is(err, nil) {
|
|
t.Error(err)
|
|
} else if getResponse.Accounts[0].Currencies[0].TotalValue == updateResp.Accounts[0].Currencies[0].TotalValue {
|
|
t.Fatalf("TestGetAccountInfo: Unexpected value of the 'TotalValue'")
|
|
}
|
|
}
|
|
|
|
func TestGetOrders(t *testing.T) {
|
|
exchName := "binance"
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
|
|
p := &gctrpc.CurrencyPair{
|
|
Delimiter: "-",
|
|
Base: currency.BTC.String(),
|
|
Quote: currency.USDT.String(),
|
|
}
|
|
|
|
_, err := s.GetOrders(context.Background(), nil)
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Errorf("expected %v, received %v", errInvalidArguments, err)
|
|
}
|
|
|
|
_, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{
|
|
AssetType: asset.Spot.String(),
|
|
Pair: p,
|
|
})
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Errorf("expected %v, received %v", errExchangeNotLoaded, err)
|
|
}
|
|
|
|
err = engerino.LoadExchange(exchName, false, nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
_, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{
|
|
Exchange: exchName,
|
|
AssetType: asset.Spot.String(),
|
|
})
|
|
if !errors.Is(err, errCurrencyPairUnset) {
|
|
t.Errorf("expected %v, received %v", errCurrencyPairUnset, err)
|
|
}
|
|
|
|
_, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{
|
|
Exchange: exchName,
|
|
Pair: p,
|
|
})
|
|
if !errors.Is(err, asset.ErrNotSupported) {
|
|
t.Errorf("expected %v, received %v", asset.ErrNotSupported, err)
|
|
}
|
|
|
|
_, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{
|
|
Exchange: exchName,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: p,
|
|
StartDate: time.Now().Format(common.SimpleTimeFormat),
|
|
EndDate: time.Now().Add(-time.Hour).Format(common.SimpleTimeFormat),
|
|
})
|
|
if !errors.Is(err, errInvalidTimes) {
|
|
t.Errorf("expected %v, received %v", errInvalidTimes, err)
|
|
}
|
|
|
|
_, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{
|
|
Exchange: exchName,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: p,
|
|
StartDate: time.Now().Format(common.SimpleTimeFormat),
|
|
EndDate: time.Now().Add(time.Hour).Format(common.SimpleTimeFormat),
|
|
})
|
|
if err != nil && !strings.Contains(err.Error(), "not supported due to unset/default API keys") {
|
|
t.Error(err)
|
|
}
|
|
if err == nil {
|
|
t.Error("expected error")
|
|
}
|
|
|
|
exch := engerino.GetExchangeByName(exchName)
|
|
if exch == nil {
|
|
t.Fatal("expected an exchange")
|
|
}
|
|
b := exch.GetBase()
|
|
b.API.Credentials.Key = "test"
|
|
b.API.Credentials.Secret = "test"
|
|
b.API.AuthenticatedSupport = true
|
|
|
|
_, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{
|
|
Exchange: exchName,
|
|
AssetType: asset.Spot.String(),
|
|
Pair: p,
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error")
|
|
}
|
|
}
|
|
|
|
func TestGetOrder(t *testing.T) {
|
|
exchName := "binance"
|
|
engerino := RPCTestSetup(t)
|
|
defer CleanRPCTest(t, engerino)
|
|
s := RPCServer{Engine: engerino}
|
|
|
|
p := &gctrpc.CurrencyPair{
|
|
Delimiter: "-",
|
|
Base: "BTC",
|
|
Quote: "USDT",
|
|
}
|
|
|
|
_, err := s.GetOrder(context.Background(), nil)
|
|
if !errors.Is(err, errInvalidArguments) {
|
|
t.Errorf("expected %v, received %v", errInvalidArguments, err)
|
|
}
|
|
|
|
_, err = s.GetOrder(context.Background(), &gctrpc.GetOrderRequest{
|
|
Exchange: exchName,
|
|
OrderId: "",
|
|
Pair: p,
|
|
Asset: "spot",
|
|
})
|
|
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Errorf("expected %v, received %v", errExchangeNotLoaded, err)
|
|
}
|
|
|
|
err = engerino.LoadExchange(exchName, false, nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
_, err = s.GetOrder(context.Background(), &gctrpc.GetOrderRequest{
|
|
Exchange: exchName,
|
|
OrderId: "",
|
|
Pair: nil,
|
|
Asset: "",
|
|
})
|
|
if !errors.Is(err, errCurrencyPairUnset) {
|
|
t.Errorf("expected %v, received %v", errCurrencyPairUnset, err)
|
|
}
|
|
|
|
_, err = s.GetOrder(context.Background(), &gctrpc.GetOrderRequest{
|
|
Exchange: exchName,
|
|
OrderId: "",
|
|
Pair: p,
|
|
Asset: "",
|
|
})
|
|
if !errors.Is(err, asset.ErrNotSupported) {
|
|
t.Errorf("expected %v, received %v", asset.ErrNotSupported, err)
|
|
}
|
|
|
|
_, err = s.GetOrder(context.Background(), &gctrpc.GetOrderRequest{
|
|
Exchange: exchName,
|
|
OrderId: "",
|
|
Pair: p,
|
|
Asset: asset.Spot.String(),
|
|
})
|
|
if !errors.Is(err, errOrderIDCannotBeEmpty) {
|
|
t.Errorf("expected %v, received %v", errOrderIDCannotBeEmpty, err)
|
|
}
|
|
err = engerino.OrderManager.Start(engerino)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = s.GetOrder(context.Background(), &gctrpc.GetOrderRequest{
|
|
Exchange: exchName,
|
|
OrderId: "1234",
|
|
Pair: p,
|
|
Asset: asset.Spot.String(),
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error")
|
|
}
|
|
}
|
|
|
|
func TestCheckVars(t *testing.T) {
|
|
var e exchange.IBotExchange
|
|
|
|
err := checkParams("Binance", e, asset.Spot, currency.NewPair(currency.BTC, currency.USDT))
|
|
if !errors.Is(err, errExchangeNotLoaded) {
|
|
t.Errorf("expected %v, got %v", errExchangeNotLoaded, err)
|
|
}
|
|
|
|
e = &binance.Binance{}
|
|
_, ok := e.(*binance.Binance)
|
|
if !ok {
|
|
t.Fatal("invalid ibotexchange interface")
|
|
}
|
|
|
|
err = checkParams("Binance", e, asset.Spot, currency.NewPair(currency.BTC, currency.USDT))
|
|
if !errors.Is(err, errExchangeDisabled) {
|
|
t.Errorf("expected %v, got %v", errExchangeDisabled, err)
|
|
}
|
|
|
|
e.SetEnabled(true)
|
|
|
|
err = checkParams("Binance", e, asset.Spot, currency.NewPair(currency.BTC, currency.USDT))
|
|
if !errors.Is(err, errAssetTypeDisabled) {
|
|
t.Errorf("expected %v, got %v", errAssetTypeDisabled, err)
|
|
}
|
|
|
|
fmt1 := currency.PairStore{
|
|
RequestFormat: ¤cy.PairFormat{Uppercase: true},
|
|
ConfigFormat: ¤cy.PairFormat{
|
|
Delimiter: currency.DashDelimiter,
|
|
Uppercase: true,
|
|
},
|
|
}
|
|
coinFutures := currency.PairStore{
|
|
RequestFormat: ¤cy.PairFormat{
|
|
Uppercase: true,
|
|
Delimiter: currency.UnderscoreDelimiter,
|
|
},
|
|
ConfigFormat: ¤cy.PairFormat{
|
|
Uppercase: true,
|
|
Delimiter: currency.UnderscoreDelimiter,
|
|
},
|
|
}
|
|
usdtFutures := currency.PairStore{
|
|
RequestFormat: ¤cy.PairFormat{
|
|
Uppercase: true,
|
|
},
|
|
ConfigFormat: ¤cy.PairFormat{
|
|
Uppercase: true,
|
|
},
|
|
}
|
|
err = e.GetBase().StoreAssetPairFormat(asset.Spot, fmt1)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
err = e.GetBase().StoreAssetPairFormat(asset.Margin, fmt1)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
err = e.GetBase().StoreAssetPairFormat(asset.CoinMarginedFutures, coinFutures)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
err = e.GetBase().StoreAssetPairFormat(asset.USDTMarginedFutures, usdtFutures)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
err = checkParams("Binance", e, asset.Spot, currency.NewPair(currency.BTC, currency.USDT))
|
|
if !errors.Is(err, errCurrencyPairInvalid) {
|
|
t.Errorf("expected %v, got %v", errCurrencyPairInvalid, err)
|
|
}
|
|
|
|
var data = []currency.Pair{
|
|
{Delimiter: currency.DashDelimiter, Base: currency.BTC, Quote: currency.USDT},
|
|
}
|
|
|
|
e.GetBase().CurrencyPairs.StorePairs(asset.Spot, data, false)
|
|
|
|
err = checkParams("Binance", e, asset.Spot, currency.NewPair(currency.BTC, currency.USDT))
|
|
if !errors.Is(err, errCurrencyNotEnabled) {
|
|
t.Errorf("expected %v, got %v", errCurrencyNotEnabled, err)
|
|
}
|
|
|
|
e.GetBase().CurrencyPairs.EnablePair(
|
|
asset.Spot,
|
|
currency.Pair{Delimiter: currency.DashDelimiter, Base: currency.BTC, Quote: currency.USDT},
|
|
)
|
|
|
|
err = checkParams("Binance", e, asset.Spot, currency.NewPair(currency.BTC, currency.USDT))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|