Add OHLC retrieval func (GetHistoricCandles) to all exchanges and expose it as a wrapper func (#414)

* initial wiring to providegethistoricalcandles

* initial wiring to providegethistoricalcandles

* initial wiring to providegethistoricalcandles

* gethistriccandles work from cli using hard coded inputs

* gethistoriccandles RPC service and CLI working fine for coinbasepro

* fixed unit test

* input check on grpc for gethistoriccandles

* updated deps

* fixed the return value when a method is not yet implemented

* code review: fixed CLI input check and int32->int64

* code review: handling wrong exchange name

* added check on granularity and allowing start and end being empty

* code review: removed currency2

* code review: dependency reverted

* improved func comment

* typo in func comment

* get historic values tests

* unit tests for get historical rates on coinbasepro

* using time format time.RFC3339

* names to camel case and improved comments

* test cleanup

* changed to camel case

* added InArray tests

* dropped not needed string time

* enforced use of int64

* fixed make check

* cleaned up code organisation to be consistent

* fixed Travis remarks

* more Travis remarks

* added comments

* regenerated proto files after merge

* linter fix
This commit is contained in:
Christian Achilli
2020-01-24 05:10:33 +00:00
committed by Adrian Gallagher
parent e5b64a5580
commit 5ac5ec8fc1
42 changed files with 1230 additions and 357 deletions

View File

@@ -3350,3 +3350,104 @@ func gctScriptUpload(c *cli.Context) error {
jsonOutput(uploadCommand)
return nil
}
var getHistoricCandlesCommand = cli.Command{
Name: "gethistoriccandles",
Usage: "gets historical candles for the specified granularity up to range size time from now.",
ArgsUsage: "<exchange> <pair> <rangesize> <granularity>",
Action: getHistoricCandles,
Flags: []cli.Flag{
cli.StringFlag{
Name: "exchange, e",
Usage: "the exchange to get the candles from",
},
cli.StringFlag{
Name: "pair",
Usage: "the currency pair to get the candles for",
},
cli.IntFlag{
Name: "rangesize, r",
Usage: "the amount of time to go back from now to fetch candles in the given granularity",
},
cli.IntFlag{
Name: "granularity, g",
Usage: "value is in seconds and can be one of the following {60, 300, 900, 3600, 21600, 86400}",
},
},
}
func getHistoricCandles(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
cli.ShowCommandHelp(c, "gethistoriccandles")
return nil
}
var exchangeName string
if c.IsSet("exchange") {
exchangeName = c.String("exchange")
} else {
exchangeName = c.Args().First()
}
if !validExchange(exchangeName) {
return errInvalidExchange
}
var currencyPair string
if c.IsSet("pair") {
currencyPair = c.String("pair")
} else {
currencyPair = c.Args().Get(1)
}
if !validPair(currencyPair) {
return errInvalidPair
}
p := currency.NewPairDelimiter(currencyPair, pairDelimiter)
var rangesize int64
if c.IsSet("rangesize") {
rangesize = c.Int64("rangesize")
} else {
rs, err := strconv.Atoi(c.Args().Get(2))
if err != nil {
return fmt.Errorf("unable to strconv input to int. Err: %s", err)
}
rangesize = int64(rs)
}
var granularity int64
if c.IsSet("granularity") {
granularity = c.Int64("granularity")
} else {
gr, err := strconv.Atoi(c.Args().Get(3))
if err != nil {
return fmt.Errorf("unable to strconv input to int. Err: %s", err)
}
granularity = int64(gr)
}
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
result, err := client.GetHistoricCandles(context.Background(),
&gctrpc.GetHistoricCandlesRequest{
Exchange: exchangeName,
Pair: &gctrpc.CurrencyPair{
Delimiter: p.Delimiter,
Base: p.Base.String(),
Quote: p.Quote.String(),
},
Rangesize: rangesize,
Granularity: granularity,
})
if err != nil {
return err
}
jsonOutput(result)
return nil
}

View File

@@ -133,6 +133,7 @@ func main() {
getTickerStreamCommand,
getExchangeTickerStreamCommand,
getAuditEventCommand,
getHistoricCandlesCommand,
gctScriptCommand,
}

View File

