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