mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-03 23:16:53 +00:00
(Exchange) Add GetHistoricCandles() & GetHistoricCandlesEx() support to exchanges (#479)
* implemented binance and bitfinex GetHistoricCandles wrapper methods) * coinbene supported added * after and before clean up * gateio wrapper completed * merged upstream/master * Added bsaic KlineIntervalSupported() method * Converted binance fixed test * WIP * new KlineConvertToExchangeStandardString method added * end of day WIP * WIP * end of day WIP started migration of trade history * added kline support to hitbtc huobi lbank * added exchangehistory to all supported exchanges started work on coinbase 300 candles/request method * end of day WIP * removed unused ta and misc changes to flag ready for review * yobit cleanup * revert coinbase changES * general code clean up and added zb support * poloniex support added * renamed method to FormatExchangeKlineInterval other misc fixes * linter fixes * linter fixes * removed verbose * fixed poloniex test coverage * revert poloniex mock data * regenerated poloniex mock data * a very verbose clean up * binance mock clean up * removed unneeded t.Log() * setting verbose to true to debug CI issue * first pass changes addressed * common.ErrNotYetImplemented implemented :D * comments added * WIP-addressed exchange requests and reverted previous GetExchangeHistory changes * WIP-addressed exchange requests and reverted previous GetExchangeHistory changes * increased test coverage added kraken support * OKGroup support completed started work on address GetExchangeHistory feedback and migrating to own PR under https://github.com/xtda/gocryptotrader/tree/exchange_history * convert zb ratelimits * gofmt run on okcoin * increased delay on rate limit * gofmt package * fixed panic with coinbene and bithumb if conversion fails * very broken end of day WIP * added support for GetHistoricCandlesEx to coinbase and binance * gofmt package * coinbase, btcmarkets, zb ex wrapper function added * added all exchange support for ex regenerated mock data * update bithumb to return wrapper method * gofmt package * end of day started work on changes * reworked test coverage added okgroup support general fixes/change requests addressed * Added OneMonth * limit checks on supportedexchanges * reverted getexchangehistory * reworked binance tesT * added workaround for kraken panic * renamed command to extended removed interval check on non-implemented commands * added wrapperconfig back * increased test coverage for FormatExchangeKlineInterval * WIP * increased test coverage for FormatExchangeKlineInterval bitfinex/gateio/huobi * linter fixes * zb kraken lbank coinbene btcmarkets support added * removed verbose * OK group support for other asset types added * swapped margin to use spot endpoint * index support added test coverage added for asset types * added asset type to okcoin test * gofmt * add asset to extended method * removed verbose * add support for coinbene swap increase test coverage * removed verbose * small clean up of okgroup wrapper functions * verbose to troubleshoot CI issues * removed verbose * added error check reverted coinbasechanges * readme updated * removed unused start/finish started work on decoupling api requests from kline package * restructured coinbene, bithumb methods, added bitstamp support * kraken time fix * BTCMarkets restructure * typo fix * removed test for futures due to contact changing * added start/end date to extended method over range * converted to assettranslator * removed verbose * removed invalid char * reverted incorrectly removed return * added import * further template updates * macos hates my keyboard :D * misc canges * x -> i * removed verbose * updated fixCasing to allocate var before checks * removed time conversion * sort all outgoing kline candles * fixCasing fix * after/before checks added * added parallel to test * logic check on BTCmarkets * removed unused param, used correct iterator * converted HitBTC to use time.Time * add iszero false check to candle times * updated resultlimit to 5000 * new line added * added comment to exported const * use configured ratelimit * fixed pair for test * panic fixed WIP on fixCasing * fixCasing rework, started work on readme docs * enable rate limiter for wrapper issues tool * docs updated * removed err from return and formatted currency * updated Yobit supported status * Updated HitBTC to use onehour candles due to test exeuction times * added further details to gctcli output * added link to docs * added link to tempalte * disable FTX websocket in config_example * fix poloneix * regenerated poloniex mock data * removed recording flag
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"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/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
@@ -184,8 +185,8 @@ func (g *Gateio) GetOrderbook(symbol string) (Orderbook, error) {
|
||||
}
|
||||
|
||||
// GetSpotKline returns kline data for the most recent time period
|
||||
func (g *Gateio) GetSpotKline(arg KlinesRequestParams) ([]*KLineResponse, error) {
|
||||
urlPath := fmt.Sprintf("%s/%s/%s/%s?group_sec=%d&range_hour=%d",
|
||||
func (g *Gateio) GetSpotKline(arg KlinesRequestParams) (kline.Item, error) {
|
||||
urlPath := fmt.Sprintf("%s/%s/%s/%s?group_sec=%s&range_hour=%d",
|
||||
g.API.Endpoints.URLSecondary,
|
||||
gateioAPIVersion,
|
||||
gateioKline,
|
||||
@@ -196,58 +197,59 @@ func (g *Gateio) GetSpotKline(arg KlinesRequestParams) ([]*KLineResponse, error)
|
||||
var rawKlines map[string]interface{}
|
||||
err := g.SendHTTPRequest(urlPath, &rawKlines)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return kline.Item{}, err
|
||||
}
|
||||
|
||||
result := kline.Item{
|
||||
Exchange: g.Name,
|
||||
}
|
||||
|
||||
var result []*KLineResponse
|
||||
if rawKlines == nil || rawKlines["data"] == nil {
|
||||
return nil, fmt.Errorf("rawKlines is nil. Err: %s", err)
|
||||
return kline.Item{}, errors.New("rawKlines is nil")
|
||||
}
|
||||
|
||||
rawKlineDatasString, _ := json.Marshal(rawKlines["data"].([]interface{}))
|
||||
var rawKlineDatas [][]interface{}
|
||||
if err := json.Unmarshal(rawKlineDatasString, &rawKlineDatas); err != nil {
|
||||
return nil, fmt.Errorf("rawKlines unmarshal failed. Err: %s", err)
|
||||
return kline.Item{}, fmt.Errorf("rawKlines unmarshal failed. Err: %s", err)
|
||||
}
|
||||
|
||||
for _, k := range rawKlineDatas {
|
||||
otString, _ := strconv.ParseFloat(k[0].(string), 64)
|
||||
otString, err := strconv.ParseFloat(k[0].(string), 64)
|
||||
if err != nil {
|
||||
return kline.Item{}, err
|
||||
}
|
||||
ot, err := convert.TimeFromUnixTimestampFloat(otString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse Kline.OpenTime. Err: %s", err)
|
||||
return kline.Item{}, fmt.Errorf("cannot parse Kline.OpenTime. Err: %s", err)
|
||||
}
|
||||
_vol, err := convert.FloatFromString(k[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse Kline.Volume. Err: %s", err)
|
||||
}
|
||||
_id, err := convert.FloatFromString(k[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse Kline.Id. Err: %s", err)
|
||||
return kline.Item{}, fmt.Errorf("cannot parse Kline.Volume. Err: %s", err)
|
||||
}
|
||||
_close, err := convert.FloatFromString(k[2])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse Kline.Close. Err: %s", err)
|
||||
return kline.Item{}, fmt.Errorf("cannot parse Kline.Close. Err: %s", err)
|
||||
}
|
||||
_high, err := convert.FloatFromString(k[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse Kline.High. Err: %s", err)
|
||||
return kline.Item{}, fmt.Errorf("cannot parse Kline.High. Err: %s", err)
|
||||
}
|
||||
_low, err := convert.FloatFromString(k[4])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse Kline.Low. Err: %s", err)
|
||||
return kline.Item{}, fmt.Errorf("cannot parse Kline.Low. Err: %s", err)
|
||||
}
|
||||
_open, err := convert.FloatFromString(k[5])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse Kline.Open. Err: %s", err)
|
||||
return kline.Item{}, fmt.Errorf("cannot parse Kline.Open. Err: %s", err)
|
||||
}
|
||||
result = append(result, &KLineResponse{
|
||||
ID: _id,
|
||||
KlineTime: ot,
|
||||
Volume: _vol,
|
||||
Close: _close,
|
||||
High: _high,
|
||||
Low: _low,
|
||||
Open: _open,
|
||||
result.Candles = append(result.Candles, kline.Candle{
|
||||
Time: ot,
|
||||
Volume: _vol,
|
||||
Close: _close,
|
||||
High: _high,
|
||||
Low: _low,
|
||||
Open: _open,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
@@ -148,11 +151,10 @@ func TestGetOrderbook(t *testing.T) {
|
||||
|
||||
func TestGetSpotKline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := g.GetSpotKline(KlinesRequestParams{
|
||||
Symbol: "btc_usdt",
|
||||
GroupSec: TimeIntervalFiveMinutes, // 5 minutes or less
|
||||
HourSize: 1, // 1 hour data
|
||||
GroupSec: "5", // 5 minutes or less
|
||||
HourSize: 1, // 1 hour data
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -578,7 +580,6 @@ func setupWSTestAuth(t *testing.T) {
|
||||
// TestWsUnsubscribe dials websocket, sends an unsubscribe request.
|
||||
func TestWsUnsubscribe(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
g.Verbose = true
|
||||
err := g.Unsubscribe(wshandler.WebsocketChannelSubscription{
|
||||
Channel: "ticker.subscribe",
|
||||
Currency: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_"),
|
||||
@@ -762,3 +763,52 @@ func TestParseTime(t *testing.T) {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHistoricCandles(t *testing.T) {
|
||||
currencyPair := currency.NewPairFromString("BTC_USDT")
|
||||
startTime := time.Now().Add(-time.Hour * 6)
|
||||
_, err := g.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHistoricCandlesExtended(t *testing.T) {
|
||||
currencyPair := currency.NewPairFromString("BTC_USDT")
|
||||
startTime := time.Now().Add(-time.Hour * 6)
|
||||
_, err := g.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_FormatExchangeKlineInterval(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
interval kline.Interval
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"OneMin",
|
||||
kline.OneMin,
|
||||
"60",
|
||||
},
|
||||
{
|
||||
"OneDay",
|
||||
kline.OneDay,
|
||||
"86400",
|
||||
},
|
||||
}
|
||||
|
||||
for x := range testCases {
|
||||
test := testCases[x]
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ret := g.FormatExchangeKlineInterval(test.interval)
|
||||
|
||||
if ret != test.output {
|
||||
t.Fatalf("unexpected result return expected: %v received: %v", test.output, ret)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ type BalancesResponse struct {
|
||||
type KlinesRequestParams struct {
|
||||
Symbol string // Required field; example LTCBTC,BTCUSDT
|
||||
HourSize int // How many hours of data
|
||||
GroupSec TimeInterval
|
||||
GroupSec string
|
||||
}
|
||||
|
||||
// KLineResponse holds the kline response data
|
||||
|
||||
@@ -108,12 +108,29 @@ func (g *Gateio) SetDefaults() {
|
||||
},
|
||||
WithdrawPermissions: exchange.AutoWithdrawCrypto |
|
||||
exchange.NoFiatWithdrawals,
|
||||
Kline: kline.ExchangeCapabilitiesSupported{
|
||||
Intervals: true,
|
||||
},
|
||||
},
|
||||
Enabled: exchange.FeaturesEnabled{
|
||||
AutoPairUpdates: true,
|
||||
Kline: kline.ExchangeCapabilitiesEnabled{
|
||||
Intervals: map[string]bool{
|
||||
kline.OneMin.Word(): true,
|
||||
kline.ThreeMin.Word(): true,
|
||||
kline.FiveMin.Word(): true,
|
||||
kline.FifteenMin.Word(): true,
|
||||
kline.ThirtyMin.Word(): true,
|
||||
kline.OneHour.Word(): true,
|
||||
kline.TwoHour.Word(): true,
|
||||
kline.FourHour.Word(): true,
|
||||
kline.SixHour.Word(): true,
|
||||
kline.TwelveHour.Word(): true,
|
||||
kline.OneDay.Word(): true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
g.Requester = request.New(g.Name,
|
||||
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
|
||||
|
||||
@@ -710,7 +727,39 @@ func (g *Gateio) ValidateCredentials() error {
|
||||
return g.CheckTransientError(err)
|
||||
}
|
||||
|
||||
// GetHistoricCandles returns candles between a time period for a set time interval
|
||||
func (g *Gateio) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end time.Time, interval time.Duration) (kline.Item, error) {
|
||||
return kline.Item{}, common.ErrNotYetImplemented
|
||||
// FormatExchangeKlineInterval returns Interval to exchange formatted string
|
||||
func (g *Gateio) FormatExchangeKlineInterval(in kline.Interval) string {
|
||||
return strconv.FormatFloat(in.Duration().Seconds(), 'f', 0, 64)
|
||||
}
|
||||
|
||||
// GetHistoricCandles returns candles between a time period for a set time interval
|
||||
func (g *Gateio) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
if !g.KlineIntervalEnabled(interval) {
|
||||
return kline.Item{}, kline.ErrorKline{
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
hours := end.Sub(start).Hours()
|
||||
params := KlinesRequestParams{
|
||||
Symbol: g.FormatExchangeCurrency(pair, a).String(),
|
||||
GroupSec: g.FormatExchangeKlineInterval(interval),
|
||||
HourSize: int(hours),
|
||||
}
|
||||
|
||||
klineData, err := g.GetSpotKline(params)
|
||||
if err != nil {
|
||||
return kline.Item{}, err
|
||||
}
|
||||
klineData.Interval = interval
|
||||
klineData.Pair = pair
|
||||
klineData.Asset = a
|
||||
|
||||
klineData.SortCandlesByTimestamp(false)
|
||||
return klineData, nil
|
||||
}
|
||||
|
||||
// GetHistoricCandlesExtended returns candles between a time period for a set time interval
|
||||
func (g *Gateio) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
return g.GetHistoricCandles(pair, a, start, end, interval)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user