@@ -12,6 +12,7 @@ import (
"os"
"os/user"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
@@ -354,3 +355,24 @@ func SplitStringSliceByLimit(in []string, limit uint) [][]string {
}
return sliceSlice
}
// InArray checks if _val_ belongs to _array_
func InArray(val, array interface{}) (exists bool, index int) {
exists = false
index = -1
if array == nil {
return
}
switch reflect.TypeOf(array).Kind() {
case reflect.Array, reflect.Slice:
s := reflect.ValueOf(array)
for i := 0; i < s.Len(); i++ {
if reflect.DeepEqual(val, s.Index(i).Interface()) {
index = i
exists = true
return
}
}
}
return
}

View File

@@ -526,3 +526,41 @@ func TestSplitStringSliceByLimit(t *testing.T) {
t.Errorf("expected len() to be 20 instead received: %v", len(out[0]))
}
}
func TestInArray(t *testing.T) {
InArray(nil, nil)
array := [6]int{2, 3, 5, 7, 11, 13}
isIn, pos := InArray(5, array)
if !isIn {
t.Errorf("failed to find the value within the array")
}
if pos != 2 {
t.Errorf("failed return the correct position of the value in the array")
}
isIn, _ = InArray(1, array)
if isIn {
t.Errorf("found a non existent value in the array")
}
slice := make([]int, 0)
slice = append(append(slice, 5), 3)
isIn, pos = InArray(5, slice)
if !isIn {
t.Errorf("failed to find the value within the slice")
}
if pos != 0 {
t.Errorf("failed return the correct position of the value in the slice")
}
isIn, pos = InArray(3, slice)
if !isIn {
t.Errorf("failed to find the value within the slice")
}
if pos != 1 {
t.Errorf("failed return the correct position of the value in the slice")
}
isIn, _ = InArray(1, slice)
if isIn {
t.Errorf("found a non existent value in the slice")
}
}

View File

