mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-21 07:26:48 +00:00
OKX: Fix intermittent GetFuturesContractDetails issue, add spread endpoints and various refactors (#1900)
* OKX: Fix intermittent GetFuturesContractDetails issue and various refactors * refactor: Update LeadTraderRanksRequest fields for clarity and improve parameter checks * refactor: Simplify live contract check in GetFuturesContractDetails * OKX: Fix spread related issues and enhance tests * OKX: Disable spread websocket support and adjust conditional logic in test * refactor: Improve error handling in syncLeadTraderUniqueID and clean up variable usage * refactor: Update LeadTraderRanksRequest State type to bool and adjust rate limit * refactor: Rename State to HasVacancy in LeadTraderRanksRequest and update related logic
This commit is contained in:
@@ -3845,48 +3845,41 @@ func (ok *Okx) GetHistoryLeadTraders(ctx context.Context, instrumentType, after,
|
||||
return resp, ok.SendHTTPRequest(ctx, exchange.RestSpot, getMyLeadTradersEPL, http.MethodGet, common.EncodeURLValues("copytrading/lead-traders-history", params), nil, &resp, request.AuthenticatedRequest)
|
||||
}
|
||||
|
||||
// GetLeadTradersRanks retrieve lead trader ranks.
|
||||
// Instrument type: SWAP, the default value
|
||||
// Sort type"overview": overview, the default value "pnl": profit and loss "aum": assets under management "win_ratio": win ratio "pnl_ratio": pnl ratio "current_copy_trader_pnl": current copy trader pnl
|
||||
// Lead trader state: "0": All lead traders, the default, including vacancy and non-vacancy "1": lead traders who have vacancy
|
||||
// Minimum lead days '1': 7 days '2': 30 days '3': 90 days '4': 180 days
|
||||
func (ok *Okx) GetLeadTradersRanks(ctx context.Context, instrumentType, sortType, state,
|
||||
minLeadDays, minAssets, maxAssets, minAssetUnderManagement, maxAssetUnderManagement,
|
||||
dataVersion, page string, limit int64,
|
||||
) ([]LeadTradersRank, error) {
|
||||
// GetLeadTradersRanks retrieves lead trader ranks
|
||||
func (ok *Okx) GetLeadTradersRanks(ctx context.Context, req *LeadTraderRanksRequest) ([]LeadTradersRank, error) {
|
||||
params := url.Values{}
|
||||
if instrumentType != "" {
|
||||
params.Set("instType", instrumentType)
|
||||
if req.InstrumentType != "" {
|
||||
params.Set("instType", req.InstrumentType)
|
||||
}
|
||||
if sortType != "" {
|
||||
params.Set("sortType", sortType)
|
||||
if req.SortType != "" {
|
||||
params.Set("sortType", req.SortType)
|
||||
}
|
||||
if state != "" {
|
||||
params.Set("state", state)
|
||||
if req.HasVacancy {
|
||||
params.Set("state", "1")
|
||||
}
|
||||
if minLeadDays != "" {
|
||||
params.Set("minLeadDays", minLeadDays)
|
||||
if req.MinLeadDays != 0 {
|
||||
params.Set("minLeadDays", strconv.FormatUint(req.MinLeadDays, 10))
|
||||
}
|
||||
if minAssets != "" {
|
||||
params.Set("minAssets", minAssets)
|
||||
if req.MinAssets > 0 {
|
||||
params.Set("minAssets", strconv.FormatFloat(req.MinAssets, 'f', -1, 64))
|
||||
}
|
||||
if maxAssets != "" {
|
||||
params.Set("maxAssets", maxAssets)
|
||||
if req.MaxAssets > 0 {
|
||||
params.Set("maxAssets", strconv.FormatFloat(req.MaxAssets, 'f', -1, 64))
|
||||
}
|
||||
if minAssetUnderManagement != "" {
|
||||
params.Set("minAum", minAssetUnderManagement)
|
||||
if req.MinAssetsUnderManagement > 0 {
|
||||
params.Set("minAum", strconv.FormatFloat(req.MinAssetsUnderManagement, 'f', -1, 64))
|
||||
}
|
||||
if maxAssetUnderManagement != "" {
|
||||
params.Set("maxAum", maxAssetUnderManagement)
|
||||
if req.MaxAssetsUnderManagement > 0 {
|
||||
params.Set("maxAum", strconv.FormatFloat(req.MaxAssetsUnderManagement, 'f', -1, 64))
|
||||
}
|
||||
if dataVersion != "" {
|
||||
params.Set("dataVer", dataVersion)
|
||||
if req.DataVersion != 0 {
|
||||
params.Set("dataVer", strconv.FormatUint(req.DataVersion, 10))
|
||||
}
|
||||
if page != "" {
|
||||
params.Set("page", page)
|
||||
if req.Page != 0 {
|
||||
params.Set("page", strconv.FormatUint(req.Page, 10))
|
||||
}
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
if req.Limit != 0 {
|
||||
params.Set("limit", strconv.FormatUint(req.Limit, 10))
|
||||
}
|
||||
var resp []LeadTradersRank
|
||||
return resp, ok.SendHTTPRequest(ctx, exchange.RestSpot, getLeadTraderRanksEPL, http.MethodGet, common.EncodeURLValues("copytrading/public-lead-traders", params), nil, &resp, request.UnauthenticatedRequest)
|
||||
@@ -4844,6 +4837,52 @@ func (ok *Okx) GetPublicSpreadTrades(ctx context.Context, spreadID string) ([]Sp
|
||||
return resp, ok.SendHTTPRequest(ctx, exchange.RestSpot, getSpreadPublicTradesEPL, http.MethodGet, common.EncodeURLValues("sprd/public-trades", params), nil, &resp, request.UnauthenticatedRequest)
|
||||
}
|
||||
|
||||
// GetSpreadCandlesticks retrieves candlestick charts for a given spread instrument
|
||||
func (ok *Okx) GetSpreadCandlesticks(ctx context.Context, spreadID string, interval kline.Interval, before, after time.Time, limit uint64) ([]SpreadCandlestick, error) {
|
||||
if spreadID == "" {
|
||||
return nil, fmt.Errorf("%w, spread ID is required", errMissingInstrumentID)
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("sprdId", spreadID)
|
||||
if !before.IsZero() {
|
||||
params.Set("before", strconv.FormatInt(before.UnixMilli(), 10))
|
||||
}
|
||||
if !after.IsZero() {
|
||||
params.Set("after", strconv.FormatInt(after.UnixMilli(), 10))
|
||||
}
|
||||
if bar := IntervalFromString(interval, true); bar != "" {
|
||||
params.Set("bar", bar)
|
||||
}
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatUint(limit, 10))
|
||||
}
|
||||
var resp []SpreadCandlestick
|
||||
return resp, ok.SendHTTPRequest(ctx, exchange.RestSpot, getSpreadCandlesticksEPL, http.MethodGet, common.EncodeURLValues("market/sprd-candles", params), nil, &resp, request.UnauthenticatedRequest)
|
||||
}
|
||||
|
||||
// GetSpreadCandlesticksHistory retrieves candlestick chart history for a given spread instrument for a period of up to 3 months
|
||||
func (ok *Okx) GetSpreadCandlesticksHistory(ctx context.Context, spreadID string, interval kline.Interval, before, after time.Time, limit uint64) ([]SpreadCandlestick, error) {
|
||||
if spreadID == "" {
|
||||
return nil, fmt.Errorf("%w, spread ID is required", errMissingInstrumentID)
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("sprdId", spreadID)
|
||||
if !before.IsZero() {
|
||||
params.Set("before", strconv.FormatInt(before.UnixMilli(), 10))
|
||||
}
|
||||
if !after.IsZero() {
|
||||
params.Set("after", strconv.FormatInt(after.UnixMilli(), 10))
|
||||
}
|
||||
if bar := IntervalFromString(interval, true); bar != "" {
|
||||
params.Set("bar", bar)
|
||||
}
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatUint(limit, 10))
|
||||
}
|
||||
var resp []SpreadCandlestick
|
||||
return resp, ok.SendHTTPRequest(ctx, exchange.RestSpot, getSpreadCandlesticksHistoryEPL, http.MethodGet, common.EncodeURLValues("market/sprd-history-candles", params), nil, &resp, request.UnauthenticatedRequest)
|
||||
}
|
||||
|
||||
// CancelAllSpreadOrdersAfterCountdown cancel all pending orders after the countdown timeout. Only applicable to spread trading
|
||||
func (ok *Okx) CancelAllSpreadOrdersAfterCountdown(ctx context.Context, timeoutDuration int64) (*SpreadOrderCancellationResponse, error) {
|
||||
if (timeoutDuration != 0) && (timeoutDuration < 10 || timeoutDuration > 120) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3092,14 +3092,18 @@ type SpreadOrderbook struct {
|
||||
|
||||
// SpreadTicker represents a ticker instance
|
||||
type SpreadTicker struct {
|
||||
SpreadID string `json:"sprdId"`
|
||||
Last types.Number `json:"last"`
|
||||
LastSize types.Number `json:"lastSz"`
|
||||
AskPrice types.Number `json:"askPx"`
|
||||
AskSize types.Number `json:"askSz"`
|
||||
BidPrice types.Number `json:"bidPx"`
|
||||
BidSize types.Number `json:"bidSz"`
|
||||
Timestamp types.Time `json:"ts"`
|
||||
SpreadID string `json:"sprdId"`
|
||||
Last types.Number `json:"last"`
|
||||
LastSize types.Number `json:"lastSz"`
|
||||
AskPrice types.Number `json:"askPx"`
|
||||
AskSize types.Number `json:"askSz"`
|
||||
BidPrice types.Number `json:"bidPx"`
|
||||
BidSize types.Number `json:"bidSz"`
|
||||
Open24Hour types.Number `json:"open24h"`
|
||||
High24Hour types.Number `json:"high24h"`
|
||||
Low24Hour types.Number `json:"low24h"`
|
||||
Volume24Hour types.Number `json:"vol24h"`
|
||||
Timestamp types.Time `json:"ts"`
|
||||
}
|
||||
|
||||
// SpreadPublicTradeItem represents publicly available trade order instance
|
||||
@@ -3112,6 +3116,22 @@ type SpreadPublicTradeItem struct {
|
||||
Timestamp types.Time `json:"ts"`
|
||||
}
|
||||
|
||||
// SpreadCandlestick represents a candlestick instance
|
||||
type SpreadCandlestick struct {
|
||||
Timestamp types.Time
|
||||
Open types.Number
|
||||
High types.Number
|
||||
Low types.Number
|
||||
Close types.Number
|
||||
Volume types.Number
|
||||
Confirm types.Number
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals the JSON data into a SpreadCandlestick struct
|
||||
func (s *SpreadCandlestick) UnmarshalJSON(data []byte) error {
|
||||
return json.Unmarshal(data, &[7]any{&s.Timestamp, &s.Open, &s.High, &s.Low, &s.Close, &s.Volume, &s.Confirm})
|
||||
}
|
||||
|
||||
// UnitConvertResponse unit convert response
|
||||
type UnitConvertResponse struct {
|
||||
InstrumentID string `json:"instId"`
|
||||
@@ -4731,6 +4751,21 @@ type CopyTradingLeadTrader struct {
|
||||
CopyState string `json:"copyState"`
|
||||
}
|
||||
|
||||
// LeadTraderRanksRequest represents lead trader ranks request parameters
|
||||
type LeadTraderRanksRequest struct {
|
||||
InstrumentType string // Instrument type e.g 'SWAP'. The default value is 'SWAP'
|
||||
SortType string // Overview, the default value. pnl: profit and loss, aum: assets under management, win_ratio: win ratio,pnl_ratio: pnl ratio, current_copy_trader_pnl: current copy trader pnl
|
||||
HasVacancy bool // false: include all lead traders (default), with or without vacancies; true: include only those with vacancies
|
||||
MinLeadDays uint64 // 1: 7 days. 2: 30 days. 3: 90 days. 4: 180 days
|
||||
MinAssets float64 // Minimum assets in USDT
|
||||
MaxAssets float64 // Maximum assets in USDT
|
||||
MinAssetsUnderManagement float64 // Minimum assets under management in USDT
|
||||
MaxAssetsUnderManagement float64 // Maximum assets under management in USDT
|
||||
DataVersion uint64 // It is 14 numbers. e.g. 20231010182400 used for pagination. A new version will be generated every 10 minutes. Only last 5 versions are stored. The default is latest version
|
||||
Page uint64 // Page number for pagination
|
||||
Limit uint64 // Number of results per request. The maximum is 20; the default is 10
|
||||
}
|
||||
|
||||
// LeadTradersRank represents lead traders rank info
|
||||
type LeadTradersRank struct {
|
||||
DataVer string `json:"dataVer"`
|
||||
|
||||
@@ -63,6 +63,11 @@ func (ok *Okx) SetDefaults() {
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
|
||||
// TODO: Disabled until spread/business websocket is implemented
|
||||
if err := ok.DisableAssetWebsocketSupport(asset.Spread); err != nil {
|
||||
log.Errorf(log.ExchangeSys, "%s error disabling %q asset websocket support: %s", ok.Name, asset.Spread.String(), err)
|
||||
}
|
||||
|
||||
// Fill out the capabilities/features that the exchange supports
|
||||
ok.Features = exchange.Features{
|
||||
CurrencyTranslations: currency.NewTranslations(map[currency.Code]currency.Code{
|
||||
@@ -360,47 +365,75 @@ func (ok *Okx) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) err
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (ok *Okx) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
|
||||
var err error
|
||||
p, err = ok.FormatExchangeCurrency(p, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok.SupportsAsset(a) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, a)
|
||||
}
|
||||
mdata, err := ok.GetTicker(ctx, p.String())
|
||||
|
||||
p, err := ok.FormatExchangeCurrency(p, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var baseVolume, quoteVolume float64
|
||||
switch a {
|
||||
case asset.Spot, asset.Margin:
|
||||
baseVolume = mdata.Vol24H.Float64()
|
||||
quoteVolume = mdata.VolCcy24H.Float64()
|
||||
case asset.PerpetualSwap, asset.Futures, asset.Options:
|
||||
baseVolume = mdata.VolCcy24H.Float64()
|
||||
quoteVolume = mdata.Vol24H.Float64()
|
||||
default:
|
||||
return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, a)
|
||||
}
|
||||
err = ticker.ProcessTicker(&ticker.Price{
|
||||
Last: mdata.LastTradePrice.Float64(),
|
||||
High: mdata.High24H.Float64(),
|
||||
Low: mdata.Low24H.Float64(),
|
||||
Bid: mdata.BestBidPrice.Float64(),
|
||||
BidSize: mdata.BestBidSize.Float64(),
|
||||
Ask: mdata.BestAskPrice.Float64(),
|
||||
AskSize: mdata.BestAskSize.Float64(),
|
||||
Volume: baseVolume,
|
||||
QuoteVolume: quoteVolume,
|
||||
Open: mdata.Open24H.Float64(),
|
||||
Pair: p,
|
||||
ExchangeName: ok.Name,
|
||||
AssetType: a,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
if a == asset.Spread {
|
||||
spreadTicker, err := ok.GetPublicSpreadTickers(ctx, p.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(spreadTicker) == 0 {
|
||||
return nil, fmt.Errorf("no ticker data for %s", p.String())
|
||||
}
|
||||
|
||||
if err := ticker.ProcessTicker(&ticker.Price{
|
||||
Last: spreadTicker[0].Last.Float64(),
|
||||
High: spreadTicker[0].High24Hour.Float64(),
|
||||
Low: spreadTicker[0].Low24Hour.Float64(),
|
||||
Bid: spreadTicker[0].BidPrice.Float64(),
|
||||
BidSize: spreadTicker[0].BidSize.Float64(),
|
||||
Ask: spreadTicker[0].AskPrice.Float64(),
|
||||
AskSize: spreadTicker[0].AskSize.Float64(),
|
||||
Volume: spreadTicker[0].Volume24Hour.Float64(),
|
||||
Open: spreadTicker[0].Open24Hour.Float64(),
|
||||
LastUpdated: spreadTicker[0].Timestamp.Time(),
|
||||
Pair: p,
|
||||
AssetType: a,
|
||||
ExchangeName: ok.Name,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
mdata, err := ok.GetTicker(ctx, p.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var baseVolume, quoteVolume float64
|
||||
switch a {
|
||||
case asset.Spot, asset.Margin:
|
||||
baseVolume = mdata.Vol24H.Float64()
|
||||
quoteVolume = mdata.VolCcy24H.Float64()
|
||||
case asset.PerpetualSwap, asset.Futures, asset.Options:
|
||||
baseVolume = mdata.VolCcy24H.Float64()
|
||||
quoteVolume = mdata.Vol24H.Float64()
|
||||
}
|
||||
if err := ticker.ProcessTicker(&ticker.Price{
|
||||
Last: mdata.LastTradePrice.Float64(),
|
||||
High: mdata.High24H.Float64(),
|
||||
Low: mdata.Low24H.Float64(),
|
||||
Bid: mdata.BestBidPrice.Float64(),
|
||||
BidSize: mdata.BestBidSize.Float64(),
|
||||
Ask: mdata.BestAskPrice.Float64(),
|
||||
AskSize: mdata.BestAskSize.Float64(),
|
||||
Volume: baseVolume,
|
||||
QuoteVolume: quoteVolume,
|
||||
Open: mdata.Open24H.Float64(),
|
||||
Pair: p,
|
||||
ExchangeName: ok.Name,
|
||||
AssetType: a,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return ticker.GetTicker(ok.Name, p, a)
|
||||
}
|
||||
|
||||
@@ -779,6 +812,10 @@ func (ok *Okx) GetRecentTrades(ctx context.Context, p currency.Pair, assetType a
|
||||
|
||||
// GetHistoricTrades retrieves historic trade data within the timeframe provided
|
||||
func (ok *Okx) GetHistoricTrades(ctx context.Context, p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]trade.Data, error) {
|
||||
if !ok.SupportsAsset(assetType) || assetType == asset.Spread {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, assetType)
|
||||
}
|
||||
|
||||
if timestampStart.Before(time.Now().Add(-kline.ThreeMonth.Duration())) {
|
||||
return nil, errOnlyThreeMonthsSupported
|
||||
}
|
||||
@@ -1518,9 +1555,6 @@ func (ok *Okx) GetOrderInfo(ctx context.Context, orderID string, pair currency.P
|
||||
return nil, currency.ErrCurrencyPairsEmpty
|
||||
}
|
||||
instrumentID := pairFormat.Format(pair)
|
||||
if !ok.SupportsAsset(assetType) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, assetType)
|
||||
}
|
||||
orderDetail, err := ok.GetOrderDetail(ctx, &OrderDetailRequestParam{
|
||||
InstrumentID: instrumentID,
|
||||
OrderID: orderID,
|
||||
@@ -1955,39 +1989,66 @@ func (ok *Okx) ValidateAPICredentials(ctx context.Context, assetType asset.Item)
|
||||
|
||||
// GetHistoricCandles returns candles between a time period for a set time interval
|
||||
func (ok *Okx) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, interval kline.Interval, start, end time.Time) (*kline.Item, error) {
|
||||
if !ok.SupportsAsset(a) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, a)
|
||||
}
|
||||
|
||||
req, err := ok.GetKlineRequest(pair, a, interval, start, end, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candles, err := ok.GetCandlesticksHistory(ctx,
|
||||
req.RequestFormatted.Base.String()+
|
||||
currency.DashDelimiter+
|
||||
req.RequestFormatted.Quote.String(),
|
||||
req.ExchangeInterval,
|
||||
start.Add(-time.Nanosecond), // Start time not inclusive of candle.
|
||||
end,
|
||||
300)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var timeSeries []kline.Candle
|
||||
switch a {
|
||||
case asset.Spread:
|
||||
candles, err := ok.GetSpreadCandlesticksHistory(ctx, req.RequestFormatted.String(), req.ExchangeInterval, start.Add(-time.Nanosecond), end, 100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeSeries = make([]kline.Candle, len(candles))
|
||||
for x := range candles {
|
||||
timeSeries[x] = kline.Candle{
|
||||
Time: candles[x].Timestamp.Time(),
|
||||
Open: candles[x].Open.Float64(),
|
||||
High: candles[x].High.Float64(),
|
||||
Low: candles[x].Low.Float64(),
|
||||
Close: candles[x].Close.Float64(),
|
||||
Volume: candles[x].Volume.Float64(),
|
||||
}
|
||||
}
|
||||
default:
|
||||
candles, err := ok.GetCandlesticksHistory(ctx,
|
||||
req.RequestFormatted.String(),
|
||||
req.ExchangeInterval,
|
||||
start.Add(-time.Nanosecond), // Start time not inclusive of candle.
|
||||
end,
|
||||
100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeSeries := make([]kline.Candle, len(candles))
|
||||
for x := range candles {
|
||||
timeSeries[x] = kline.Candle{
|
||||
Time: candles[x].OpenTime.Time(),
|
||||
Open: candles[x].OpenPrice.Float64(),
|
||||
High: candles[x].HighestPrice.Float64(),
|
||||
Low: candles[x].LowestPrice.Float64(),
|
||||
Close: candles[x].ClosePrice.Float64(),
|
||||
Volume: candles[x].Volume.Float64(),
|
||||
timeSeries = make([]kline.Candle, len(candles))
|
||||
for x := range candles {
|
||||
timeSeries[x] = kline.Candle{
|
||||
Time: candles[x].OpenTime.Time(),
|
||||
Open: candles[x].OpenPrice.Float64(),
|
||||
High: candles[x].HighestPrice.Float64(),
|
||||
Low: candles[x].LowestPrice.Float64(),
|
||||
Close: candles[x].ClosePrice.Float64(),
|
||||
Volume: candles[x].Volume.Float64(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return req.ProcessResponse(timeSeries)
|
||||
}
|
||||
|
||||
// GetHistoricCandlesExtended returns candles between a time period for a set time interval
|
||||
func (ok *Okx) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, interval kline.Interval, start, end time.Time) (*kline.Item, error) {
|
||||
if !ok.SupportsAsset(a) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, a)
|
||||
}
|
||||
|
||||
req, err := ok.GetKlineExtendedRequest(pair, a, interval, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2002,27 +2063,47 @@ func (ok *Okx) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pai
|
||||
|
||||
timeSeries := make([]kline.Candle, 0, req.Size())
|
||||
for y := range req.RangeHolder.Ranges {
|
||||
var candles []CandleStick
|
||||
candles, err = ok.GetCandlesticksHistory(ctx,
|
||||
req.RequestFormatted.Base.String()+
|
||||
currency.DashDelimiter+
|
||||
req.RequestFormatted.Quote.String(),
|
||||
req.ExchangeInterval,
|
||||
req.RangeHolder.Ranges[y].Start.Time.Add(-time.Nanosecond), // Start time not inclusive of candle.
|
||||
req.RangeHolder.Ranges[y].End.Time,
|
||||
300)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for x := range candles {
|
||||
timeSeries = append(timeSeries, kline.Candle{
|
||||
Time: candles[x].OpenTime.Time(),
|
||||
Open: candles[x].OpenPrice.Float64(),
|
||||
High: candles[x].HighestPrice.Float64(),
|
||||
Low: candles[x].LowestPrice.Float64(),
|
||||
Close: candles[x].ClosePrice.Float64(),
|
||||
Volume: candles[x].Volume.Float64(),
|
||||
})
|
||||
switch a {
|
||||
case asset.Spread:
|
||||
candles, err := ok.GetSpreadCandlesticksHistory(ctx,
|
||||
req.RequestFormatted.String(),
|
||||
req.ExchangeInterval,
|
||||
req.RangeHolder.Ranges[y].Start.Time.Add(-time.Nanosecond), // Start time not inclusive of candle.
|
||||
req.RangeHolder.Ranges[y].End.Time,
|
||||
100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for x := range candles {
|
||||
timeSeries = append(timeSeries, kline.Candle{
|
||||
Time: candles[x].Timestamp.Time(),
|
||||
Open: candles[x].Open.Float64(),
|
||||
High: candles[x].High.Float64(),
|
||||
Low: candles[x].Low.Float64(),
|
||||
Close: candles[x].Close.Float64(),
|
||||
Volume: candles[x].Volume.Float64(),
|
||||
})
|
||||
}
|
||||
default:
|
||||
candles, err := ok.GetCandlesticksHistory(ctx,
|
||||
req.RequestFormatted.String(),
|
||||
req.ExchangeInterval,
|
||||
req.RangeHolder.Ranges[y].Start.Time.Add(-time.Nanosecond), // Start time not inclusive of candle.
|
||||
req.RangeHolder.Ranges[y].End.Time,
|
||||
100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for x := range candles {
|
||||
timeSeries = append(timeSeries, kline.Candle{
|
||||
Time: candles[x].OpenTime.Time(),
|
||||
Open: candles[x].OpenPrice.Float64(),
|
||||
High: candles[x].HighestPrice.Float64(),
|
||||
Low: candles[x].LowestPrice.Float64(),
|
||||
Close: candles[x].ClosePrice.Float64(),
|
||||
Volume: candles[x].Volume.Float64(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return req.ProcessResponse(timeSeries)
|
||||
@@ -2731,16 +2812,31 @@ func (ok *Okx) GetFuturesContractDetails(ctx context.Context, item asset.Item) (
|
||||
}
|
||||
resp := make([]futures.Contract, len(result))
|
||||
for i := range result {
|
||||
var cp, underlying currency.Pair
|
||||
underlying, err = currency.NewPairFromString(result[i].Underlying)
|
||||
cp, err := currency.NewPairFromString(result[i].InstrumentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp, err = currency.NewPairFromString(result[i].InstrumentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
var (
|
||||
underlying currency.Pair
|
||||
settleCurr currency.Code
|
||||
contractSettlementType futures.ContractSettlementType
|
||||
)
|
||||
|
||||
if result[i].State == "live" {
|
||||
underlying, err = currency.NewPairFromString(result[i].Underlying)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settleCurr = currency.NewCode(result[i].SettlementCurrency)
|
||||
|
||||
contractSettlementType = futures.Linear
|
||||
if result[i].SettlementCurrency == result[i].BaseCurrency {
|
||||
contractSettlementType = futures.Inverse
|
||||
}
|
||||
}
|
||||
settleCurr := currency.NewCode(result[i].SettlementCurrency)
|
||||
|
||||
var ct futures.ContractType
|
||||
if item == asset.PerpetualSwap {
|
||||
ct = futures.Perpetual
|
||||
@@ -2752,25 +2848,25 @@ func (ok *Okx) GetFuturesContractDetails(ctx context.Context, item asset.Item) (
|
||||
ct = futures.Quarterly
|
||||
}
|
||||
}
|
||||
contractSettlementType := futures.Linear
|
||||
if result[i].SettlementCurrency == result[i].BaseCurrency {
|
||||
contractSettlementType = futures.Inverse
|
||||
}
|
||||
|
||||
resp[i] = futures.Contract{
|
||||
Exchange: ok.Name,
|
||||
Name: cp,
|
||||
Underlying: underlying,
|
||||
Asset: item,
|
||||
StartDate: result[i].ListTime.Time(),
|
||||
EndDate: result[i].ExpTime.Time(),
|
||||
IsActive: result[i].State == "live",
|
||||
Status: result[i].State,
|
||||
Type: ct,
|
||||
SettlementType: contractSettlementType,
|
||||
SettlementCurrencies: currency.Currencies{settleCurr},
|
||||
MarginCurrency: settleCurr,
|
||||
Multiplier: result[i].ContractValue.Float64(),
|
||||
MaxLeverage: result[i].MaxLeverage.Float64(),
|
||||
Exchange: ok.Name,
|
||||
Name: cp,
|
||||
Underlying: underlying,
|
||||
Asset: item,
|
||||
StartDate: result[i].ListTime.Time(),
|
||||
EndDate: result[i].ExpTime.Time(),
|
||||
IsActive: result[i].State == "live",
|
||||
Status: result[i].State,
|
||||
Type: ct,
|
||||
SettlementType: contractSettlementType,
|
||||
MarginCurrency: settleCurr,
|
||||
Multiplier: result[i].ContractValue.Float64(),
|
||||
MaxLeverage: result[i].MaxLeverage.Float64(),
|
||||
}
|
||||
|
||||
if !settleCurr.IsEmpty() {
|
||||
resp[i].SettlementCurrencies = currency.Currencies{settleCurr}
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
|
||||
@@ -254,6 +254,8 @@ const (
|
||||
getSpreadOrderbookEPL
|
||||
getSpreadTickerEPL
|
||||
getSpreadPublicTradesEPL
|
||||
getSpreadCandlesticksEPL
|
||||
getSpreadCandlesticksHistoryEPL
|
||||
cancelAllSpreadOrdersAfterEPL
|
||||
getActiveSpreadOrdersEPL
|
||||
getSpreadOrders7DaysEPL
|
||||
@@ -581,20 +583,22 @@ var rateLimits = func() request.RateLimitDefinitions {
|
||||
getBlockTickersEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getBlockTradesEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
|
||||
// Spread Orders rate limiters
|
||||
placeSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
cancelSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
cancelAllSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 10, 1),
|
||||
amendSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadOrderDetailsEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getActiveSpreadOrdersEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 10, 1),
|
||||
getSpreadOrders7DaysEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadOrderTradesEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadsEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadOrderbookEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadTickerEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadPublicTradesEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
cancelAllSpreadOrdersAfterEPL: request.NewRateLimitWithWeight(oneSecondInterval, 1, 1),
|
||||
// Spread trading related rate limiters
|
||||
placeSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
cancelSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
cancelAllSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 10, 1),
|
||||
amendSpreadOrderEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadOrderDetailsEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getActiveSpreadOrdersEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 10, 1),
|
||||
getSpreadOrders7DaysEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadOrderTradesEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadsEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadOrderbookEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadTickerEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadPublicTradesEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
getSpreadCandlesticksEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 40, 1),
|
||||
getSpreadCandlesticksHistoryEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
cancelAllSpreadOrdersAfterEPL: request.NewRateLimitWithWeight(oneSecondInterval, 1, 1),
|
||||
|
||||
// Public Data Endpoints
|
||||
getInstrumentsEPL: request.NewRateLimitWithWeight(twoSecondsInterval, 20, 1),
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange"
|
||||
)
|
||||
|
||||
func TestWSPlaceOrder(t *testing.T) {
|
||||
@@ -20,8 +21,10 @@ func TestWSPlaceOrder(t *testing.T) {
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
|
||||
testexch.SetupWs(t, ok)
|
||||
|
||||
out := &PlaceOrderRequestParam{
|
||||
InstrumentID: btcusdt,
|
||||
InstrumentID: mainPair.String(),
|
||||
TradeMode: TradeModeIsolated, // depending on portfolio settings this can also be TradeModeCash
|
||||
Side: "Buy",
|
||||
OrderType: "post_only",
|
||||
@@ -46,8 +49,10 @@ func TestWSPlaceMultipleOrders(t *testing.T) {
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
|
||||
testexch.SetupWs(t, ok)
|
||||
|
||||
out := PlaceOrderRequestParam{
|
||||
InstrumentID: btcusdt,
|
||||
InstrumentID: mainPair.String(),
|
||||
TradeMode: TradeModeIsolated, // depending on portfolio settings this can also be TradeModeCash
|
||||
Side: "Buy",
|
||||
OrderType: "post_only",
|
||||
@@ -70,12 +75,14 @@ func TestWSCancelOrder(t *testing.T) {
|
||||
_, err = ok.WSCancelOrder(t.Context(), &CancelOrderRequestParam{})
|
||||
require.ErrorIs(t, err, errMissingInstrumentID)
|
||||
|
||||
_, err = ok.WSCancelOrder(t.Context(), &CancelOrderRequestParam{InstrumentID: btcusdt})
|
||||
_, err = ok.WSCancelOrder(t.Context(), &CancelOrderRequestParam{InstrumentID: mainPair.String()})
|
||||
require.ErrorIs(t, err, order.ErrOrderIDNotSet)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
|
||||
got, err := ok.WSCancelOrder(request.WithVerbose(t.Context()), &CancelOrderRequestParam{InstrumentID: btcusdt, OrderID: "2341161427393388544"})
|
||||
testexch.SetupWs(t, ok)
|
||||
|
||||
got, err := ok.WSCancelOrder(request.WithVerbose(t.Context()), &CancelOrderRequestParam{InstrumentID: mainPair.String(), OrderID: "2341161427393388544"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
}
|
||||
@@ -89,12 +96,14 @@ func TestWSCancelMultipleOrders(t *testing.T) {
|
||||
_, err = ok.WSCancelMultipleOrders(t.Context(), []CancelOrderRequestParam{{}})
|
||||
require.ErrorIs(t, err, errMissingInstrumentID)
|
||||
|
||||
_, err = ok.WSCancelMultipleOrders(t.Context(), []CancelOrderRequestParam{{InstrumentID: btcusdt}})
|
||||
_, err = ok.WSCancelMultipleOrders(t.Context(), []CancelOrderRequestParam{{InstrumentID: mainPair.String()}})
|
||||
require.ErrorIs(t, err, order.ErrOrderIDNotSet)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
|
||||
got, err := ok.WSCancelMultipleOrders(request.WithVerbose(t.Context()), []CancelOrderRequestParam{{InstrumentID: btcusdt, OrderID: "2341184920998715392"}})
|
||||
testexch.SetupWs(t, ok)
|
||||
|
||||
got, err := ok.WSCancelMultipleOrders(request.WithVerbose(t.Context()), []CancelOrderRequestParam{{InstrumentID: mainPair.String(), OrderID: "2341184920998715392"}})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
}
|
||||
@@ -109,7 +118,7 @@ func TestWSAmendOrder(t *testing.T) {
|
||||
_, err = ok.WSAmendOrder(t.Context(), out)
|
||||
require.ErrorIs(t, err, errMissingInstrumentID)
|
||||
|
||||
out.InstrumentID = btcusdt
|
||||
out.InstrumentID = mainPair.String()
|
||||
_, err = ok.WSAmendOrder(t.Context(), out)
|
||||
require.ErrorIs(t, err, order.ErrOrderIDNotSet)
|
||||
|
||||
@@ -119,6 +128,8 @@ func TestWSAmendOrder(t *testing.T) {
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
|
||||
testexch.SetupWs(t, ok)
|
||||
|
||||
out.NewPrice = 21000
|
||||
got, err := ok.WSAmendOrder(request.WithVerbose(t.Context()), out)
|
||||
require.NoError(t, err)
|
||||
@@ -135,7 +146,7 @@ func TestWSAmendMultipleOrders(t *testing.T) {
|
||||
_, err = ok.WSAmendMultipleOrders(t.Context(), []AmendOrderRequestParams{out})
|
||||
require.ErrorIs(t, err, errMissingInstrumentID)
|
||||
|
||||
out.InstrumentID = btcusdt
|
||||
out.InstrumentID = mainPair.String()
|
||||
_, err = ok.WSAmendMultipleOrders(t.Context(), []AmendOrderRequestParams{out})
|
||||
require.ErrorIs(t, err, order.ErrOrderIDNotSet)
|
||||
|
||||
@@ -144,6 +155,7 @@ func TestWSAmendMultipleOrders(t *testing.T) {
|
||||
require.ErrorIs(t, err, errInvalidNewSizeOrPriceInformation)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
testexch.SetupWs(t, ok)
|
||||
out.NewPrice = 20000
|
||||
|
||||
got, err := ok.WSAmendMultipleOrders(request.WithVerbose(t.Context()), []AmendOrderRequestParams{out})
|
||||
@@ -163,10 +175,11 @@ func TestWSMassCancelOrders(t *testing.T) {
|
||||
require.ErrorIs(t, err, errInstrumentFamilyRequired)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
testexch.SetupWs(t, ok)
|
||||
err = ok.WSMassCancelOrders(request.WithVerbose(t.Context()), []CancelMassReqParam{
|
||||
{
|
||||
InstrumentType: "OPTION",
|
||||
InstrumentFamily: "BTC-USD",
|
||||
InstrumentFamily: optionsPair.String(),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -178,8 +191,9 @@ func TestWSPlaceSpreadOrder(t *testing.T) {
|
||||
require.ErrorIs(t, err, common.ErrNilPointer)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
testexch.SetupWs(t, ok)
|
||||
result, err := ok.WSPlaceSpreadOrder(request.WithVerbose(t.Context()), &SpreadOrderParam{
|
||||
SpreadID: "BTC-USDT_BTC-USDT-SWAP",
|
||||
SpreadID: spreadPair.String(),
|
||||
ClientOrderID: "b15",
|
||||
Side: order.Buy.Lower(),
|
||||
OrderType: "limit",
|
||||
@@ -200,6 +214,7 @@ func TestWSAmendSpreadOrder(t *testing.T) {
|
||||
require.ErrorIs(t, err, errSizeOrPriceIsRequired)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
testexch.SetupWs(t, ok)
|
||||
result, err := ok.WSAmendSpreadOrder(request.WithVerbose(t.Context()), &AmendSpreadOrderParam{
|
||||
OrderID: "2510789768709120",
|
||||
NewSize: 2,
|
||||
@@ -213,6 +228,7 @@ func TestWSCancelSpreadOrder(t *testing.T) {
|
||||
_, err := ok.WSCancelSpreadOrder(t.Context(), "", "")
|
||||
require.ErrorIs(t, err, order.ErrOrderIDNotSet)
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
testexch.SetupWs(t, ok)
|
||||
result, err := ok.WSCancelSpreadOrder(request.WithVerbose(t.Context()), "1234", "")
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
@@ -221,7 +237,8 @@ func TestWSCancelSpreadOrder(t *testing.T) {
|
||||
func TestWSCancelAllSpreadOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, ok, canManipulateRealOrders)
|
||||
err := ok.WSCancelAllSpreadOrders(request.WithVerbose(t.Context()), "BTC-USDT_BTC-USDT-SWAP")
|
||||
testexch.SetupWs(t, ok)
|
||||
err := ok.WSCancelAllSpreadOrders(request.WithVerbose(t.Context()), spreadPair.String())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ func SetupWs(tb testing.TB, e exchange.IBotExchange) {
|
||||
w.GenerateSubs = func() (subscription.List, error) { return subscription.List{}, nil }
|
||||
|
||||
err = w.Connect()
|
||||
require.NoError(tb, err, "WsConnect should not error")
|
||||
require.NoError(tb, err, "Connect must not error")
|
||||
|
||||
setupWsOnce[e] = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user