diff --git a/Const.go b/Const.go index e193b1fb..2ef285af 100644 --- a/Const.go +++ b/Const.go @@ -119,42 +119,45 @@ var ( QUARTER_CONTRACT = "quarter" //季度合约 BI_QUARTER_CONTRACT = "bi_quarter" // NEXT QUARTER SWAP_CONTRACT = "swap" //永续合约 + SWAP_USDT_CONTRACT = "swap-usdt" ) //exchanges const const ( - KUCOIN = "kucoin.com" - OKCOIN_COM = "okcoin.com" - OKEX = "okex.com" - OKEX_V3 = "okex.com_v3" - OKEX_FUTURE = "okex.com_future" - OKEX_SWAP = "okex.com_swap" - HUOBI = "huobi.com" - HUOBI_PRO = "huobi.pro" - BITSTAMP = "bitstamp.net" - KRAKEN = "kraken.com" - ZB = "zb.com" - BITFINEX = "bitfinex.com" - BINANCE = "binance.com" - BINANCE_SWAP = "binance.com_swap" - POLONIEX = "poloniex.com" - COINEX = "coinex.com" - BITHUMB = "bithumb.com" - GATEIO = "gate.io" - BITTREX = "bittrex.com" - GDAX = "gdax.com" - BIGONE = "big.one" - FCOIN = "fcoin.com" - FCOIN_MARGIN = "fcoin.com_margin" - FMEX = "fmex.com" - HITBTC = "hitbtc.com" - BITMEX = "bitmex.com" - BITMEX_TEST = "testnet.bitmex.com" - CRYPTOPIA = "cryptopia.co.nz" - HBDM = "hbdm.com" - COINBENE = "coinbene.com" - ATOP = "a.top" - BITGET_SWAP = "bitget_swap" + KUCOIN = "kucoin.com" + OKCOIN_COM = "okcoin.com" + OKEX = "okex.com" + OKEX_V3 = "okex.com_v3" + OKEX_FUTURE = "okex.com_future" + OKEX_SWAP = "okex.com_swap" + HUOBI = "huobi.com" + HUOBI_PRO = "huobi.pro" + BITSTAMP = "bitstamp.net" + KRAKEN = "kraken.com" + ZB = "zb.com" + BITFINEX = "bitfinex.com" + BINANCE = "binance.com" + BINANCE_SWAP = "binance.com_swap" + BINANCE_FUTURES = "binance.com_futures" + POLONIEX = "poloniex.com" + COINEX = "coinex.com" + BITHUMB = "bithumb.com" + GATEIO = "gate.io" + BITTREX = "bittrex.com" + GDAX = "gdax.com" + BIGONE = "big.one" + FCOIN = "fcoin.com" + FCOIN_MARGIN = "fcoin.com_margin" + FMEX = "fmex.com" + HITBTC = "hitbtc.com" + BITMEX = "bitmex.com" + BITMEX_TEST = "testnet.bitmex.com" + CRYPTOPIA = "cryptopia.co.nz" + HBDM = "hbdm.com" + HBDM_SWAP = "hbdm.com_swap" + COINBENE = "coinbene.com" + ATOP = "a.top" + BITGET_SWAP = "bitget_swap" ) const ( @@ -166,8 +169,9 @@ const ( SPOT_MARGIN //币币杠杆交易 WALLET // 资金账户 _ - TIPS //余币宝 - SWAP //永续合约 + TIPS //余币宝 + SWAP //永续合约 + SWAP_USDT //usdt本位永续合约 ) type LimitOrderOptionalParameter int diff --git a/IMG_1177.jpg b/IMG_1177.jpg index d67929bd..da66f319 100644 Binary files a/IMG_1177.jpg and b/IMG_1177.jpg differ diff --git a/README.md b/README.md index 8d62b567..b3e94763 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,4 @@ ETH:0x98573ddb33cdddce480c3bc1f9279ccd88ca1e93 ### 欢迎为作者付一碗面钱 -一碗面钱   一碗面钱 +![微信](wx_pay.JPG) ![支付宝](IMG_1177.jpg) \ No newline at end of file diff --git a/binance/Binance.go b/binance/Binance.go index c7dbf991..9f7e1e71 100644 --- a/binance/Binance.go +++ b/binance/Binance.go @@ -6,6 +6,7 @@ import ( . "github.com/nntaoli-project/goex" "net/http" "net/url" + "sort" "strconv" "strings" "time" @@ -305,6 +306,8 @@ func (bn *Binance) GetDepth(size int, currencyPair CurrencyPair) (*Depth, error) } } + sort.Sort(sort.Reverse(depth.AskList)) + return depth, nil } @@ -492,7 +495,6 @@ func (bn *Binance) GetOneOrder(orderId string, currencyPair CurrencyPair) (*Orde ord.Amount = ToFloat64(respmap["origQty"].(string)) ord.Price = ToFloat64(respmap["price"].(string)) ord.DealAmount = ToFloat64(respmap["executedQty"]) - ord.AvgPrice = ord.Price // response no avg price , fill price ord.OrderTime = ToInt(respmap["time"]) cummulativeQuoteQty := ToFloat64(respmap["cummulativeQuoteQty"]) diff --git a/binance/BinanceFutures.go b/binance/BinanceFutures.go new file mode 100644 index 00000000..9c361810 --- /dev/null +++ b/binance/BinanceFutures.go @@ -0,0 +1,676 @@ +package binance + +import ( + "errors" + "fmt" + . "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "math" + "net/http" + "net/url" + "sort" + "strings" + "sync" + "time" +) + +type BaseResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` +} + +type AccountResponse struct { + FeeTier int `json:"feeTier"` + CanTrade bool `json:"canTrade"` + Assets []struct { + Asset string `json:"asset"` + WalletBalance float64 `json:"walletBalance,string"` + UnrealizedProfit float64 `json:"unrealizedProfit,string"` + MaintMargin float64 `json:"maintMargin,string"` + } `json:"assets"` +} + +type OrderInfoResponse struct { + BaseResponse + Symbol string `json:"symbol"` + Pair string `json:"pair"` + ClientOrderId string `json:"clientOrderId"` + OrderId int64 `json:"orderId"` + AvgPrice float64 `json:"avgPrice,string"` + ExecutedQty float64 `json:"executedQty,string"` + OrigQty float64 `json:"origQty,string"` + Price float64 `json:"price,string"` + Side string `json:"side"` + PositionSide string `json:"positionSide"` + Status string `json:"status"` + Type string `json:"type"` + Time int64 `json:"time"` + UpdateTime int64 `json:"updateTime"` +} + +type PositionRiskResponse struct { + Symbol string `json:"symbol"` + PositionAmt float64 `json:"positionAmt,string"` + EntryPrice float64 `json:"entryPrice,string"` + UnRealizedProfit float64 `json:"unRealizedProfit,string"` + LiquidationPrice float64 `json:"liquidationPrice,string"` + Leverage float64 `json:"leverage,string"` + MarginType string `json:"marginType"` + PositionSide string `json:"positionSide"` +} + +type SymbolInfo struct { + Symbol string + Pair string + ContractType string `json:"contractType"` + DeliveryDate int64 `json:"deliveryDate"` + ContractStatus string `json:"contractStatus"` + ContractSize int `json:"contractSize"` + PricePrecision int `json:"pricePrecision"` +} + +type BinanceFutures struct { + base *Binance + apikey string + exchangeInfo *struct { + Symbols []SymbolInfo `json:"symbols"` + } +} + +func NewBinanceFutures(config *APIConfig) *BinanceFutures { + if config.Endpoint == "" { + config.Endpoint = "https://dapi.binance.com" + } + + if config.HttpClient == nil { + config.HttpClient = http.DefaultClient + } + + bs := &BinanceFutures{ + apikey: config.ApiKey, + base: NewWithConfig(config), + } + + bs.base.apiV1 = config.Endpoint + "/dapi/v1/" + + go bs.GetExchangeInfo() + + return bs +} + +func (bs *BinanceFutures) SetBaseUri(uri string) { + bs.base.baseUrl = uri +} + +func (bs *BinanceFutures) GetExchangeName() string { + return BINANCE_FUTURES +} + +func (bs *BinanceFutures) GetFutureTicker(currencyPair CurrencyPair, contractType string) (*Ticker, error) { + symbol, err := bs.adaptToSymbol(currencyPair, contractType) + if err != nil { + return nil, err + } + + ticker24hrUri := bs.base.apiV1 + "ticker/24hr?symbol=" + symbol + tickerBookUri := bs.base.apiV1 + "ticker/bookTicker?symbol=" + symbol + + var ( + ticker24HrResp []interface{} + tickerBookResp []interface{} + err1 error + err2 error + wg = sync.WaitGroup{} + ) + + wg.Add(2) + + go func() { + defer wg.Done() + ticker24HrResp, err1 = HttpGet3(bs.base.httpClient, ticker24hrUri, map[string]string{}) + }() + + go func() { + defer wg.Done() + tickerBookResp, err2 = HttpGet3(bs.base.httpClient, tickerBookUri, map[string]string{}) + }() + + wg.Wait() + + if err1 != nil { + return nil, err1 + } + + if err2 != nil { + return nil, err2 + } + + if len(ticker24HrResp) == 0 { + return nil, errors.New("response is empty") + } + + if len(tickerBookResp) == 0 { + return nil, errors.New("response is empty") + } + + ticker24HrMap := ticker24HrResp[0].(map[string]interface{}) + tickerBookMap := tickerBookResp[0].(map[string]interface{}) + + var ticker Ticker + ticker.Pair = currencyPair + ticker.Date = ToUint64(tickerBookMap["time"]) + ticker.Last = ToFloat64(ticker24HrMap["lastPrice"]) + ticker.Buy = ToFloat64(tickerBookMap["bidPrice"]) + ticker.Sell = ToFloat64(tickerBookMap["askPrice"]) + ticker.High = ToFloat64(ticker24HrMap["highPrice"]) + ticker.Low = ToFloat64(ticker24HrMap["lowPrice"]) + ticker.Vol = ToFloat64(ticker24HrMap["volume"]) + + return &ticker, nil +} + +func (bs *BinanceFutures) GetFutureDepth(currencyPair CurrencyPair, contractType string, size int) (*Depth, error) { + symbol, err := bs.adaptToSymbol(currencyPair, contractType) + if err != nil { + return nil, err + } + + limit := 5 + if size <= 5 { + limit = 5 + } else if size <= 10 { + limit = 10 + } else if size <= 20 { + limit = 20 + } else if size <= 50 { + limit = 50 + } else if size <= 100 { + limit = 100 + } else if size <= 500 { + limit = 500 + } else { + limit = 1000 + } + + depthUri := bs.base.apiV1 + "depth?symbol=%s&limit=%d" + + ret, err := HttpGet(bs.base.httpClient, fmt.Sprintf(depthUri, symbol, limit)) + if err != nil { + return nil, err + } + logger.Debug(ret) + + var dep Depth + + dep.ContractType = contractType + dep.Pair = currencyPair + eT := int64(ret["E"].(float64)) + dep.UTime = time.Unix(0, eT*int64(time.Millisecond)) + + for _, item := range ret["asks"].([]interface{}) { + ask := item.([]interface{}) + dep.AskList = append(dep.AskList, DepthRecord{ + Price: ToFloat64(ask[0]), + Amount: ToFloat64(ask[1]), + }) + } + + for _, item := range ret["bids"].([]interface{}) { + bid := item.([]interface{}) + dep.BidList = append(dep.BidList, DepthRecord{ + Price: ToFloat64(bid[0]), + Amount: ToFloat64(bid[1]), + }) + } + + sort.Sort(sort.Reverse(dep.AskList)) + + return &dep, nil +} + +func (bs *BinanceFutures) GetFutureIndex(currencyPair CurrencyPair) (float64, error) { + panic("not supported.") +} + +func (bs *BinanceFutures) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAccount, error) { + accountUri := bs.base.apiV1 + "account" + param := url.Values{} + bs.base.buildParamsSigned(¶m) + + respData, err := HttpGet5(bs.base.httpClient, accountUri+"?"+param.Encode(), map[string]string{ + "X-MBX-APIKEY": bs.apikey}) + + if err != nil { + return nil, err + } + + logger.Debug(string(respData)) + + var ( + accountResp AccountResponse + futureAccounts FutureAccount + ) + + err = json.Unmarshal(respData, &accountResp) + if err != nil { + return nil, fmt.Errorf("response body: %s , %w", string(respData), err) + } + + futureAccounts.FutureSubAccounts = make(map[Currency]FutureSubAccount, 4) + for _, asset := range accountResp.Assets { + currency := NewCurrency(asset.Asset, "") + futureAccounts.FutureSubAccounts[currency] = FutureSubAccount{ + Currency: NewCurrency(asset.Asset, ""), + AccountRights: asset.WalletBalance, + KeepDeposit: asset.MaintMargin, + ProfitReal: 0, + ProfitUnreal: asset.UnrealizedProfit, + RiskRate: 0, + } + } + + return &futureAccounts, nil +} + +func (bs *BinanceFutures) PlaceFutureOrder(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (string, error) { + apiPath := "order" + symbol, err := bs.adaptToSymbol(currencyPair, contractType) + if err != nil { + return "", err + } + + param := url.Values{} + param.Set("symbol", symbol) + param.Set("newClientOrderId", GenerateOrderClientId(32)) + param.Set("quantity", amount) + param.Set("newOrderRespType", "ACK") + + if matchPrice == 0 { + param.Set("type", "LIMIT") + param.Set("timeInForce", "GTC") + param.Set("price", price) + } else { + param.Set("type", "MARKET") + } + + switch openType { + case OPEN_BUY, CLOSE_SELL: + param.Set("side", "BUY") + case OPEN_SELL, CLOSE_BUY: + param.Set("side", "SELL") + } + + bs.base.buildParamsSigned(¶m) + + resp, err := HttpPostForm2(bs.base.httpClient, fmt.Sprintf("%s%s", bs.base.apiV1, apiPath), param, + map[string]string{"X-MBX-APIKEY": bs.apikey}) + + if err != nil { + return "", err + } + + logger.Debug(string(resp)) + + var response struct { + BaseResponse + OrderId int64 `json:"orderId"` + } + + err = json.Unmarshal(resp, &response) + if err != nil { + return "", err + } + + if response.Code == 0 { + return fmt.Sprint(response.OrderId), nil + } + + return "", errors.New(response.Msg) +} + +func (bs *BinanceFutures) LimitFuturesOrder(currencyPair CurrencyPair, contractType, price, amount string, openType int, opt ...LimitOrderOptionalParameter) (*FutureOrder, error) { + orderId, err := bs.PlaceFutureOrder(currencyPair, contractType, price, amount, openType, 0, 10) + return &FutureOrder{ + OrderID2: orderId, + Currency: currencyPair, + ContractName: contractType, + Amount: ToFloat64(amount), + Price: ToFloat64(price), + OType: openType, + }, err +} + +func (bs *BinanceFutures) MarketFuturesOrder(currencyPair CurrencyPair, contractType, amount string, openType int) (*FutureOrder, error) { + orderId, err := bs.PlaceFutureOrder(currencyPair, contractType, "", amount, openType, 1, 10) + return &FutureOrder{ + OrderID2: orderId, + Currency: currencyPair, + ContractName: contractType, + Amount: ToFloat64(amount), + OType: openType, + }, err +} + +func (bs *BinanceFutures) FutureCancelOrder(currencyPair CurrencyPair, contractType, orderId string) (bool, error) { + apiPath := "order" + symbol, err := bs.adaptToSymbol(currencyPair, contractType) + if err != nil { + return false, err + } + + param := url.Values{} + param.Set("symbol", symbol) + if strings.HasPrefix(orderId, "goex") { + param.Set("origClientOrderId", orderId) + } else { + param.Set("orderId", orderId) + } + + bs.base.buildParamsSigned(¶m) + + reqUrl := fmt.Sprintf("%s%s?%s", bs.base.apiV1, apiPath, param.Encode()) + resp, err := HttpDeleteForm(bs.base.httpClient, reqUrl, url.Values{}, map[string]string{"X-MBX-APIKEY": bs.apikey}) + if err != nil { + logger.Errorf("request url: %s", reqUrl) + return false, err + } + + logger.Debug(string(resp)) + + return true, nil +} + +func (bs *BinanceFutures) GetFuturePosition(currencyPair CurrencyPair, contractType string) ([]FuturePosition, error) { + symbol, err := bs.adaptToSymbol(currencyPair, contractType) + if err != nil { + return nil, err + } + + params := url.Values{} + bs.base.buildParamsSigned(¶ms) + path := bs.base.apiV1 + "positionRisk?" + params.Encode() + + respBody, err := HttpGet5(bs.base.httpClient, path, map[string]string{"X-MBX-APIKEY": bs.apikey}) + if err != nil { + return nil, err + } + logger.Debug(string(respBody)) + + var ( + positionRiskResponse []PositionRiskResponse + positions []FuturePosition + ) + + err = json.Unmarshal(respBody, &positionRiskResponse) + if err != nil { + logger.Errorf("response body: %s", string(respBody)) + return nil, err + } + + for _, info := range positionRiskResponse { + if info.Symbol != symbol { + continue + } + + p := FuturePosition{ + LeverRate: info.Leverage, + Symbol: currencyPair, + ForceLiquPrice: info.LiquidationPrice, + } + + if info.PositionAmt > 0 { + p.BuyAmount = info.PositionAmt + p.BuyAvailable = info.PositionAmt + p.BuyPriceAvg = info.EntryPrice + p.BuyPriceCost = info.EntryPrice + p.BuyProfit = info.UnRealizedProfit + p.BuyProfitReal = info.UnRealizedProfit + } else if info.PositionAmt < 0 { + p.SellAmount = math.Abs(info.PositionAmt) + p.SellAvailable = math.Abs(info.PositionAmt) + p.SellPriceAvg = info.EntryPrice + p.SellPriceCost = info.EntryPrice + p.SellProfit = info.UnRealizedProfit + p.SellProfitReal = info.UnRealizedProfit + } + + positions = append(positions, p) + } + + return positions, nil +} + +func (bs *BinanceFutures) GetFutureOrders(orderIds []string, currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + panic("not supported.") +} + +func (bs *BinanceFutures) GetFutureOrder(orderId string, currencyPair CurrencyPair, contractType string) (*FutureOrder, error) { + apiPath := "order" + symbol, err := bs.adaptToSymbol(currencyPair, contractType) + if err != nil { + return nil, err + } + + param := url.Values{} + param.Set("symbol", symbol) + param.Set("orderId", orderId) + + bs.base.buildParamsSigned(¶m) + + reqUrl := fmt.Sprintf("%s%s?%s", bs.base.apiV1, apiPath, param.Encode()) + resp, err := HttpGet5(bs.base.httpClient, reqUrl, map[string]string{"X-MBX-APIKEY": bs.apikey}) + if err != nil { + logger.Errorf("request url: %s", reqUrl) + return nil, err + } + + logger.Debug(string(resp)) + + var getOrderInfoResponse OrderInfoResponse + err = json.Unmarshal(resp, &getOrderInfoResponse) + if err != nil { + logger.Errorf("response body: %s", string(resp)) + return nil, err + } + + return &FutureOrder{ + Currency: currencyPair, + ClientOid: getOrderInfoResponse.ClientOrderId, + OrderID2: fmt.Sprint(getOrderInfoResponse.OrderId), + Price: getOrderInfoResponse.Price, + Amount: getOrderInfoResponse.OrigQty, + AvgPrice: getOrderInfoResponse.AvgPrice, + DealAmount: getOrderInfoResponse.ExecutedQty, + OrderTime: getOrderInfoResponse.Time / 1000, + Status: bs.adaptStatus(getOrderInfoResponse.Status), + OType: bs.adaptOType(getOrderInfoResponse.Side, getOrderInfoResponse.PositionSide), + ContractName: contractType, + FinishedTime: getOrderInfoResponse.UpdateTime / 1000, + }, nil +} + +func (bs *BinanceFutures) GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + apiPath := "openOrders" + param := url.Values{} + + symbol, err := bs.adaptToSymbol(currencyPair, contractType) + if err != nil { + return nil, err + } + + param.Set("symbol", symbol) + bs.base.buildParamsSigned(¶m) + + respbody, err := HttpGet5(bs.base.httpClient, fmt.Sprintf("%s%s?%s", bs.base.apiV1, apiPath, param.Encode()), + map[string]string{ + "X-MBX-APIKEY": bs.apikey, + }) + if err != nil { + return nil, err + } + logger.Debug(string(respbody)) + + var ( + openOrderResponse []OrderInfoResponse + orders []FutureOrder + ) + + err = json.Unmarshal(respbody, &openOrderResponse) + if err != nil { + return nil, err + } + + for _, ord := range openOrderResponse { + orders = append(orders, FutureOrder{ + Currency: currencyPair, + ClientOid: ord.ClientOrderId, + OrderID: ord.OrderId, + OrderID2: fmt.Sprint(ord.OrderId), + Price: ord.Price, + Amount: ord.OrigQty, + AvgPrice: ord.AvgPrice, + DealAmount: ord.ExecutedQty, + Status: bs.adaptStatus(ord.Status), + OType: bs.adaptOType(ord.Side, ord.PositionSide), + ContractName: contractType, + FinishedTime: ord.UpdateTime / 1000, + OrderTime: ord.Time / 1000, + }) + } + + return orders, nil +} + +func (bs *BinanceFutures) GetFee() (float64, error) { + panic("not supported.") +} + +func (bs *BinanceFutures) GetContractValue(currencyPair CurrencyPair) (float64, error) { + switch currencyPair { + case BTC_USD: + return 100, nil + default: + return 10, nil + } +} + +func (bs *BinanceFutures) GetDeliveryTime() (int, int, int, int) { + panic("not supported.") +} + +func (bs *BinanceFutures) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { + panic("not supported.") +} + +func (bs *BinanceFutures) GetTrades(contractType string, currencyPair CurrencyPair, since int64) ([]Trade, error) { + panic("not supported.") +} + +func (bs *BinanceFutures) GetFutureEstimatedPrice(currencyPair CurrencyPair) (float64, error) { + panic("not supported.") +} + +func (bs *BinanceFutures) GetExchangeInfo() { + exchangeInfoUri := bs.base.apiV1 + "exchangeInfo" + ret, err := HttpGet5(bs.base.httpClient, exchangeInfoUri, map[string]string{}) + if err != nil { + logger.Error("[exchangeInfo] Http Error", err) + return + } + + err = json.Unmarshal(ret, &bs.exchangeInfo) + if err != nil { + logger.Error("json unmarshal response content error , content= ", string(ret)) + return + } + + logger.Debug("[ExchangeInfo]", bs.exchangeInfo) +} + +func (bs *BinanceFutures) adaptToSymbol(pair CurrencyPair, contractType string) (string, error) { + if contractType == THIS_WEEK_CONTRACT || contractType == NEXT_WEEK_CONTRACT { + return "", errors.New("binance only support contract quarter or bi_quarter") + } + + if contractType == SWAP_CONTRACT { + return fmt.Sprintf("%s_PERP", pair.AdaptUsdtToUsd().ToSymbol("")), nil + } + + if bs.exchangeInfo == nil || len(bs.exchangeInfo.Symbols) == 0 { + bs.GetExchangeInfo() + } + + for _, info := range bs.exchangeInfo.Symbols { + if info.ContractType != "PERPETUAL" && + info.ContractStatus == "TRADING" && + info.DeliveryDate <= time.Now().Unix()*1000 { + logger.Debugf("pair=%s , contractType=%s, delivery date = %d , now= %d", info.Pair, info.ContractType, info.DeliveryDate, time.Now().Unix()*1000) + bs.GetExchangeInfo() + } + + if info.Pair == pair.ToSymbol("") { + if info.ContractStatus != "TRADING" { + return "", errors.New("contract status " + info.ContractStatus) + } + + if info.ContractType == "CURRENT_QUARTER" && contractType == QUARTER_CONTRACT { + return info.Symbol, nil + } + + if info.ContractType == "NEXT_QUARTER" && contractType == BI_QUARTER_CONTRACT { + return info.Symbol, nil + } + } + } + + return "", errors.New("binance not support " + pair.ToSymbol("") + " " + contractType) +} + +func (bs *BinanceFutures) adaptStatus(status string) TradeStatus { + switch status { + case "NEW": + return ORDER_UNFINISH + case "CANCELED": + return ORDER_CANCEL + case "FILLED": + return ORDER_FINISH + case "PARTIALLY_FILLED": + return ORDER_PART_FINISH + case "PENDING_CANCEL": + return ORDER_CANCEL_ING + case "REJECTED": + return ORDER_REJECT + default: + return ORDER_UNFINISH + } +} + +func (bs *BinanceFutures) adaptOType(side string, positionSide string) int { + if positionSide == "BOTH" && side == "SELL" { + return OPEN_SELL + } + + if positionSide == "BOTH" && side == "BUY" { + return OPEN_BUY + } + + if positionSide == "LONG" { + switch side { + case "BUY": + return OPEN_BUY + default: + return CLOSE_BUY + } + } + + if positionSide == "SHORT" { + switch side { + case "SELL": + return OPEN_SELL + default: + return CLOSE_SELL + } + } + + return 0 +} diff --git a/binance/BinanceFutures_test.go b/binance/BinanceFutures_test.go new file mode 100644 index 00000000..6ba9fe2b --- /dev/null +++ b/binance/BinanceFutures_test.go @@ -0,0 +1,65 @@ +package binance + +import ( + "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "net/http" + "testing" +) + +var baDapi = NewBinanceFutures(&goex.APIConfig{ + HttpClient: http.DefaultClient, + ApiKey: "", + ApiSecretKey: "", +}) + +func init() { + logger.SetLevel(logger.DEBUG) +} + +func TestBinanceFutures_GetFutureDepth(t *testing.T) { + t.Log(baDapi.GetFutureDepth(goex.ETH_USD, goex.QUARTER_CONTRACT, 10)) +} + +func TestBinanceSwap_GetFutureTicker(t *testing.T) { + ticker, err := baDapi.GetFutureTicker(goex.LTC_USD, goex.SWAP_CONTRACT) + t.Log(err) + t.Logf("%+v", ticker) +} + +func TestBinance_GetExchangeInfo(t *testing.T) { + baDapi.GetExchangeInfo() +} + +func TestBinanceFutures_GetFutureUserinfo(t *testing.T) { + t.Log(baDapi.GetFutureUserinfo()) +} + +func TestBinanceFutures_PlaceFutureOrder(t *testing.T) { + //1044675677 + t.Log(baDapi.PlaceFutureOrder(goex.BTC_USD, goex.QUARTER_CONTRACT, "19990", "2", goex.OPEN_SELL, 0, 10)) +} + +func TestBinanceFutures_LimitFuturesOrder(t *testing.T) { + t.Log(baDapi.LimitFuturesOrder(goex.BTC_USD, goex.QUARTER_CONTRACT, "20001", "2", goex.OPEN_SELL)) +} + +func TestBinanceFutures_MarketFuturesOrder(t *testing.T) { + t.Log(baDapi.MarketFuturesOrder(goex.BTC_USD, goex.QUARTER_CONTRACT, "2", goex.OPEN_SELL)) +} + +func TestBinanceFutures_GetFutureOrder(t *testing.T) { + t.Log(baDapi.GetFutureOrder("1045208666", goex.BTC_USD, goex.QUARTER_CONTRACT)) +} + +func TestBinanceFutures_FutureCancelOrder(t *testing.T) { + t.Log(baDapi.FutureCancelOrder(goex.BTC_USD, goex.QUARTER_CONTRACT, "1045328328")) +} + +func TestBinanceFutures_GetFuturePosition(t *testing.T) { + t.Log(baDapi.GetFuturePosition(goex.BTC_USD, goex.QUARTER_CONTRACT)) +} + +func TestBinanceFutures_GetUnfinishFutureOrders(t *testing.T) { + t.Log(baDapi.GetUnfinishFutureOrders(goex.BTC_USD , goex.QUARTER_CONTRACT)) +} diff --git a/binance/BinanceSwap.go b/binance/BinanceSwap.go index 181168d6..ef590498 100644 --- a/binance/BinanceSwap.go +++ b/binance/BinanceSwap.go @@ -5,6 +5,7 @@ import ( "fmt" "net/url" "strconv" + "strings" "sync" "time" @@ -17,12 +18,14 @@ const ( type BinanceSwap struct { Binance + f *BinanceFutures } func NewBinanceSwap(config *APIConfig) *BinanceSwap { if config.Endpoint == "" { config.Endpoint = baseUrl } + bs := &BinanceSwap{ Binance: Binance{ baseUrl: config.Endpoint, @@ -31,6 +34,13 @@ func NewBinanceSwap(config *APIConfig) *BinanceSwap { secretKey: config.ApiSecretKey, httpClient: config.HttpClient, }, + f: NewBinanceFutures(&APIConfig{ + Endpoint: strings.ReplaceAll(config.Endpoint, "fapi", "dapi"), + HttpClient: config.HttpClient, + ApiKey: config.ApiKey, + ApiSecretKey: config.ApiSecretKey, + Lever: config.Lever, + }), } bs.setTimeOffset() return bs @@ -66,19 +76,19 @@ func (bs *BinanceSwap) setTimeOffset() error { return nil } -/** - *获取交割预估价 - */ func (bs *BinanceSwap) GetFutureEstimatedPrice(currencyPair CurrencyPair) (float64, error) { panic("not supported.") } -/** - * 期货行情 - * @param currency_pair btc_usd:比特币 ltc_usd :莱特币 - * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 - */ func (bs *BinanceSwap) GetFutureTicker(currency CurrencyPair, contractType string) (*Ticker, error) { + if contractType == SWAP_CONTRACT { + return bs.f.GetFutureTicker(currency.AdaptUsdtToUsd(), SWAP_CONTRACT) + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currency2 := bs.adaptCurrencyPair(currency) tickerPriceUri := bs.apiV1 + "ticker/price?symbol=" + currency2.ToSymbol("") tickerBookUri := bs.apiV1 + "ticker/bookTicker?symbol=" + currency2.ToSymbol("") @@ -113,15 +123,15 @@ func (bs *BinanceSwap) GetFutureTicker(currency CurrencyPair, contractType strin return &ticker, nil } -/** - * 期货深度 - * @param currencyPair btc_usd:比特币 ltc_usd :莱特币 - * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 - * @param size 获取深度档数 - * @return - */ - func (bs *BinanceSwap) GetFutureDepth(currency CurrencyPair, contractType string, size int) (*Depth, error) { + if contractType == SWAP_CONTRACT { + return bs.f.GetFutureDepth(currency.AdaptUsdtToUsd(), SWAP_CONTRACT, size) + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + if size <= 5 { size = 5 } else if size <= 10 { @@ -185,6 +195,14 @@ func (bs *BinanceSwap) GetFutureDepth(currency CurrencyPair, contractType string } func (bs *BinanceSwap) GetTrades(contractType string, currencyPair CurrencyPair, since int64) ([]Trade, error) { + if contractType == SWAP_CONTRACT { + return bs.f.GetTrades(SWAP_CONTRACT, currencyPair.AdaptUsdtToUsd(), since) + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + param := url.Values{} param.Set("symbol", bs.adaptCurrencyPair(currencyPair).ToSymbol("")) param.Set("limit", "500") @@ -219,10 +237,6 @@ func (bs *BinanceSwap) GetTrades(contractType string, currencyPair CurrencyPair, } -/** - * 期货指数 - * @param currencyPair btc_usd:比特币 ltc_usd :莱特币 - */ func (bs *BinanceSwap) GetFutureIndex(currencyPair CurrencyPair) (float64, error) { respmap, err := HttpGet(bs.httpClient, bs.apiV1+"premiumIndex?symbol="+bs.adaptCurrencyPair(currencyPair).ToSymbol("")) if err != nil { @@ -236,6 +250,11 @@ func (bs *BinanceSwap) GetFutureIndex(currencyPair CurrencyPair) (float64, error *全仓账户 */ func (bs *BinanceSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAccount, error) { + acc, err := bs.f.GetFutureUserinfo(currencyPair...) + if err != nil { + return nil, err + } + params := url.Values{} bs.buildParamsSigned(¶ms) path := bs.apiV1 + ACCOUNT_URI + params.Encode() @@ -243,11 +262,10 @@ func (bs *BinanceSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureA if err != nil { return nil, err } + if _, isok := respmap["code"]; isok == true { return nil, errors.New(respmap["msg"].(string)) } - acc := &FutureAccount{} - acc.FutureSubAccounts = make(map[Currency]FutureSubAccount) balances := respmap["assets"].([]interface{}) for _, v := range balances { @@ -261,10 +279,11 @@ func (bs *BinanceSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureA RiskRate: ToFloat64(vv["unrealizedProfit"]), } } + return acc, nil } -// transferType - 1: 现货账户向合约账户划转 2: 合约账户向现货账户划转 +//@deprecated please call the Wallet api func (bs *BinanceSwap) Transfer(currency Currency, transferType int, amount float64) (int64, error) { params := url.Values{} @@ -294,17 +313,25 @@ func (bs *BinanceSwap) PlaceFutureOrder(currencyPair CurrencyPair, contractType, return fOrder.OrderID2, err } -/** - * @deprecated - * 期货下单 - * @param currencyPair btc_usd:比特币 ltc_usd :莱特币 - * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 - * @param price 价格 - * @param amount 委托数量 - * @param openType 1:开多 2:开空 3:平多 4:平空 - * @param matchPrice 是否为对手价 0:不是 1:是 ,当取值为1时,price无效 - */ func (bs *BinanceSwap) PlaceFutureOrder2(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (*FutureOrder, error) { + if contractType == SWAP_CONTRACT { + orderId, err := bs.f.PlaceFutureOrder(currencyPair.AdaptUsdtToUsd(), contractType, price, amount, openType, matchPrice, leverRate) + return &FutureOrder{ + OrderID2: orderId, + Price: ToFloat64(price), + Amount: ToFloat64(amount), + Status: ORDER_UNFINISH, + Currency: currencyPair, + OType: openType, + LeverRate: leverRate, + ContractName: contractType, + }, err + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + fOrder := &FutureOrder{ Currency: currencyPair, ClientOid: GenerateOrderClientId(32), @@ -366,14 +393,15 @@ func (bs *BinanceSwap) MarketFuturesOrder(currencyPair CurrencyPair, contractTyp return bs.PlaceFutureOrder2(currencyPair, contractType, "0", amount, openType, 1, 10) } -/** - * 取消订单 - * @param symbol btc_usd:比特币 ltc_usd :莱特币 - * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 - * @param orderId 订单ID - - */ func (bs *BinanceSwap) FutureCancelOrder(currencyPair CurrencyPair, contractType, orderId string) (bool, error) { + if contractType == SWAP_CONTRACT { + return bs.f.FutureCancelOrder(currencyPair.AdaptUsdtToUsd(), contractType, orderId) + } + + if contractType != SWAP_USDT_CONTRACT { + return false, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currencyPair = bs.adaptCurrencyPair(currencyPair) path := bs.apiV1 + ORDER_URI params := url.Values{} @@ -403,6 +431,18 @@ func (bs *BinanceSwap) FutureCancelOrder(currencyPair CurrencyPair, contractType } func (bs *BinanceSwap) FutureCancelAllOrders(currencyPair CurrencyPair, contractType string) (bool, error) { + if contractType == SWAP_CONTRACT { + return false, errors.New("not support") + } + + if contractType == SWAP_CONTRACT { + return false, errors.New("not support") + } + + if contractType != SWAP_USDT_CONTRACT { + return false, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currencyPair = bs.adaptCurrencyPair(currencyPair) path := bs.apiV1 + "allOpenOrders" params := url.Values{} @@ -430,6 +470,10 @@ func (bs *BinanceSwap) FutureCancelAllOrders(currencyPair CurrencyPair, contract } func (bs *BinanceSwap) FutureCancelOrders(currencyPair CurrencyPair, contractType string, orderIdList []string) (bool, error) { + if contractType != SWAP_USDT_CONTRACT { + return false, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currencyPair = bs.adaptCurrencyPair(currencyPair) path := bs.apiV1 + "batchOrders" @@ -463,13 +507,15 @@ func (bs *BinanceSwap) FutureCancelOrders(currencyPair CurrencyPair, contractTyp return true, nil } -/** - * 用户持仓查询 - * @param symbol btc_usd:比特币 ltc_usd :莱特币 - * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 - * @return - */ func (bs *BinanceSwap) GetFuturePosition(currencyPair CurrencyPair, contractType string) ([]FuturePosition, error) { + if contractType == SWAP_CONTRACT { + return bs.f.GetFuturePosition(currencyPair.AdaptUsdtToUsd(), contractType) + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currencyPair1 := bs.adaptCurrencyPair(currencyPair) params := url.Values{} @@ -511,10 +557,15 @@ func (bs *BinanceSwap) GetFuturePosition(currencyPair CurrencyPair, contractType return positions, nil } -/** - *获取订单信息 - */ func (bs *BinanceSwap) GetFutureOrders(orderIds []string, currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + if contractType == SWAP_CONTRACT { + return nil, errors.New("not support") + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + if len(orderIds) == 0 { return nil, errors.New("orderIds is empty") } @@ -556,10 +607,15 @@ func (bs *BinanceSwap) GetFutureOrders(orderIds []string, currencyPair CurrencyP } -/** - *获取单个订单信息 - */ func (bs *BinanceSwap) GetFutureOrder(orderId string, currencyPair CurrencyPair, contractType string) (*FutureOrder, error) { + if contractType == SWAP_CONTRACT { + return bs.f.GetFutureOrder(orderId, currencyPair.AdaptUsdtToUsd(), contractType) + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currencyPair1 := bs.adaptCurrencyPair(currencyPair) params := url.Values{} @@ -652,10 +708,15 @@ func (bs *BinanceSwap) parseOrderStatus(sts string) TradeStatus { return orderStatus } -/** - *获取未完成订单信息 - */ func (bs *BinanceSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + if contractType == SWAP_CONTRACT { + return bs.f.GetUnfinishFutureOrders(currencyPair.AdaptUsdtToUsd(), contractType) + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currencyPair1 := bs.adaptCurrencyPair(currencyPair) params := url.Values{} @@ -685,31 +746,27 @@ func (bs *BinanceSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, contra return orders, nil } -/** - *获取交易费 - */ func (bs *BinanceSwap) GetFee() (float64, error) { panic("not supported.") } -/** - *获取每张合约价值 - */ func (bs *BinanceSwap) GetContractValue(currencyPair CurrencyPair) (float64, error) { panic("not supported.") } -/** - *获取交割时间 星期(0,1,2,3,4,5,6),小时,分,秒 - */ func (bs *BinanceSwap) GetDeliveryTime() (int, int, int, int) { panic("not supported.") } -/** - * 获取K线数据 - */ func (bs *BinanceSwap) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { + if contractType == SWAP_CONTRACT { + return bs.f.GetKlineRecords(contractType, currency.AdaptUsdtToUsd(), period, since, since) + } + + if contractType != SWAP_USDT_CONTRACT { + return nil, errors.New("contract is error,please incoming SWAP_CONTRACT or SWAP_USDT_CONTRACT") + } + currency2 := bs.adaptCurrencyPair(currency) params := url.Values{} params.Set("symbol", currency2.ToSymbol("")) diff --git a/binance/Wallet.go b/binance/Wallet.go new file mode 100644 index 00000000..5d2c7cb5 --- /dev/null +++ b/binance/Wallet.go @@ -0,0 +1,78 @@ +package binance + +import ( + "errors" + "fmt" + . "github.com/nntaoli-project/goex" + "net/url" +) + +type Wallet struct { + ba *Binance + conf *APIConfig +} + +func NewWallet(c *APIConfig) *Wallet { + return &Wallet{ba: NewWithConfig(c), conf: c} +} + +func (w *Wallet) GetAccount() (*Account, error) { + return nil, errors.New("not implement") +} + +func (w *Wallet) Withdrawal(param WithdrawParameter) (withdrawId string, err error) { + return "", errors.New("not implement") +} + +func (w *Wallet) Transfer(param TransferParameter) error { + transferUrl := w.conf.Endpoint + "/sapi/v1/futures/transfer" + + postParam := url.Values{} + postParam.Set("asset", param.Currency) + postParam.Set("amount", fmt.Sprint(param.Amount)) + + if param.From == SPOT && param.To == SWAP_USDT { + postParam.Set("type", "1") + } + + if param.From == SWAP_USDT && param.To == SPOT { + postParam.Set("type", "2") + } + + if param.From == SPOT && param.To == FUTURE { + postParam.Set("type", "3") + } + + if param.From == FUTURE && param.To == SPOT { + postParam.Set("type", "4") + } + + w.ba.buildParamsSigned(&postParam) + + resp, err := HttpPostForm2(w.ba.httpClient, transferUrl, postParam, + map[string]string{"X-MBX-APIKEY": w.ba.accessKey}) + + if err != nil { + return err + } + + respmap := make(map[string]interface{}) + err = json.Unmarshal(resp, &respmap) + if err != nil { + return err + } + + if respmap["tranId"] != nil && ToInt64(respmap["tranId"]) > 0 { + return nil + } + + return errors.New(string(resp)) +} + +func (w *Wallet) GetWithDrawHistory(currency *Currency) ([]DepositWithdrawHistory, error) { + return nil, errors.New("not implement") +} + +func (w *Wallet) GetDepositHistory(currency *Currency) ([]DepositWithdrawHistory, error) { + return nil, errors.New("not implement") +} diff --git a/binance/Wallet_test.go b/binance/Wallet_test.go new file mode 100644 index 00000000..b2e05fb9 --- /dev/null +++ b/binance/Wallet_test.go @@ -0,0 +1,26 @@ +package binance + +import ( + "github.com/nntaoli-project/goex" + "net/http" + "testing" +) + +var wallet *Wallet + +func init() { + wallet = NewWallet(&goex.APIConfig{ + HttpClient: http.DefaultClient, + ApiKey: "", + ApiSecretKey: "", + }) +} + +func TestWallet_Transfer(t *testing.T) { + t.Log(wallet.Transfer(goex.TransferParameter{ + Currency: "USDT", + From: goex.SPOT, + To: goex.SWAP_USDT, + Amount: 100, + })) +} diff --git a/builder/APIBuilder.go b/builder/APIBuilder.go index ed304c83..b6aaed2c 100644 --- a/builder/APIBuilder.go +++ b/builder/APIBuilder.go @@ -280,6 +280,13 @@ func (builder *APIBuilder) BuildFuture(exName string) (api FutureRestAPI) { Endpoint: builder.futuresEndPoint, ApiKey: builder.apiKey, ApiSecretKey: builder.secretkey}) + case HBDM_SWAP: + return huobi.NewHbdmSwap(&APIConfig{ + HttpClient: builder.client, + Endpoint: builder.endPoint, + ApiKey: builder.apiKey, + ApiSecretKey: builder.secretkey, + }) case OKEX_SWAP: return okex.NewOKEx(&APIConfig{ HttpClient: builder.client, @@ -296,13 +303,20 @@ func (builder *APIBuilder) BuildFuture(exName string) (api FutureRestAPI) { ApiSecretKey: builder.secretkey, }) - case BINANCE, BINANCE_SWAP: + case BINANCE_SWAP: return binance.NewBinanceSwap(&APIConfig{ HttpClient: builder.client, Endpoint: builder.futuresEndPoint, ApiKey: builder.apiKey, ApiSecretKey: builder.secretkey, }) + case BINANCE, BINANCE_FUTURES: + return binance.NewBinanceFutures(&APIConfig{ + HttpClient: builder.client, + Endpoint: builder.futuresEndPoint, + ApiKey: builder.apiKey, + ApiSecretKey: builder.secretkey, + }) default: println(fmt.Sprintf("%s not support future", exName)) @@ -342,6 +356,20 @@ func (builder *APIBuilder) BuildWallet(exName string) (WalletApi, error) { ApiSecretKey: builder.secretkey, ApiPassphrase: builder.apiPassphrase, }).OKExWallet, nil + case HUOBI_PRO: + return huobi.NewWallet(&APIConfig{ + HttpClient: builder.client, + Endpoint: builder.endPoint, + ApiKey: builder.apiKey, + ApiSecretKey: builder.secretkey, + }), nil + case BINANCE: + return binance.NewWallet(&APIConfig{ + HttpClient: builder.client, + Endpoint: builder.endPoint, + ApiKey: builder.apiKey, + ApiSecretKey: builder.secretkey, + }), nil } return nil, errors.New("not support the wallet api for " + exName) } diff --git a/huobi/Hbdm.go b/huobi/Hbdm.go index df17e3a7..50bd7143 100644 --- a/huobi/Hbdm.go +++ b/huobi/Hbdm.go @@ -744,6 +744,7 @@ func (dm *Hbdm) doRequest(path string, params *url.Values, data interface{}) err return err } + logger.Debugf("response body: %s", string(resp)) //log.Println(string(resp)) err = json.Unmarshal(resp, &ret) if err != nil { diff --git a/huobi/Hbdm_Swap.go b/huobi/Hbdm_Swap.go new file mode 100644 index 00000000..3c9df609 --- /dev/null +++ b/huobi/Hbdm_Swap.go @@ -0,0 +1,451 @@ +package huobi + +import ( + "encoding/json" + "errors" + "fmt" + . "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "net/url" + "sort" + "time" +) + +type HbdmSwap struct { + base *Hbdm + c *APIConfig +} + +const ( + getSwapContractInfoApiPath = "/swap-ex/v1/swap_contract_info" + tickerApiPath = "/swap-ex/market/detail/merged" + marketApiPath = "/swap-ex/market/depth" + klineApiPath = "/swap-api/market/history/kline" + accountApiPath = "/swap-api/v1/swap_account_info" + placeOrderApiPath = "/swap-api/v1/swap_order" + getPositionApiPath = "/swap-api/v1/swap_position_info" + cancelOrderApiPath = "/swap-api/v1/swap_cancel" + getOpenOrdersApiPath = "/swap-api/v1/swap_openorders" + getOrderInfoApiPath = "/swap-api/v1/swap_order_info" +) + +func NewHbdmSwap(c *APIConfig) *HbdmSwap { + if c.Lever <= 0 { + c.Lever = 10 + } + + return &HbdmSwap{ + base: NewHbdm(c), + c: c, + } +} + +func (swap *HbdmSwap) GetExchangeName() string { + return HBDM_SWAP +} + +func (swap *HbdmSwap) GetFutureTicker(currencyPair CurrencyPair, contractType string) (*Ticker, error) { + tickerUrl := fmt.Sprintf("%s%s?contract_code=%s", swap.base.config.Endpoint, tickerApiPath, currencyPair.ToSymbol("-")) + responseBody, err := HttpGet5(swap.base.config.HttpClient, tickerUrl, map[string]string{}) + if err != nil { + return nil, err + } + logger.Debugf("response body: %s", string(responseBody)) + + var ( + tickResponse struct { + BaseResponse + Tick struct { + Id int64 `json:"id"` + Vol float64 `json:"vol,string"` + Count int64 `json:"count"` + Open float64 `json:"open,string"` + Close float64 `json:"close,string"` + Low float64 `json:"low,string"` + High float64 `json:"high,string"` + Amount float64 `json:"amount,string"` + Ask []float64 `json:"ask"` + Bid []float64 `json:"bid"` + Ts int64 `json:"ts"` + } `json:"tick"` + } + ) + + err = json.Unmarshal(responseBody, &tickResponse) + if err != nil { + return nil, err + } + + if tickResponse.Status != "ok" { + return nil, errors.New(string(responseBody)) + } + + return &Ticker{ + Pair: currencyPair, + Last: 0, + Buy: tickResponse.Tick.Bid[0], + Sell: tickResponse.Tick.Ask[0], + High: tickResponse.Tick.High, + Low: tickResponse.Tick.Low, + Vol: tickResponse.Tick.Vol, + Date: uint64(tickResponse.Tick.Ts), + }, nil +} + +func (swap *HbdmSwap) GetFutureDepth(currencyPair CurrencyPair, contractType string, size int) (*Depth, error) { + step := 0 + if size <= 20 { + step = 6 + } + depthUrl := fmt.Sprintf("%s%s?contract_code=%s&type=step%d", swap.base.config.Endpoint, marketApiPath, currencyPair.ToSymbol("-"), step) + responseBody, err := HttpGet5(swap.base.config.HttpClient, depthUrl, map[string]string{}) + if err != nil { + return nil, err + } + logger.Debugf("response body: %s", string(responseBody)) + + var ( + dep Depth + tickResponse struct { + BaseResponse + Tick struct { + Id int64 `json:"id"` + Ts int64 `json:"ts"` + Bids [][]interface{} `json:"bids"` + Asks [][]interface{} `json:"asks"` + } `json:"tick"` + } + ) + + err = json.Unmarshal(responseBody, &tickResponse) + if err != nil { + return nil, err + } + + if tickResponse.Status != "ok" { + return nil, errors.New(string(responseBody)) + } + + dep.Pair = currencyPair + dep.ContractType = contractType + dep.UTime = time.Unix(0, tickResponse.Ts*int64(time.Millisecond)) + + for i, item := range tickResponse.Tick.Bids { + if i >= size { + break + } + dep.BidList = append(dep.BidList, DepthRecord{ + Price: ToFloat64(item[0]), + Amount: ToFloat64(item[1]), + }) + } + + for i, item := range tickResponse.Tick.Asks { + if i >= size { + break + } + dep.AskList = append(dep.AskList, DepthRecord{ + Price: ToFloat64(item[0]), + Amount: ToFloat64(item[1]), + }) + } + + sort.Sort(sort.Reverse(dep.AskList)) + + return &dep, nil +} + +func (swap *HbdmSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAccount, error) { + var accountInfoResponse []struct { + Symbol string `json:"symbol"` + MarginBalance float64 `json:"margin_balance"` + MarginPosition float64 `json:"margin_position"` + MarginFrozen float64 `json:"margin_frozen"` + MarginAvailable float64 `json:"margin_available"` + ProfitReal float64 `json:"profit_real"` + ProfitUnreal float64 `json:"profit_unreal"` + RiskRate float64 `json:"risk_rate"` + LiquidationPrice float64 `json:"liquidation_price"` + } + + param := url.Values{} + if len(currencyPair) > 0 { + param.Set("contract_code", currencyPair[0].ToSymbol("-")) + } + + err := swap.base.doRequest(accountApiPath, ¶m, &accountInfoResponse) + if err != nil { + return nil, err + } + + var futureAccount FutureAccount + futureAccount.FutureSubAccounts = make(map[Currency]FutureSubAccount, 4) + + for _, acc := range accountInfoResponse { + currency := NewCurrency(acc.Symbol, "") + futureAccount.FutureSubAccounts[currency] = FutureSubAccount{ + Currency: currency, + AccountRights: acc.MarginBalance, + KeepDeposit: acc.MarginPosition, + ProfitReal: acc.ProfitReal, + ProfitUnreal: acc.ProfitUnreal, + RiskRate: acc.RiskRate, + } + } + + return &futureAccount, nil +} + +func (swap *HbdmSwap) PlaceFutureOrder(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (string, error) { + param := url.Values{} + param.Set("contract_code", currencyPair.ToSymbol("-")) + param.Set("client_order_id", fmt.Sprint(time.Now().UnixNano())) + param.Set("price", price) + param.Set("volume", amount) + param.Set("lever_rate", fmt.Sprintf("%.0f", leverRate)) + + direction, offset := swap.base.adaptOpenType(openType) + param.Set("direction", direction) + param.Set("offset", offset) + logger.Info(direction, offset) + + if matchPrice == 1 { + param.Set("order_price_type", "opponent") + } else { + param.Set("order_price_type", "limit") + } + + var orderResponse struct { + OrderId string `json:"order_id_str"` + ClientOrderId int64 `json:"client_order_id"` + } + + err := swap.base.doRequest(placeOrderApiPath, ¶m, &orderResponse) + if err != nil { + return "", err + } + + return orderResponse.OrderId, nil +} + +func (swap *HbdmSwap) LimitFuturesOrder(currencyPair CurrencyPair, contractType, price, amount string, openType int, opt ...LimitOrderOptionalParameter) (*FutureOrder, error) { + orderId, err := swap.PlaceFutureOrder(currencyPair, contractType, price, amount, openType, 0, swap.c.Lever) + return &FutureOrder{ + Currency: currencyPair, + OrderID2: orderId, + Amount: ToFloat64(amount), + Price: ToFloat64(price), + OType: openType, + ContractName: contractType, + }, err +} + +func (swap *HbdmSwap) MarketFuturesOrder(currencyPair CurrencyPair, contractType, amount string, openType int) (*FutureOrder, error) { + orderId, err := swap.PlaceFutureOrder(currencyPair, contractType, "", amount, openType, 1, 10) + return &FutureOrder{ + Currency: currencyPair, + OrderID2: orderId, + Amount: ToFloat64(amount), + OType: openType, + ContractName: contractType, + }, err +} + +func (swap *HbdmSwap) FutureCancelOrder(currencyPair CurrencyPair, contractType, orderId string) (bool, error) { + param := url.Values{} + param.Set("order_id", orderId) + param.Set("contract_code", currencyPair.ToSymbol("-")) + + var cancelResponse struct { + Errors []struct { + ErrMsg string `json:"err_msg"` + Successes string `json:"successes,omitempty"` + } `json:"errors"` + } + + err := swap.base.doRequest(cancelOrderApiPath, ¶m, &cancelResponse) + if err != nil { + return false, err + } + + if len(cancelResponse.Errors) > 0 { + return false, errors.New(cancelResponse.Errors[0].ErrMsg) + } + + return true, nil +} + +func (swap *HbdmSwap) GetFuturePosition(currencyPair CurrencyPair, contractType string) ([]FuturePosition, error) { + param := url.Values{} + param.Set("contract_code", currencyPair.ToSymbol("-")) + + var ( + tempPositionMap map[string]*FuturePosition + futuresPositions []FuturePosition + positionResponse []struct { + Symbol string + ContractCode string `json:"contract_code"` + Volume float64 `json:"volume"` + Available float64 `json:"available"` + CostOpen float64 `json:"cost_open"` + CostHold float64 `json:"cost_hold"` + ProfitUnreal float64 `json:"profit_unreal"` + ProfitRate float64 `json:"profit_rate"` + Profit float64 `json:"profit"` + PositionMargin float64 `json:"position_margin"` + LeverRate float64 `json:"lever_rate"` + Direction string `json:"direction"` + } + ) + + err := swap.base.doRequest(getPositionApiPath, ¶m, &positionResponse) + if err != nil { + return nil, err + } + + futuresPositions = make([]FuturePosition, 0, 2) + tempPositionMap = make(map[string]*FuturePosition, 2) + + for _, pos := range positionResponse { + if tempPositionMap[pos.ContractCode] == nil { + tempPositionMap[pos.ContractCode] = new(FuturePosition) + } + switch pos.Direction { + case "sell": + tempPositionMap[pos.ContractCode].ContractType = pos.ContractCode + tempPositionMap[pos.ContractCode].Symbol = NewCurrencyPair3(pos.ContractCode, "-") + tempPositionMap[pos.ContractCode].SellAmount = pos.Volume + tempPositionMap[pos.ContractCode].SellAvailable = pos.Available + tempPositionMap[pos.ContractCode].SellPriceAvg = pos.CostOpen + tempPositionMap[pos.ContractCode].SellPriceCost = pos.CostHold + tempPositionMap[pos.ContractCode].SellProfitReal = pos.ProfitRate + tempPositionMap[pos.ContractCode].SellProfit = pos.Profit + case "buy": + tempPositionMap[pos.ContractCode].ContractType = pos.ContractCode + tempPositionMap[pos.ContractCode].Symbol = NewCurrencyPair3(pos.ContractCode, "-") + tempPositionMap[pos.ContractCode].BuyAmount = pos.Volume + tempPositionMap[pos.ContractCode].BuyAvailable = pos.Available + tempPositionMap[pos.ContractCode].BuyPriceAvg = pos.CostOpen + tempPositionMap[pos.ContractCode].BuyPriceCost = pos.CostHold + tempPositionMap[pos.ContractCode].BuyProfitReal = pos.ProfitRate + tempPositionMap[pos.ContractCode].BuyProfit = pos.Profit + } + } + + for _, pos := range tempPositionMap { + futuresPositions = append(futuresPositions, *pos) + } + + return futuresPositions, nil +} + +func (swap *HbdmSwap) GetFutureOrders(orderIds []string, currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + return nil, nil +} + +func (swap *HbdmSwap) GetFutureOrder(orderId string, currencyPair CurrencyPair, contractType string) (*FutureOrder, error) { + var ( + orderInfoResponse []OrderInfo + param = url.Values{} + ) + + param.Set("contract_code", currencyPair.ToSymbol("-")) + param.Set("order_id", orderId) + + err := swap.base.doRequest(getOrderInfoApiPath, ¶m, &orderInfoResponse) + if err != nil { + return nil, err + } + + if len(orderInfoResponse) == 0 { + return nil, errors.New("not found") + } + + orderInfo := orderInfoResponse[0] + + return &FutureOrder{ + Currency: currencyPair, + ClientOid: fmt.Sprint(orderInfo.ClientOrderId), + OrderID2: fmt.Sprint(orderInfo.OrderId), + Price: orderInfo.Price, + Amount: orderInfo.Volume, + AvgPrice: orderInfo.TradeAvgPrice, + DealAmount: orderInfo.TradeVolume, + OrderID: orderInfo.OrderId, + Status: swap.base.adaptOrderStatus(orderInfo.Status), + OType: swap.base.adaptOffsetDirectionToOpenType(orderInfo.Offset, orderInfo.Direction), + LeverRate: orderInfo.LeverRate, + Fee: orderInfo.Fee, + ContractName: orderInfo.ContractCode, + OrderTime: orderInfo.CreatedAt, + }, nil +} + +func (swap *HbdmSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + param := url.Values{} + param.Set("contract_code", currencyPair.ToSymbol("-")) + param.Set("page_size", "50") + + var openOrderResponse struct { + Orders []OrderInfo + } + + err := swap.base.doRequest(getOpenOrdersApiPath, ¶m, &openOrderResponse) + if err != nil { + return nil, err + } + + openOrders := make([]FutureOrder, 0, len(openOrderResponse.Orders)) + for _, ord := range openOrderResponse.Orders { + openOrders = append(openOrders, FutureOrder{ + Currency: currencyPair, + ClientOid: fmt.Sprint(ord.ClientOrderId), + OrderID2: fmt.Sprint(ord.OrderId), + Price: ord.Price, + Amount: ord.Volume, + AvgPrice: ord.TradeAvgPrice, + DealAmount: ord.TradeVolume, + OrderID: ord.OrderId, + Status: swap.base.adaptOrderStatus(ord.Status), + OType: swap.base.adaptOffsetDirectionToOpenType(ord.Offset, ord.Direction), + LeverRate: ord.LeverRate, + Fee: ord.Fee, + OrderTime: ord.CreatedAt, + }) + } + + return openOrders, nil +} + +func (swap *HbdmSwap) GetContractValue(currencyPair CurrencyPair) (float64, error) { + switch currencyPair { + case BTC_USD, BTC_USDT: + return 100, nil + default: + return 0, nil + } +} + +func (swap *HbdmSwap) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { + panic("not implement") +} + +func (swap *HbdmSwap) GetTrades(contractType string, currencyPair CurrencyPair, since int64) ([]Trade, error) { + panic("not implement") +} + +func (swap *HbdmSwap) GetFee() (float64, error) { + panic("not implement") +} + +func (swap *HbdmSwap) GetFutureIndex(currencyPair CurrencyPair) (float64, error) { + panic("not implement") +} + +func (swap *HbdmSwap) GetDeliveryTime() (int, int, int, int) { + panic("not implement") +} + +func (swap *HbdmSwap) GetFutureEstimatedPrice(currencyPair CurrencyPair) (float64, error) { + panic("not implement") +} diff --git a/huobi/Hbdm_Swap_test.go b/huobi/Hbdm_Swap_test.go new file mode 100644 index 00000000..27b899fe --- /dev/null +++ b/huobi/Hbdm_Swap_test.go @@ -0,0 +1,55 @@ +package huobi + +import ( + "github.com/nntaoli-project/goex" + "net/http" + "testing" +) + +var swap *HbdmSwap + +func init() { + swap = NewHbdmSwap(&goex.APIConfig{ + HttpClient: http.DefaultClient, + Endpoint: "https://api.btcgateway.pro", + ApiKey: "", + ApiSecretKey: "", + Lever: 5, + }) +} + +func TestHbdmSwap_GetFutureTicker(t *testing.T) { + t.Log(swap.GetFutureTicker(goex.BTC_USD, goex.SWAP_CONTRACT)) +} + +func TestHbdmSwap_GetFutureDepth(t *testing.T) { + dep, err := swap.GetFutureDepth(goex.BTC_USD, goex.SWAP_CONTRACT, 5) + t.Log(err) + t.Log(dep.AskList) + t.Log(dep.BidList) +} + +func TestHbdmSwap_GetFutureUserinfo(t *testing.T) { + t.Log(swap.GetFutureUserinfo(goex.NewCurrencyPair2("DOT_USD"))) +} + +func TestHbdmSwap_GetFuturePosition(t *testing.T) { + t.Log(swap.GetFuturePosition(goex.NewCurrencyPair2("DOT_USD"), goex.SWAP_CONTRACT)) +} + +func TestHbdmSwap_LimitFuturesOrder(t *testing.T) { + //784115347040780289 + t.Log(swap.LimitFuturesOrder(goex.NewCurrencyPair2("DOT_USD"), goex.SWAP_CONTRACT, "6.5", "1", goex.OPEN_SELL)) +} + +func TestHbdmSwap_FutureCancelOrder(t *testing.T) { + t.Log(swap.FutureCancelOrder(goex.NewCurrencyPair2("DOT_USD"), goex.SWAP_CONTRACT, "784118017750929408")) +} + +func TestHbdmSwap_GetUnfinishFutureOrders(t *testing.T) { + t.Log(swap.GetUnfinishFutureOrders(goex.NewCurrencyPair2("DOT_USD"), goex.SWAP_CONTRACT)) +} + +func TestHbdmSwap_GetFutureOrder(t *testing.T) { + t.Log(swap.GetFutureOrder("784118017750929408", goex.NewCurrencyPair2("DOT_USD"), goex.SWAP_CONTRACT)) +} diff --git a/huobi/Wallet.go b/huobi/Wallet.go new file mode 100644 index 00000000..997af90b --- /dev/null +++ b/huobi/Wallet.go @@ -0,0 +1,124 @@ +package huobi + +import ( + "encoding/json" + "errors" + "fmt" + . "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "net/url" + "strings" +) + +type Wallet struct { + pro *HuoBiPro +} + +func NewWallet(c *APIConfig) *Wallet { + return &Wallet{pro: NewHuobiWithConfig(c)} +} + +//获取钱包资产 +func (w *Wallet) GetAccount() (*Account, error) { + return nil, errors.New("not implement") +} + +func (w *Wallet) Withdrawal(param WithdrawParameter) (withdrawId string, err error) { + return "", errors.New("not implement") +} + +func (w *Wallet) Transfer(param TransferParameter) error { + if param.From == SUB_ACCOUNT || param.To == SUB_ACCOUNT || + param.From == SPOT_MARGIN || param.To == SPOT_MARGIN { + return errors.New("not implements") + } + + httpParam := url.Values{} + httpParam.Set("currency", strings.ToLower(param.Currency)) + httpParam.Set("amount", FloatToString(param.Amount, 8)) + + path := "" + + if (param.From == SPOT && param.To == FUTURE) || + (param.From == FUTURE && param.To == SPOT) { + path = "/v1/futures/transfer" + } + + if param.From == SWAP || param.From == SWAP_USDT || + param.To == SWAP || param.To == SWAP_USDT { + path = "/v2/account/transfer" + } + + if param.From == SPOT && param.To == FUTURE { + httpParam.Set("type", "pro-to-futures") + } + + if param.From == FUTURE && param.To == SPOT { + httpParam.Set("type", "futures-to-pro") + } + + if param.From == SPOT && param.To == SWAP { + httpParam.Set("from", "spot") + httpParam.Set("to", "swap") + } + + if param.From == SPOT && param.To == SWAP_USDT { + httpParam.Set("currency", "usdt") + httpParam.Set("from", "spot") + httpParam.Set("to", "linear-swap") + httpParam.Set("margin-account", fmt.Sprintf("%s-usdt", strings.ToLower(param.Currency))) + } + + if param.From == SWAP && param.To == SPOT { + httpParam.Set("from", "swap") + httpParam.Set("to", "spot") + } + + if param.From == SWAP_USDT && param.To == SPOT { + httpParam.Set("currency", "usdt") + httpParam.Set("from", "linear-swap") + httpParam.Set("to", "spot") + httpParam.Set("margin-account", + fmt.Sprintf("%s-usdt", strings.ToLower(param.Currency))) + } + + w.pro.buildPostForm("POST", path, &httpParam) + + postJsonParam, _ := ValuesToJson(httpParam) + responseBody, err := HttpPostForm3(w.pro.httpClient, + fmt.Sprintf("%s%s?%s", w.pro.baseUrl, path, httpParam.Encode()), + string(postJsonParam), + map[string]string{"Content-Type": "application/json", "Accept-Language": "zh-cn"}) + + if err != nil { + return err + } + + logger.Debugf("[response body] %s", string(responseBody)) + + var responseRet map[string]interface{} + + err = json.Unmarshal(responseBody, &responseRet) + if err != nil { + return err + } + + if responseRet["status"] != nil && + responseRet["status"].(string) == "ok" { + return nil + } + + if responseRet["code"] != nil && responseRet["code"].(float64) == 200 { + return nil + } + + return errors.New(string(responseBody)) +} + +func (w *Wallet) GetWithDrawHistory(currency *Currency) ([]DepositWithdrawHistory, error) { + return nil, errors.New("not implement") +} + +func (w *Wallet) GetDepositHistory(currency *Currency) ([]DepositWithdrawHistory, error) { + return nil, errors.New("not implement") +} diff --git a/huobi/Wallet_test.go b/huobi/Wallet_test.go new file mode 100644 index 00000000..0d840fed --- /dev/null +++ b/huobi/Wallet_test.go @@ -0,0 +1,25 @@ +package huobi + +import ( + "github.com/nntaoli-project/goex" + "testing" +) + +var wallet *Wallet + +func init() { + wallet = NewWallet(&goex.APIConfig{ + HttpClient: httpProxyClient, + ApiKey: "", + ApiSecretKey: "", + }) +} + +func TestWallet_Transfer(t *testing.T) { + t.Log(wallet.Transfer(goex.TransferParameter{ + Currency: "BTC", + From: goex.SWAP_USDT, + To: goex.SPOT, + Amount: 11, + })) +} diff --git a/okex/OKExSwap.go b/okex/OKExSwap.go index e630398c..aeebe046 100644 --- a/okex/OKExSwap.go +++ b/okex/OKExSwap.go @@ -4,8 +4,10 @@ import ( "encoding/json" "errors" "fmt" + "github.com/nntaoli-project/goex/internal/logger" "net/url" "strconv" + "strings" "time" . "github.com/nntaoli-project/goex" @@ -185,39 +187,43 @@ func (ok *OKExSwap) GetFutureDepth(currencyPair CurrencyPair, contractType strin } func (ok *OKExSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAccount, error) { - var infos SwapAccounts + var ( + err error + infos SwapAccounts + ) + + if len(currencyPair) == 1 { + accountInfo, err := ok.GetFutureAccountInfo(currencyPair[0]) + if err != nil { + return nil, err + } + + if accountInfo == nil { + return nil, errors.New("api return info is empty") + } - err := ok.OKEx.DoRequest("GET", GET_ACCOUNTS, "", &infos) + infos.Info = append(infos.Info, *accountInfo) + + goto wrapperF + } + + err = ok.OKEx.DoRequest("GET", GET_ACCOUNTS, "", &infos) if err != nil { return nil, err } //log.Println(infos) +wrapperF: acc := FutureAccount{} acc.FutureSubAccounts = make(map[Currency]FutureSubAccount, 2) for _, account := range infos.Info { subAcc := FutureSubAccount{AccountRights: account.Equity, - KeepDeposit: account.Margin, ProfitReal: account.RealizedPnl, ProfitUnreal: account.UnrealizedPnl, RiskRate: account.MarginRatio} - switch account.InstrumentId { - case BTC_USD_SWAP: - subAcc.Currency = BTC - case LTC_USD_SWAP: - subAcc.Currency = LTC - case ETH_USD_SWAP: - subAcc.Currency = ETH - case ETC_USD_SWAP: - subAcc.Currency = ETC - case BCH_USD_SWAP: - subAcc.Currency = BCH - case BSV_USD_SWAP: - subAcc.Currency = BSV - case EOS_USD_SWAP: - subAcc.Currency = EOS - case XRP_USD_SWAP: - subAcc.Currency = XRP - default: - subAcc.Currency = UNKNOWN + KeepDeposit: account.Margin, ProfitReal: account.RealizedPnl, + ProfitUnreal: account.UnrealizedPnl, RiskRate: account.MarginRatio} + meta := strings.Split(account.InstrumentId, "-") + if len(meta) > 0 { + subAcc.Currency = NewCurrency(meta[0], "") } acc.FutureSubAccounts[subAcc.Currency] = subAcc } @@ -225,34 +231,17 @@ func (ok *OKExSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAcco return &acc, nil } -type AccountInfo struct { - Info struct { - Currency string `json:"currency"` - Equity float64 `json:"equity,string"` - FixedBalance float64 `json:"fixed_balance,string"` - InstrumentID string `json:"instrument_id"` - MaintMarginRatio float64 `json:"maint_margin_ratio,string"` - Margin float64 `json:"margin,string"` - MarginFrozen float64 `json:"margin_frozen,string"` - MarginMode string `json:"margin_mode"` - MarginRatio float64 `json:"margin_ratio,string"` - MaxWithdraw float64 `json:"max_withdraw,string"` - RealizedPnl float64 `json:"realized_pnl,string"` - Timestamp string `json:"timestamp"` - TotalAvailBalance float64 `json:"total_avail_balance,string"` - Underlying string `json:"underlying"` - UnrealizedPnl float64 `json:"unrealized_pnl,string"` - } `json:"info"` -} - -func (ok *OKExSwap) GetFutureAccountInfo(currency CurrencyPair) (*AccountInfo, error) { - var infos AccountInfo +func (ok *OKExSwap) GetFutureAccountInfo(currency CurrencyPair) (*SwapAccountInfo, error) { + var infos struct { + Info SwapAccountInfo `json:"info"` + } err := ok.OKEx.DoRequest("GET", fmt.Sprintf("/api/swap/v3/%s/accounts", ok.adaptContractType(currency)), "", &infos) if err != nil { return nil, err } - return &infos, nil + + return &infos.Info, nil } /* @@ -294,7 +283,9 @@ func (ok *OKExSwap) PlaceFutureOrder2(currencyPair CurrencyPair, contractType, p Price: price, MatchPrice: fmt.Sprint(matchPrice), Type: fmt.Sprint(openType), - Size: amount}, + Size: amount, + OrderType: "0", + }, ok.adaptContractType(currencyPair), } @@ -328,10 +319,12 @@ func (ok *OKExSwap) PlaceFutureOrder2(currencyPair CurrencyPair, contractType, p err := ok.DoRequest("POST", PLACE_ORDER, reqBody, &resp) if err != nil { + logger.Errorf("[param] %s", param) return fOrder, err } if resp.ErrorMessage != "" { + logger.Errorf("[param] %s", param) return fOrder, errors.New(fmt.Sprintf("%s:%s", resp.ErrorCode, resp.ErrorMessage)) } diff --git a/wx_pay.JPG b/wx_pay.JPG index 214968df..72003783 100644 Binary files a/wx_pay.JPG and b/wx_pay.JPG differ