mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-01 23:16:51 +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:
@@ -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