@@ -1221,6 +1221,44 @@ func (s *RPCServer) GetAuditEvent(ctx context.Context, r *gctrpc.GetAuditEventRe
return &resp, nil
}
// GetHistoricCandles returns historical candles for a given exchange
func (s *RPCServer) GetHistoricCandles(ctx context.Context, req *gctrpc.GetHistoricCandlesRequest) (*gctrpc.GetHistoricCandlesResponse, error) {
if req.Exchange == "" {
return nil, errors.New(errExchangeNameUnset)
}
if req.Pair.String() == "" {
return nil, errors.New(errCurrencyPairUnset)
}
exchange := GetExchangeByName(req.Exchange)
if exchange == nil {
return nil, errors.New("Exchange " + req.Exchange + " not found")
}
candles, err := exchange.GetHistoricCandles(currency.Pair{
Delimiter: req.Pair.Delimiter,
Base: currency.NewCode(req.Pair.Base),
Quote: currency.NewCode(req.Pair.Quote),
}, req.Rangesize, req.Granularity)
if err != nil {
return nil, err
}
resp := gctrpc.GetHistoricCandlesResponse{}
for _, candle := range candles {
tempCandle := &gctrpc.Candle{
Time: candle.Time,
Low: candle.Low,
High: candle.High,
Open: candle.Open,
Close: candle.Close,
Volume: candle.Volume,
}
resp.Candle = append(resp.Candle, tempCandle)
}
return &resp, nil
}
// GCTScriptStatus returns a slice of current running scripts that includes next run time and uuid
func (s *RPCServer) GCTScriptStatus(ctx context.Context, r *gctrpc.GCTScriptStatusRequest) (*gctrpc.GCTScriptStatusResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {

View File

@@ -72,6 +72,11 @@ type Binance struct {
validIntervals []TimeInterval
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *Binance) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetExchangeInfo returns exchange information. Check binance_types for more
// information
func (b *Binance) GetExchangeInfo() (ExchangeInfo, error) {

View File

@@ -91,6 +91,11 @@ type Bitfinex struct {
WebsocketSubdChannels map[int]WebsocketChanInfo
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *Bitfinex) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetPlatformStatus returns the Bifinex platform status
func (b *Bitfinex) GetPlatformStatus() (int, error) {
var response []interface{}

View File

@@ -7,6 +7,7 @@ import (
"net/url"
"strconv"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
@@ -70,6 +71,11 @@ type Bitflyer struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *Bitflyer) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetLatestBlockCA returns the latest block information from bitflyer chain
// analysis system
func (b *Bitflyer) GetLatestBlockCA() (ChainAnalysisBlock, error) {

View File

@@ -11,6 +11,7 @@ import (
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -55,6 +56,11 @@ type Bithumb struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *Bithumb) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetTradablePairs returns a list of tradable currencies
func (b *Bithumb) GetTradablePairs() ([]string, error) {
result, err := b.GetAllTickers()

View File

@@ -9,6 +9,7 @@ import (
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -908,3 +909,8 @@ func calculateTradingFee(purchasePrice, amount float64, isMaker bool) float64 {
return fee * purchasePrice * amount
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *Bitmex) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}

View File

@@ -64,6 +64,11 @@ type Bitstamp struct {
WebsocketConn *wshandler.WebsocketConnection
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *Bitstamp) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetFee returns an estimate of fee based on type of transaction
func (b *Bitstamp) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
var fee float64

View File

@@ -9,6 +9,7 @@ import (
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -62,6 +63,11 @@ type Bittrex struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *Bittrex) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetMarkets is used to get the open and available trading markets at Bittrex
// along with other meta data.
func (b *Bittrex) GetMarkets() (Market, error) {

View File

@@ -82,6 +82,11 @@ type BTCMarkets struct {
WebsocketConn *wshandler.WebsocketConnection
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *BTCMarkets) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetMarkets returns the BTCMarkets instruments
func (b *BTCMarkets) GetMarkets() ([]Market, error) {
var resp []Market

View File

@@ -10,6 +10,7 @@ import (
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -324,3 +325,8 @@ func calculateTradingFee(isMaker bool) float64 {
func parseOrderTime(timeStr string) (time.Time, error) {
return time.Parse(btseTimeLayout, timeStr)
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (b *BTSE) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}

View File

@@ -162,19 +162,28 @@ func (c *CoinbasePro) GetTrades(currencyPair string) ([]Trade, error) {
// GetHistoricRates returns historic rates for a product. Rates are returned in
// grouped buckets based on requested granularity.
func (c *CoinbasePro) GetHistoricRates(currencyPair string, start, end, granularity int64) ([]History, error) {
func (c *CoinbasePro) GetHistoricRates(currencyPair, start, end string, granularity int64) ([]History, error) {
var resp [][]interface{}
var history []History
values := url.Values{}
if start > 0 {
values.Set("start", strconv.FormatInt(start, 10))
if len(start) > 0 {
values.Set("start", start)
} else {
values.Set("start", "")
}
if end > 0 {
values.Set("end", strconv.FormatInt(end, 10))
if len(end) > 0 {
values.Set("end", end)
} else {
values.Set("end", "")
}
allowedGranularities := [6]int64{60, 300, 900, 3600, 21600, 86400}
validGran, _ := common.InArray(granularity, allowedGranularities)
if !validGran {
return nil, errors.New("Invalid granularity value: " + strconv.FormatInt(granularity, 10) + ". Allowed values are {60, 300, 900, 3600, 21600, 86400}")
}
if granularity > 0 {
values.Set("granularity", strconv.FormatInt(granularity, 10))
}

View File

@@ -1,6 +1,7 @@
package coinbasepro
import (
"fmt"
"log"
"net/http"
"os"
@@ -75,10 +76,55 @@ func TestGetTrades(t *testing.T) {
}
}
func TestGetHistoricRates(t *testing.T) {
_, err := c.GetHistoricRates(testPair, 0, 0, 0)
func TestGetHistoricRatesApiCheck(t *testing.T) {
e := expectedCandles(5, 300, 60)
if e != nil {
t.Error(e)
}
e = expectedCandles(2, 600, 300)
if e != nil {
t.Error(e)
}
e = expectedCandles(2, 1800, 900)
if e != nil {
t.Error(e)
}
e = expectedCandles(2, 7200, 3600)
if e != nil {
t.Error(e)
}
e = expectedCandles(2, 43200, 21600)
if e != nil {
t.Error(e)
}
e = expectedCandles(2, 172800, 86400)
if e != nil {
t.Error(e)
}
}
// expectedCandles uses the previous candle time window because the current one might not be complete and if used the test would become non-deterministic
func expectedCandles(expectedCandles int, timeRange time.Duration, candleGranularity int64) error {
end := time.Now().UTC().Add(-time.Second * timeRange) // the latest candle may not yet be ready, so skipping to the previous one
start := end.Add(-time.Second * timeRange)
resp, err := c.GetHistoricRates(testPair, start.Format(time.RFC3339), end.Format(time.RFC3339), candleGranularity)
if err != nil {
t.Error("GetHistoricRates() error", err)
return err
}
if len(resp) != expectedCandles {
err := fmt.Errorf("expected %d candles, returned: %d", expectedCandles, len(resp))
return err
}
return nil
}
func TestGetHistoricRatesGranularityCheck(t *testing.T) {
end := time.Now().UTC()
start := time.Now().UTC().Add(-time.Second * 300)
invalidGranularity := 11
_, err := c.GetHistoricRates(testPair, start.Format(time.RFC3339), end.Format(time.RFC3339), int64(invalidGranularity))
if err == nil {
t.Error("granularity validation did not work as expected")
}
}

View File

@@ -96,6 +96,7 @@ func (c *CoinbasePro) SetDefaults() {
TradeFee: true,
FiatDepositFee: true,
FiatWithdrawalFee: true,
CandleHistory: true,
},
WebsocketCapabilities: protocol.Features{
TickerFetching: true,
@@ -615,3 +616,26 @@ func (c *CoinbasePro) GetSubscriptions() ([]wshandler.WebsocketChannelSubscripti
func (c *CoinbasePro) AuthenticateWebsocket() error {
return common.ErrFunctionNotSupported
}
// GetHistoricCandles Allows to retrieve an amount of candles back in time starting from now up to rangesize * granularity, where granularity is the trade period covered by each candle
func (c *CoinbasePro) GetHistoricCandles(p currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
end := time.Now().UTC()
b := granularity * rangesize
start := time.Now().UTC().Add(-time.Second * time.Duration(b))
history, err := c.GetHistoricRates(p.String(), start.Format(time.RFC3339), end.Format(time.RFC3339), granularity)
if err != nil {
return nil, err
}
var candles []exchange.Candle
for x := range history {
candles = append(candles, exchange.Candle{
Time: history[x].Time,
Low: history[x].Low,
High: history[x].High,
Open: history[x].Open,
Close: history[x].Close,
Volume: history[x].Volume,
})
}
return candles, nil
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
@@ -1072,3 +1073,8 @@ func (c *Coinbene) SendAuthHTTPRequest(method, path, epPath string, isSwap bool,
}
return json.Unmarshal(resp, result)
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (c *Coinbene) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -9,6 +9,7 @@ import (
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -55,6 +56,11 @@ type COINUT struct {
instrumentMap instrumentMap
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (c *COINUT) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// SeedInstruments seeds the instrument map
func (c *COINUT) SeedInstruments() error {
i, err := c.GetInstruments()

View File

@@ -139,6 +139,16 @@ type TradeHistory struct {
Description string
}
// Candle holds historic rate information. Modelled after the Coinbase historic rate structure
type Candle struct {
Time int64
Low float64
High float64
Open float64
Close float64
Volume float64
}
// FundHistory holds exchange funding history data
type FundHistory struct {
ExchangeName string

View File

@@ -49,6 +49,11 @@ type EXMO struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (e *EXMO) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetTrades returns the trades for a symbol or symbols
func (e *EXMO) GetTrades(symbol string) (map[string][]Trades, error) {
v := url.Values{}

View File

@@ -8,6 +8,7 @@ import (
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -47,6 +48,12 @@ type Gateio struct {
exchange.Base
}
// GetHistoriCandles
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (g *Gateio) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetSymbols returns all supported symbols
func (g *Gateio) GetSymbols() ([]string, error) {
var result []string

View File

@@ -11,6 +11,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
@@ -444,3 +445,8 @@ func calculateTradingFee(notionVolume *NotionalVolume, purchasePrice, amount flo
return volumeFee * amount * purchasePrice
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (g *Gemini) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -8,6 +8,7 @@ import (
"net/url"
"strconv"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -50,6 +51,11 @@ type HitBTC struct {
WebsocketConn *wshandler.WebsocketConnection
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (h *HitBTC) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// Public Market Data
// https://api.hitbtc.com/?python#market-data

View File

@@ -70,6 +70,11 @@ type HUOBI struct {
AuthenticatedWebsocketConn *wshandler.WebsocketConnection
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (h *HUOBI) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetSpotKline returns kline data
// KlinesRequestParams contains symbol, period and size
func (h *HUOBI) GetSpotKline(arg KlinesRequestParams) ([]KlineItem, error) {

View File

@@ -68,4 +68,5 @@ type IBotExchange interface {
GetDefaultConfig() (*config.ExchangeConfig, error)
GetBase() *Base
SupportsAsset(assetType asset.Item) bool
GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]Candle, error)
}

View File

@@ -40,6 +40,11 @@ type ItBit struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (i *ItBit) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetTicker returns ticker info for a specified market.
// currencyPair - example "XBTUSD" "XBTSGD" "XBTEUR"
func (i *ItBit) GetTicker(currencyPair string) (Ticker, error) {

View File

@@ -9,6 +9,7 @@ import (
"strings"
"sync"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -63,6 +64,11 @@ type Kraken struct {
wsRequestMtx sync.Mutex
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (k *Kraken) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetServerTime returns current server time
func (k *Kraken) GetServerTime() (TimeResponse, error) {
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenServerTime)

View File

@@ -8,6 +8,7 @@ import (
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -40,6 +41,11 @@ type LakeBTC struct {
WebsocketConn
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (l *LakeBTC) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetTicker returns the current ticker from lakeBTC
func (l *LakeBTC) GetTicker() (map[string]Ticker, error) {
response := make(map[string]TickerResponse)

View File

@@ -15,7 +15,9 @@ import (
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
gctcrypto "github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
@@ -578,3 +580,8 @@ func (l *Lbank) SendAuthHTTPRequest(method, endpoint string, vals url.Values, re
l.HTTPDebugging,
l.HTTPRecording)
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (l *Lbank) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
@@ -112,6 +113,11 @@ type LocalBitcoins struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (l *LocalBitcoins) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrFunctionNotSupported
}
// GetAccountInformation lets you retrieve the public user information on a
// LocalBitcoins user. The response contains the same information that is found
// on an account's public profile page.

View File

@@ -1,6 +1,9 @@
package okcoin
import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/okgroup"
)
@@ -18,3 +21,8 @@ const (
type OKCoin struct {
okgroup.OKGroup
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (o *OKCoin) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -5,6 +5,8 @@ import (
"net/http"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/okgroup"
)
@@ -45,6 +47,11 @@ type OKEX struct {
okgroup.OKGroup
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (o *OKEX) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrFunctionNotSupported
}
// GetFuturesPostions Get the information of all holding positions in futures trading.
// Due to high energy consumption, you are advised to capture data with the "Futures Account of a Currency" API instead.
func (o *OKEX) GetFuturesPostions() (resp okgroup.GetFuturesPositionsResponse, _ error) {

View File

@@ -10,6 +10,7 @@ import (
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -59,6 +60,11 @@ type Poloniex struct {
WebsocketConn *wshandler.WebsocketConnection
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (p *Poloniex) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetTicker returns current ticker information
func (p *Poloniex) GetTicker() (map[string]Ticker, error) {
type response struct {

View File

@@ -37,4 +37,5 @@ type Features struct {
AuthenticatedEndpoints bool `json:"authenticatedEndpoints,omitempty"`
MessageCorrelation bool `json:"messageCorrelation,omitempty"`
MessageSequenceNumbers bool `json:"messageSequenceNumbers,omitempty"`
CandleHistory bool `json:"candlehistory,omitempty"`
}

View File

@@ -8,6 +8,7 @@ import (
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -42,6 +43,11 @@ type Yobit struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (y *Yobit) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// GetInfo returns the Yobit info
func (y *Yobit) GetInfo() (Info, error) {
resp := Info{}

View File

@@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -47,6 +48,11 @@ type ZB struct {
exchange.Base
}
// GetHistoriCandles returns _rangesize_ number of candles for the given _granularity_ and _pair_ starting from the latest available
func (z *ZB) GetHistoricCandles(pair currency.Pair, rangesize, granularity int64) ([]exchange.Candle, error) {
return nil, common.ErrNotYetImplemented
}
// SpotNewOrder submits an order to ZB
func (z *ZB) SpotNewOrder(arg SpotNewOrderRequestParams) (int64, error) {
var result SpotNewOrderResponse

View File

@@ -1,4 +1,5 @@
echo "GoCryptoTrader: Generating gRPC, proxy and swagger files."
export GOPATH=$(go env GOPATH)
protoc -I=. -I=$GOPATH/src -I=$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:. rpc.proto
protoc -I=. -I=$GOPATH/src -I=$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:. rpc.proto
protoc -I=. -I=$GOPATH/src -I=$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=logtostderr=true:. rpc.proto

File diff suppressed because it is too large Load Diff

View File

@@ -1711,6 +1711,39 @@ func local_request_GoCryptoTrader_GCTScriptAutoLoadToggle_0(ctx context.Context,
}
var (
filter_GoCryptoTrader_GetHistoricCandles_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_GoCryptoTrader_GetHistoricCandles_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetHistoricCandlesRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetHistoricCandles_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetHistoricCandles(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GetHistoricCandles_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetHistoricCandlesRequest
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetHistoricCandles_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetHistoricCandles(ctx, &protoReq)
return msg, metadata, err
}
// RegisterGoCryptoTraderHandlerServer registers the http handlers for service GoCryptoTrader to "mux".
// UnaryRPC :call GoCryptoTraderServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -2804,6 +2837,26 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve
})
mux.Handle("GET", pattern_GoCryptoTrader_GetHistoricCandles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GetHistoricCandles_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GetHistoricCandles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@@ -3985,6 +4038,26 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve
})
mux.Handle("GET", pattern_GoCryptoTrader_GetHistoricCandles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GetHistoricCandles_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GetHistoricCandles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@@ -4102,6 +4175,8 @@ var (
pattern_GoCryptoTrader_GCTScriptListAll_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "stop"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptAutoLoadToggle_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "autoload"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GetHistoricCandles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "gethistoriccandles"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
@@ -4218,4 +4293,6 @@ var (
forward_GoCryptoTrader_GCTScriptListAll_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptAutoLoadToggle_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GetHistoricCandles_0 = runtime.ForwardResponseMessage
)

View File

@@ -521,6 +521,26 @@ message GetAuditEventResponse {
repeated AuditEvent events = 1;
}
message GetHistoricCandlesRequest {
string exchange = 1;
CurrencyPair pair = 2;
int64 rangesize = 3;
int64 granularity = 4;
}
message GetHistoricCandlesResponse {
repeated Candle candle =1;
}
message Candle {
int64 time = 1;
double low = 2;
double high = 3;
double open = 4;
double close = 5;
double volume = 6;
}
message AuditEvent {
string type = 1 ;
string identifier = 2;
@@ -956,5 +976,9 @@ service GoCryptoTrader {
};
}
rpc GetHistoricCandles(GetHistoricCandlesRequest) returns (GetHistoricCandlesResponse) {
option (google.api.http) = {
get: "/v1/gethistoriccandles"
};
}
}

View File

@@ -840,6 +840,62 @@
]
}
},
"/v1/gethistoriccandles": {
"get": {
"operationId": "GetHistoricCandles",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGetHistoricCandlesResponse"
}
}
},
"parameters": [
{
"name": "exchange",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "pair.delimiter",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "pair.base",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "pair.quote",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "rangesize",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
},
{
"name": "granularity",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
}
],
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/getinfo": {
"get": {
"operationId": "GetInfo",
@@ -1548,6 +1604,35 @@
"gctrpcCancelOrderResponse": {
"type": "object"
},
"gctrpcCandle": {
"type": "object",
"properties": {
"time": {
"type": "string",
"format": "int64"
},
"low": {
"type": "number",
"format": "double"
},
"high": {
"type": "number",
"format": "double"
},
"open": {
"type": "number",
"format": "double"
},
"close": {
"type": "number",
"format": "double"
},
"volume": {
"type": "number",
"format": "double"
}
}
},
"gctrpcCoin": {
"type": "object",
"properties": {
@@ -2032,6 +2117,17 @@
}
}
},
"gctrpcGetHistoricCandlesResponse": {
"type": "object",
"properties": {
"candle": {
"type": "array",
"items": {
"$ref": "#/definitions/gctrpcCandle"
}
}
}
},
"gctrpcGetInfoResponse": {
"type": "object",
"properties": {