mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 23:16:49 +00:00
Okx: Fix Instrument unmarshal (#1225)
* Okx: Fix Instrument unmarshal This fixes: `json: invalid use of ,string struct tag, trying to unmarshal "" into float64` when TwapSz or other fields are empty, which happened briefly today. As a DriveBy it also simplifies the exposed Instrument type to native types only. * Okx: Add missing InstrumentFamily to Instrument * Okx: Add Instrument Unmarshal tests DriveBy: Removes stray newline in type conversions * Okx: Fix empty line upsetting linter
This commit is contained in:
@@ -3125,3 +3125,100 @@ func TestGetIntervalEnum(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const instrumentJSON = `{"alias":"","baseCcy":"","category":"1","ctMult":"1","ctType":"linear","ctVal":"0.0001","ctValCcy":"BTC","expTime":"","instFamily":"BTC-USDC","instId":"BTC-USDC-SWAP","instType":"SWAP","lever":"125","listTime":"1666076190000","lotSz":"1","maxIcebergSz":"100000000.0000000000000000","maxLmtSz":"100000000","maxMktSz":"85000","maxStopSz":"85000","maxTriggerSz":"100000000.0000000000000000","maxTwapSz":"","minSz":"1","optType":"","quoteCcy":"","settleCcy":"USDC","state":"live","stk":"","tickSz":"0.1","uly":"BTC-USDC"}`
|
||||
|
||||
func TestInstrument(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var i Instrument
|
||||
err := json.Unmarshal([]byte(instrumentJSON), &i)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if i.Alias != "" {
|
||||
t.Error("expected empty alias")
|
||||
}
|
||||
if i.BaseCurrency != "" {
|
||||
t.Error("expected empty base currency")
|
||||
}
|
||||
if i.Category != "1" {
|
||||
t.Error("expected 1 category")
|
||||
}
|
||||
if i.ContractMultiplier != "1" {
|
||||
t.Error("expected 1 contract multiplier")
|
||||
}
|
||||
if i.ContractType != "linear" {
|
||||
t.Error("expected linear contract type")
|
||||
}
|
||||
if i.ContractValue != "0.0001" {
|
||||
t.Error("expected 0.0001 contract value")
|
||||
}
|
||||
if i.ContractValueCurrency != currency.BTC.String() {
|
||||
t.Error("expected BTC contract value currency")
|
||||
}
|
||||
if !i.ExpTime.IsZero() {
|
||||
t.Error("expected empty expiry time")
|
||||
}
|
||||
if i.InstrumentFamily != "BTC-USDC" {
|
||||
t.Error("expected BTC-USDC instrument family")
|
||||
}
|
||||
if i.InstrumentID != "BTC-USDC-SWAP" {
|
||||
t.Error("expected BTC-USDC-SWAP instrument ID")
|
||||
}
|
||||
if i.InstrumentType != asset.PerpetualSwap {
|
||||
t.Error("expected SWAP instrument type")
|
||||
}
|
||||
if i.MaxLeverage != 125 {
|
||||
t.Error("expected 125 leverage")
|
||||
}
|
||||
if i.ListTime.UnixMilli() != 1666076190000 {
|
||||
t.Error("expected 1666076190000 listing time")
|
||||
}
|
||||
if i.LotSize != 1 {
|
||||
t.Error("expected 1 lot size")
|
||||
}
|
||||
if i.MaxSpotIcebergSize != 100000000.0000000000000000 {
|
||||
t.Error("expected 100000000.0000000000000000 max iceberg order size")
|
||||
}
|
||||
if i.MaxQuantityOfSpotLimitOrder != 100000000 {
|
||||
t.Error("expected 100000000 max limit order size")
|
||||
}
|
||||
if i.MaxQuantityOfMarketLimitOrder != 85000 {
|
||||
t.Error("expected 85000 max market order size")
|
||||
}
|
||||
if i.MaxStopSize != 85000 {
|
||||
t.Error("expected 85000 max stop order size")
|
||||
}
|
||||
if i.MaxTriggerSize != 100000000.0000000000000000 {
|
||||
t.Error("expected 100000000.0000000000000000 max trigger order size")
|
||||
}
|
||||
if i.MaxQuantityOfSpotTwapLimitOrder != 0 {
|
||||
t.Error("expected empty max TWAP size")
|
||||
}
|
||||
if i.MinimumOrderSize != 1 {
|
||||
t.Error("expected 1 min size")
|
||||
}
|
||||
if i.OptionType != "" {
|
||||
t.Error("expected empty option type")
|
||||
}
|
||||
if i.QuoteCurrency != "" {
|
||||
t.Error("expected empty quote currency")
|
||||
}
|
||||
if i.SettlementCurrency != currency.USDC.String() {
|
||||
t.Error("expected USDC settlement currency")
|
||||
}
|
||||
if i.State != "live" {
|
||||
t.Error("expected live state")
|
||||
}
|
||||
if i.StrikePrice != "" {
|
||||
t.Error("expected empty strike price")
|
||||
}
|
||||
if i.TickSize != 0.1 {
|
||||
t.Error("expected 0.1 tick size")
|
||||
}
|
||||
if i.Underlying != "BTC-USDC" {
|
||||
t.Error("expected BTC-USDC underlying")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package okx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -65,40 +64,84 @@ func (a *okxUnixMilliTime) Time() time.Time {
|
||||
return time.UnixMilli(int64(*a))
|
||||
}
|
||||
|
||||
// numbersOnlyRegexp for checking the value is numerics only
|
||||
var numbersOnlyRegexp = regexp.MustCompile(`^\d*$`)
|
||||
type okxTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON deserializes byte data to okxTime instance.
|
||||
func (t *okxTime) UnmarshalJSON(data []byte) error {
|
||||
var num string
|
||||
err := json.Unmarshal(data, &num)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if num == "" {
|
||||
return nil
|
||||
}
|
||||
value, err := strconv.ParseInt(num, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Time = time.UnixMilli(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
type okxAssetType struct {
|
||||
asset.Item
|
||||
}
|
||||
|
||||
// UnmarshalJSON deserializes JSON, and timestamp information.
|
||||
func (a *okxAssetType) UnmarshalJSON(data []byte) error {
|
||||
var t string
|
||||
err := json.Unmarshal(data, &t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.Item, err = GetAssetTypeFromInstrumentType(strings.ToUpper(t))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalJSON deserializes JSON, and timestamp information.
|
||||
func (a *Instrument) UnmarshalJSON(data []byte) error {
|
||||
type Alias Instrument
|
||||
chil := &struct {
|
||||
*Alias
|
||||
ListTime string `json:"listTime"`
|
||||
ExpTime string `json:"expTime"`
|
||||
InstrumentType string `json:"instType"`
|
||||
ListTime okxTime `json:"listTime"`
|
||||
ExpTime okxTime `json:"expTime"`
|
||||
InstrumentType okxAssetType `json:"instType"`
|
||||
MaxLeverage okxNumericalValue `json:"lever"`
|
||||
TickSize okxNumericalValue `json:"tickSz"`
|
||||
LotSize okxNumericalValue `json:"lotSz"`
|
||||
MinimumOrderSize okxNumericalValue `json:"minSz"`
|
||||
MaxQuantityOfSpotLimitOrder okxNumericalValue `json:"maxLmtSz"`
|
||||
MaxQuantityOfMarketLimitOrder okxNumericalValue `json:"maxMktSz"`
|
||||
MaxQuantityOfSpotTwapLimitOrder okxNumericalValue `json:"maxTwapSz"`
|
||||
MaxSpotIcebergSize okxNumericalValue `json:"maxIcebergSz"`
|
||||
MaxTriggerSize okxNumericalValue `json:"maxTriggerSz"`
|
||||
MaxStopSize okxNumericalValue `json:"maxStopSz"`
|
||||
}{
|
||||
Alias: (*Alias)(a),
|
||||
}
|
||||
err := json.Unmarshal(data, chil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if numbersOnlyRegexp.MatchString(chil.ListTime) {
|
||||
var val int
|
||||
if val, err = strconv.Atoi(chil.ListTime); err == nil {
|
||||
a.ListTime = time.UnixMilli(int64(val))
|
||||
}
|
||||
}
|
||||
if numbersOnlyRegexp.MatchString(chil.ExpTime) {
|
||||
var val int
|
||||
if val, err = strconv.Atoi(chil.ExpTime); err == nil {
|
||||
a.ExpTime = time.UnixMilli(int64(val))
|
||||
}
|
||||
}
|
||||
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
|
||||
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
|
||||
if err := json.Unmarshal(data, chil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.ListTime = chil.ListTime.Time
|
||||
a.ExpTime = chil.ExpTime.Time
|
||||
a.InstrumentType = chil.InstrumentType.Item
|
||||
a.MaxLeverage = chil.MaxLeverage.Float64()
|
||||
a.TickSize = chil.TickSize.Float64()
|
||||
a.LotSize = chil.LotSize.Float64()
|
||||
a.MinimumOrderSize = chil.MinimumOrderSize.Float64()
|
||||
a.MaxQuantityOfSpotLimitOrder = chil.MaxQuantityOfSpotLimitOrder.Float64()
|
||||
a.MaxQuantityOfMarketLimitOrder = chil.MaxQuantityOfMarketLimitOrder.Float64()
|
||||
a.MaxQuantityOfSpotTwapLimitOrder = chil.MaxQuantityOfSpotTwapLimitOrder.Float64()
|
||||
a.MaxSpotIcebergSize = chil.MaxSpotIcebergSize.Float64()
|
||||
a.MaxTriggerSize = chil.MaxTriggerSize.Float64()
|
||||
a.MaxStopSize = chil.MaxStopSize.Float64()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -274,33 +274,34 @@ type InstrumentsFetchParams struct {
|
||||
|
||||
// Instrument representing an instrument with open contract.
|
||||
type Instrument struct {
|
||||
InstrumentType asset.Item `json:"instType"`
|
||||
InstrumentID string `json:"instId"`
|
||||
Underlying string `json:"uly"`
|
||||
Category string `json:"category"`
|
||||
BaseCurrency string `json:"baseCcy"`
|
||||
QuoteCurrency string `json:"quoteCcy"`
|
||||
SettlementCurrency string `json:"settleCcy"`
|
||||
ContractValue string `json:"ctVal"`
|
||||
ContractMultiplier string `json:"ctMult"`
|
||||
ContractValueCurrency string `json:"ctValCcy"`
|
||||
OptionType string `json:"optType"`
|
||||
StrikePrice string `json:"stk"`
|
||||
ListTime time.Time `json:"listTime"`
|
||||
ExpTime time.Time `json:"expTime"`
|
||||
MaxLeverage okxNumericalValue `json:"lever"`
|
||||
TickSize okxNumericalValue `json:"tickSz"`
|
||||
LotSize okxNumericalValue `json:"lotSz"`
|
||||
MinimumOrderSize okxNumericalValue `json:"minSz"`
|
||||
ContractType string `json:"ctType"`
|
||||
Alias string `json:"alias"`
|
||||
State string `json:"state"`
|
||||
MaxQuantityOfSpotLimitOrder float64 `json:"maxLmtSz,string"`
|
||||
MaxQuantityOfMarketLimitOrder float64 `json:"maxMktSz,string"`
|
||||
MaxQuantityOfSpotTwapLimitOrder float64 `json:"maxTwapSz,string"`
|
||||
MaxSpotIcebergSize float64 `json:"maxIcebergSz,string"`
|
||||
MaxTriggerSize float64 `json:"maxTriggerSz,string"`
|
||||
MaxStopSize float64 `json:"maxStopSz,string"`
|
||||
InstrumentType asset.Item `json:"instType"`
|
||||
InstrumentID string `json:"instId"`
|
||||
InstrumentFamily string `json:"instFamily"`
|
||||
Underlying string `json:"uly"`
|
||||
Category string `json:"category"`
|
||||
BaseCurrency string `json:"baseCcy"`
|
||||
QuoteCurrency string `json:"quoteCcy"`
|
||||
SettlementCurrency string `json:"settleCcy"`
|
||||
ContractValue string `json:"ctVal"`
|
||||
ContractMultiplier string `json:"ctMult"`
|
||||
ContractValueCurrency string `json:"ctValCcy"`
|
||||
OptionType string `json:"optType"`
|
||||
StrikePrice string `json:"stk"`
|
||||
ListTime time.Time `json:"listTime"`
|
||||
ExpTime time.Time `json:"expTime"`
|
||||
MaxLeverage float64 `json:"lever"`
|
||||
TickSize float64 `json:"tickSz"`
|
||||
LotSize float64 `json:"lotSz"`
|
||||
MinimumOrderSize float64 `json:"minSz"`
|
||||
ContractType string `json:"ctType"`
|
||||
Alias string `json:"alias"`
|
||||
State string `json:"state"`
|
||||
MaxQuantityOfSpotLimitOrder float64 `json:"maxLmtSz"`
|
||||
MaxQuantityOfMarketLimitOrder float64 `json:"maxMktSz"`
|
||||
MaxQuantityOfSpotTwapLimitOrder float64 `json:"maxTwapSz"`
|
||||
MaxSpotIcebergSize float64 `json:"maxIcebergSz"`
|
||||
MaxTriggerSize float64 `json:"maxTriggerSz"`
|
||||
MaxStopSize float64 `json:"maxStopSz"`
|
||||
}
|
||||
|
||||
// DeliveryHistoryDetail holds instrument id and delivery price information detail
|
||||
|
||||
@@ -341,8 +341,8 @@ func (ok *Okx) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) err
|
||||
limits[x] = order.MinMaxLevel{
|
||||
Pair: pair,
|
||||
Asset: a,
|
||||
PriceStepIncrementSize: insts[x].TickSize.Float64(),
|
||||
MinAmount: insts[x].MinimumOrderSize.Float64(),
|
||||
PriceStepIncrementSize: insts[x].TickSize,
|
||||
MinAmount: insts[x].MinimumOrderSize,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user