mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-31 23:16:54 +00:00
technical_analysis: TWAP & VWAP + TA methods to candles and link to existing RPC server for GCTCLI prototyping (#970)
* kline: add weighted price helpers for candles * twap/vwap: basic implementation and hook to rpc for protype * ta: cont implementation. (WIP) * kline: Add tests * kline: add helpers * ta: full impl. * kline: remove support for macd and add in correlation-coefficient handling * rpc: change naming convention * linter: fix * protolinter: fix * linter: ++ * kline: reinstate macd handling after adding in check * glorious: nits * gctcl: linter * Update exchanges/kline/weighted_price.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * glorious: nits * glorious: nits v2.0 * kline: fix test * huobi-tests: shift from next quarter to this weeks contracts as they were erroring out in tests. * btcmarkets: update supported kline intervals * zb: fix test * rpcserver: fix bug and tests Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gct-ta/indicators"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/file"
|
||||
@@ -36,6 +37,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
|
||||
"github.com/thrasher-corp/gocryptotrader/gctrpc"
|
||||
@@ -71,6 +73,7 @@ var (
|
||||
errNoAccountInformation = errors.New("account information does not exist")
|
||||
errShutdownNotAllowed = errors.New("shutting down this bot instance is not allowed via gRPC, please enable by command line flag --grpcshutdown or config.json field grpcAllowBotShutdown")
|
||||
errGRPCShutdownSignalIsNil = errors.New("cannot shutdown, gRPC shutdown channel is nil")
|
||||
errInvalidStrategy = errors.New("invalid strategy")
|
||||
)
|
||||
|
||||
// RPCServer struct
|
||||
@@ -108,7 +111,15 @@ func (s *RPCServer) authenticateClient(ctx context.Context) (context.Context, er
|
||||
password != s.Config.RemoteControl.Password {
|
||||
return ctx, fmt.Errorf("username/password mismatch")
|
||||
}
|
||||
return exchange.ParseCredentialsMetadata(ctx, md)
|
||||
ctx, err = exchange.ParseCredentialsMetadata(ctx, md)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
if _, ok := md["verbose"]; ok {
|
||||
ctx = request.WithVerbose(ctx)
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// StartRPCServer starts a gRPC server with TLS auth
|
||||
@@ -4575,3 +4586,166 @@ func (s *RPCServer) Shutdown(_ context.Context, _ *gctrpc.ShutdownRequest) (*gct
|
||||
s.Engine.GRPCShutdownSignal = nil
|
||||
return &gctrpc.ShutdownResponse{}, nil
|
||||
}
|
||||
|
||||
// GetTechnicalAnalysis using the requested technical analysis method will
|
||||
// return a set(s) of signals for price action analysis.
|
||||
func (s *RPCServer) GetTechnicalAnalysis(ctx context.Context, r *gctrpc.GetTechnicalAnalysisRequest) (*gctrpc.GetTechnicalAnalysisResponse, error) {
|
||||
exch, err := s.GetExchangeByName(r.Exchange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
as, err := asset.New(r.AssetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pair, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
klineInterval := kline.Interval(r.Interval)
|
||||
|
||||
err = exch.GetBase().ValidateKline(pair, as, klineInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
klines, err := exch.GetHistoricCandlesExtended(ctx,
|
||||
pair,
|
||||
as,
|
||||
r.Start.AsTime(),
|
||||
r.End.AsTime(),
|
||||
klineInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signals := make(map[string]*gctrpc.ListOfSignals)
|
||||
switch strings.ToUpper(r.AlgorithmType) {
|
||||
case "TWAP":
|
||||
var price float64
|
||||
price, err = klines.GetTWAP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["TWAP"] = &gctrpc.ListOfSignals{Signals: []float64{price}}
|
||||
case "VWAP":
|
||||
var prices []float64
|
||||
prices, err = klines.GetVWAPs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["VWAP"] = &gctrpc.ListOfSignals{Signals: prices}
|
||||
case "ATR":
|
||||
var prices []float64
|
||||
prices, err = klines.GetAverageTrueRange(r.Period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["ATR"] = &gctrpc.ListOfSignals{Signals: prices}
|
||||
case "BBANDS":
|
||||
var bollinger *kline.Bollinger
|
||||
bollinger, err = klines.GetBollingerBands(r.Period,
|
||||
r.StandardDeviationUp,
|
||||
r.StandardDeviationDown,
|
||||
indicators.MaType(r.MovingAverageType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["UPPER"] = &gctrpc.ListOfSignals{Signals: bollinger.Upper}
|
||||
signals["MIDDLE"] = &gctrpc.ListOfSignals{Signals: bollinger.Middle}
|
||||
signals["LOWER"] = &gctrpc.ListOfSignals{Signals: bollinger.Lower}
|
||||
case "COCO":
|
||||
otherExch := exch
|
||||
if r.OtherExchange != "" {
|
||||
otherExch, err = s.GetExchangeByName(r.OtherExchange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
otherAs := as
|
||||
if r.OtherAssetType != "" {
|
||||
otherAs, err = asset.New(r.OtherAssetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if r.OtherPair.String() == "" {
|
||||
return nil, errors.New("other pair is empty, to compare this must be specified")
|
||||
}
|
||||
|
||||
var otherPair currency.Pair
|
||||
otherPair, err = currency.NewPairFromStrings(r.OtherPair.Base, r.OtherPair.Quote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var otherKlines kline.Item
|
||||
otherKlines, err = otherExch.GetHistoricCandlesExtended(ctx,
|
||||
otherPair, otherAs, r.Start.AsTime(), r.End.AsTime(), klineInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var correlation []float64
|
||||
correlation, err = klines.GetCorrelationCoefficient(&otherKlines, r.Period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["COCO"] = &gctrpc.ListOfSignals{Signals: correlation}
|
||||
case "SMA":
|
||||
var prices []float64
|
||||
prices, err = klines.GetSimpleMovingAverageOnClose(r.Period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["SMA"] = &gctrpc.ListOfSignals{Signals: prices}
|
||||
case "EMA":
|
||||
var prices []float64
|
||||
prices, err = klines.GetExponentialMovingAverageOnClose(r.Period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["EMA"] = &gctrpc.ListOfSignals{Signals: prices}
|
||||
case "MACD":
|
||||
var macd *kline.MACD
|
||||
macd, err = klines.GetMovingAverageConvergenceDivergenceOnClose(r.FastPeriod,
|
||||
r.SlowPeriod,
|
||||
r.Period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["MACD"] = &gctrpc.ListOfSignals{Signals: macd.Results}
|
||||
signals["SIGNAL"] = &gctrpc.ListOfSignals{Signals: macd.SignalVals}
|
||||
signals["HISTOGRAM"] = &gctrpc.ListOfSignals{Signals: macd.Histogram}
|
||||
case "MFI":
|
||||
var prices []float64
|
||||
prices, err = klines.GetMoneyFlowIndex(r.Period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["MFI"] = &gctrpc.ListOfSignals{Signals: prices}
|
||||
case "OBV":
|
||||
var prices []float64
|
||||
prices, err = klines.GetOnBalanceVolume()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["OBV"] = &gctrpc.ListOfSignals{Signals: prices}
|
||||
case "RSI":
|
||||
var prices []float64
|
||||
prices, err = klines.GetRelativeStrengthIndexOnClose(r.Period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signals["RSI"] = &gctrpc.ListOfSignals{Signals: prices}
|
||||
default:
|
||||
return nil, fmt.Errorf("%w '%s'", errInvalidStrategy, r.AlgorithmType)
|
||||
}
|
||||
|
||||
return &gctrpc.GetTechnicalAnalysisResponse{Signals: signals}, nil
|
||||
}
|
||||
|
||||
@@ -72,22 +72,29 @@ func (f fExchange) GetHistoricCandles(ctx context.Context, p currency.Pair, a as
|
||||
}, nil
|
||||
}
|
||||
|
||||
func generateCandles(amount int, timeStart time.Time, interval kline.Interval) []kline.Candle {
|
||||
candy := make([]kline.Candle, amount)
|
||||
for x := 0; x < amount; x++ {
|
||||
candy[x] = kline.Candle{
|
||||
Time: timeStart,
|
||||
Open: 1337,
|
||||
High: 1337,
|
||||
Low: 1337,
|
||||
Close: 1337,
|
||||
Volume: 1337,
|
||||
}
|
||||
timeStart = timeStart.Add(interval.Duration())
|
||||
}
|
||||
return candy
|
||||
}
|
||||
|
||||
func (f fExchange) GetHistoricCandlesExtended(ctx context.Context, p currency.Pair, a asset.Item, timeStart, _ time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
return kline.Item{
|
||||
Exchange: fakeExchangeName,
|
||||
Pair: p,
|
||||
Asset: a,
|
||||
Interval: interval,
|
||||
Candles: []kline.Candle{
|
||||
{
|
||||
Time: timeStart,
|
||||
Open: 1337,
|
||||
High: 1337,
|
||||
Low: 1337,
|
||||
Close: 1337,
|
||||
Volume: 1337,
|
||||
},
|
||||
},
|
||||
Candles: generateCandles(33, timeStart, interval),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2340,3 +2347,275 @@ func TestShutdown(t *testing.T) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTechnicalAnalysis(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
em := SetupExchangeManager()
|
||||
exch, err := em.NewExchangeByName(testExchange)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := exch.GetBase()
|
||||
b.Name = fakeExchangeName
|
||||
b.Enabled = true
|
||||
|
||||
cp, err := currency.NewPairFromString("btc-usd")
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received '%v', expected '%v'", err, nil)
|
||||
}
|
||||
|
||||
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
|
||||
b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
ConfigFormat: ¤cy.PairFormat{},
|
||||
Available: currency.Pairs{cp},
|
||||
Enabled: currency.Pairs{cp},
|
||||
}
|
||||
b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
ConfigFormat: ¤cy.PairFormat{},
|
||||
Available: currency.Pairs{cp},
|
||||
Enabled: currency.Pairs{cp},
|
||||
}
|
||||
|
||||
b.Features.Enabled.Kline.Intervals = map[string]bool{
|
||||
kline.OneDay.Word(): true,
|
||||
}
|
||||
em.Add(fExchange{IBotExchange: exch})
|
||||
s := RPCServer{
|
||||
Engine: &Engine{
|
||||
ExchangeManager: em,
|
||||
currencyStateManager: &CurrencyStateManager{
|
||||
started: 1,
|
||||
iExchangeManager: em,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{})
|
||||
if !errors.Is(err, ErrExchangeNameIsEmpty) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrExchangeNameIsEmpty)
|
||||
}
|
||||
|
||||
_, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
})
|
||||
if !errors.Is(err, asset.ErrNotSupported) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
|
||||
}
|
||||
|
||||
_, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "upsideprofitcontract",
|
||||
Pair: &gctrpc.CurrencyPair{},
|
||||
})
|
||||
if !errors.Is(err, kline.ErrValidatingParams) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, kline.ErrValidatingParams)
|
||||
}
|
||||
|
||||
_, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
})
|
||||
if !errors.Is(err, errInvalidStrategy) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidStrategy)
|
||||
}
|
||||
|
||||
resp, err := s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "twap",
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if resp.Signals["TWAP"].Signals[0] != 1337 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", resp.Signals["TWAP"].Signals[0], 1337)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "vwap",
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["VWAP"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["VWAP"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "atr",
|
||||
Period: 9,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["ATR"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["ATR"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "bbands",
|
||||
Period: 9,
|
||||
StandardDeviationUp: 0.5,
|
||||
StandardDeviationDown: 0.5,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["UPPER"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["UPPER"].Signals), 33)
|
||||
}
|
||||
|
||||
if len(resp.Signals["MIDDLE"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["MIDDLE"].Signals), 33)
|
||||
}
|
||||
|
||||
if len(resp.Signals["LOWER"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["LOWER"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
OtherPair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "COCO",
|
||||
Period: 9,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["COCO"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["COCO"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "sma",
|
||||
Period: 9,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["SMA"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["SMA"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "ema",
|
||||
Period: 9,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["EMA"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["EMA"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "macd",
|
||||
Period: 9,
|
||||
FastPeriod: 12,
|
||||
SlowPeriod: 26,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["MACD"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["MACD"].Signals), 33)
|
||||
}
|
||||
|
||||
if len(resp.Signals["SIGNAL"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["SIGNAL"].Signals), 33)
|
||||
}
|
||||
|
||||
if len(resp.Signals["HISTOGRAM"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["HISTOGRAM"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "mfi",
|
||||
Period: 9,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["MFI"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["MFI"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "obv",
|
||||
Period: 9,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["OBV"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["OBV"].Signals), 33)
|
||||
}
|
||||
|
||||
resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{
|
||||
Exchange: fakeExchangeName,
|
||||
AssetType: "spot",
|
||||
Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"},
|
||||
Interval: int64(kline.OneDay),
|
||||
AlgorithmType: "rsi",
|
||||
Period: 9,
|
||||
})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if len(resp.Signals["RSI"].Signals) != 33 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["RSI"].Signals), 33)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user