github.com/nntaoli-project/goex@v1.3.3/okex/OKExSwap.go (about)

     1  package okex
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/nntaoli-project/goex/internal/logger"
    13  
    14  	. "github.com/nntaoli-project/goex"
    15  )
    16  
    17  const (
    18  	/*
    19  	  http headers
    20  	*/
    21  	OK_ACCESS_KEY        = "OK-ACCESS-KEY"
    22  	OK_ACCESS_SIGN       = "OK-ACCESS-SIGN"
    23  	OK_ACCESS_TIMESTAMP  = "OK-ACCESS-TIMESTAMP"
    24  	OK_ACCESS_PASSPHRASE = "OK-ACCESS-PASSPHRASE"
    25  
    26  	/**
    27  	  paging params
    28  	*/
    29  	OK_FROM  = "OK-FROM"
    30  	OK_TO    = "OK-TO"
    31  	OK_LIMIT = "OK-LIMIT"
    32  
    33  	CONTENT_TYPE = "Content-Type"
    34  	ACCEPT       = "Accept"
    35  	COOKIE       = "Cookie"
    36  	LOCALE       = "locale="
    37  
    38  	APPLICATION_JSON      = "application/json"
    39  	APPLICATION_JSON_UTF8 = "application/json; charset=UTF-8"
    40  
    41  	/*
    42  	  i18n: internationalization
    43  	*/
    44  	ENGLISH            = "en_US"
    45  	SIMPLIFIED_CHINESE = "zh_CN"
    46  	//zh_TW || zh_HK
    47  	TRADITIONAL_CHINESE = "zh_HK"
    48  
    49  	/*
    50  	  http methods
    51  	*/
    52  	GET    = "GET"
    53  	POST   = "POST"
    54  	DELETE = "DELETE"
    55  
    56  	/*
    57  	 others
    58  	*/
    59  	ResultDataJsonString = "resultDataJsonString"
    60  	ResultPageJsonString = "resultPageJsonString"
    61  
    62  	BTC_USD_SWAP = "BTC-USD-SWAP"
    63  	LTC_USD_SWAP = "LTC-USD-SWAP"
    64  	ETH_USD_SWAP = "ETH-USD-SWAP"
    65  	ETC_USD_SWAP = "ETC-USD-SWAP"
    66  	BCH_USD_SWAP = "BCH-USD-SWAP"
    67  	BSV_USD_SWAP = "BSV-USD-SWAP"
    68  	EOS_USD_SWAP = "EOS-USD-SWAP"
    69  	XRP_USD_SWAP = "XRP-USD-SWAP"
    70  
    71  	/*Rest Endpoint*/
    72  	Endpoint              = "https://www.okex.com"
    73  	GET_ACCOUNTS          = "/api/swap/v3/accounts"
    74  	PLACE_ORDER           = "/api/swap/v3/order"
    75  	CANCEL_ORDER          = "/api/swap/v3/cancel_order/%s/%s"
    76  	GET_ORDER             = "/api/swap/v3/orders/%s/%s"
    77  	GET_POSITION          = "/api/swap/v3/%s/position"
    78  	GET_DEPTH             = "/api/swap/v3/instruments/%s/depth?size=%d"
    79  	GET_TICKER            = "/api/swap/v3/instruments/%s/ticker"
    80  	GET_ALL_TICKER        = "/api/swap/v3/instruments/ticker"
    81  	GET_UNFINISHED_ORDERS = "/api/swap/v3/orders/%s?status=%d&limit=%d"
    82  	PLACE_ALGO_ORDER      = "/api/swap/v3/order_algo"
    83  	CANCEL_ALGO_ORDER     = "/api/swap/v3/cancel_algos"
    84  	GET_ALGO_ORDER        = "/api/swap/v3/order_algo/%s?order_type=%d&"
    85  )
    86  
    87  type BaseResponse struct {
    88  	ErrorCode    string `json:"error_code"`
    89  	ErrorMessage string `json:"error_message"`
    90  	Result       bool   `json:"result,string"`
    91  }
    92  
    93  type OKExSwap struct {
    94  	*OKEx
    95  	config *APIConfig
    96  }
    97  
    98  func NewOKExSwap(config *APIConfig) *OKExSwap {
    99  	return &OKExSwap{OKEx: &OKEx{config: config}, config: config}
   100  }
   101  
   102  func (ok *OKExSwap) GetExchangeName() string {
   103  	return OKEX_SWAP
   104  }
   105  
   106  func (ok *OKExSwap) GetFutureTicker(currencyPair CurrencyPair, contractType string) (*Ticker, error) {
   107  	var resp struct {
   108  		InstrumentId string  `json:"instrument_id"`
   109  		Last         float64 `json:"last,string"`
   110  		High24h      float64 `json:"high_24h,string"`
   111  		Low24h       float64 `json:"low_24h,string"`
   112  		BestBid      float64 `json:"best_bid,string"`
   113  		BestAsk      float64 `json:"best_ask,string"`
   114  		Volume24h    float64 `json:"volume_24h,string"`
   115  		Timestamp    string  `json:"timestamp"`
   116  	}
   117  	contractType = ok.adaptContractType(currencyPair)
   118  	err := ok.DoRequest("GET", fmt.Sprintf(GET_TICKER, contractType), "", &resp)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	date, _ := time.Parse(time.RFC3339, resp.Timestamp)
   124  	return &Ticker{
   125  		Pair: currencyPair,
   126  		Last: resp.Last,
   127  		Low:  resp.Low24h,
   128  		High: resp.High24h,
   129  		Vol:  resp.Volume24h,
   130  		Buy:  resp.BestBid,
   131  		Sell: resp.BestAsk,
   132  		Date: uint64(date.UnixNano() / int64(time.Millisecond))}, nil
   133  }
   134  
   135  func (ok *OKExSwap) GetFutureAllTicker() (*[]FutureTicker, error) {
   136  	var resp SwapTickerList
   137  	err := ok.DoRequest("GET", GET_ALL_TICKER, "", &resp)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	var tickers []FutureTicker
   143  	for _, t := range resp {
   144  		date, _ := time.Parse(time.RFC3339, t.Timestamp)
   145  		tickers = append(tickers, FutureTicker{
   146  			ContractType: t.InstrumentId,
   147  			Ticker: &Ticker{
   148  				Pair: NewCurrencyPair3(t.InstrumentId, "-"),
   149  				Sell: t.BestAsk,
   150  				Buy:  t.BestBid,
   151  				Low:  t.Low24h,
   152  				High: t.High24h,
   153  				Last: t.Last,
   154  				Vol:  t.Volume24h,
   155  				Date: uint64(date.UnixNano() / int64(time.Millisecond))}})
   156  	}
   157  
   158  	return &tickers, nil
   159  }
   160  
   161  func (ok *OKExSwap) GetFutureDepth(currencyPair CurrencyPair, contractType string, size int) (*Depth, error) {
   162  	var resp SwapInstrumentDepth
   163  	contractType = ok.adaptContractType(currencyPair)
   164  
   165  	err := ok.DoRequest("GET", fmt.Sprintf(GET_DEPTH, contractType, size), "", &resp)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	var dep Depth
   171  	dep.ContractType = contractType
   172  	dep.Pair = currencyPair
   173  	dep.UTime, _ = time.Parse(time.RFC3339, resp.Timestamp)
   174  
   175  	for _, v := range resp.Bids {
   176  		dep.BidList = append(dep.BidList, DepthRecord{
   177  			Price:  ToFloat64(v[0]),
   178  			Amount: ToFloat64(v[1])})
   179  	}
   180  
   181  	for i := len(resp.Asks) - 1; i >= 0; i-- {
   182  		dep.AskList = append(dep.AskList, DepthRecord{
   183  			Price:  ToFloat64(resp.Asks[i][0]),
   184  			Amount: ToFloat64(resp.Asks[i][1])})
   185  	}
   186  
   187  	return &dep, nil
   188  }
   189  
   190  func (ok *OKExSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAccount, error) {
   191  	var (
   192  		err   error
   193  		infos SwapAccounts
   194  	)
   195  
   196  	if len(currencyPair) == 1 {
   197  		accountInfo, err := ok.GetFutureAccountInfo(currencyPair[0])
   198  		if err != nil {
   199  			return nil, err
   200  		}
   201  
   202  		if accountInfo == nil {
   203  			return nil, errors.New("api return info is empty")
   204  		}
   205  
   206  		infos.Info = append(infos.Info, *accountInfo)
   207  
   208  		goto wrapperF
   209  	}
   210  
   211  	err = ok.OKEx.DoRequest("GET", GET_ACCOUNTS, "", &infos)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	//log.Println(infos)
   217  wrapperF:
   218  	acc := FutureAccount{}
   219  	acc.FutureSubAccounts = make(map[Currency]FutureSubAccount, 2)
   220  
   221  	for _, account := range infos.Info {
   222  		subAcc := FutureSubAccount{AccountRights: account.Equity,
   223  			KeepDeposit: account.Margin, ProfitReal: account.RealizedPnl,
   224  			ProfitUnreal: account.UnrealizedPnl, RiskRate: account.MarginRatio}
   225  		meta := strings.Split(account.InstrumentId, "-")
   226  		if len(meta) > 0 {
   227  			subAcc.Currency = NewCurrency(meta[0], "")
   228  		}
   229  		acc.FutureSubAccounts[subAcc.Currency] = subAcc
   230  	}
   231  
   232  	return &acc, nil
   233  }
   234  
   235  func (ok *OKExSwap) GetFutureAccountInfo(currency CurrencyPair) (*SwapAccountInfo, error) {
   236  	var infos struct {
   237  		Info SwapAccountInfo `json:"info"`
   238  	}
   239  
   240  	err := ok.OKEx.DoRequest("GET", fmt.Sprintf("/api/swap/v3/%s/accounts", ok.adaptContractType(currency)), "", &infos)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	return &infos.Info, nil
   246  }
   247  
   248  /*
   249   OKEX swap api parameter's definition
   250   @author Lingting Fu
   251   @date 2018-12-27
   252   @version 1.0.0
   253  */
   254  
   255  type BasePlaceOrderInfo struct {
   256  	ClientOid  string `json:"client_oid"`
   257  	Price      string `json:"price"`
   258  	MatchPrice string `json:"match_price"`
   259  	Type       string `json:"type"`
   260  	Size       string `json:"size"`
   261  	OrderType  string `json:"order_type"`
   262  }
   263  
   264  type PlaceOrderInfo struct {
   265  	BasePlaceOrderInfo
   266  	InstrumentId string `json:"instrument_id"`
   267  }
   268  
   269  type PlaceOrdersInfo struct {
   270  	InstrumentId string                `json:"instrument_id"`
   271  	OrderData    []*BasePlaceOrderInfo `json:"order_data"`
   272  }
   273  
   274  func (ok *OKExSwap) PlaceFutureOrder(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (string, error) {
   275  	fOrder, err := ok.PlaceFutureOrder2(currencyPair, contractType, price, amount, openType, matchPrice)
   276  	return fOrder.OrderID2, err
   277  }
   278  
   279  func (ok *OKExSwap) PlaceFutureOrder2(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, opt ...LimitOrderOptionalParameter) (*FutureOrder, error) {
   280  	cid := GenerateOrderClientId(32)
   281  	param := PlaceOrderInfo{
   282  		BasePlaceOrderInfo{
   283  			ClientOid:  cid,
   284  			Price:      price,
   285  			MatchPrice: fmt.Sprint(matchPrice),
   286  			Type:       fmt.Sprint(openType),
   287  			Size:       amount,
   288  			OrderType:  "0",
   289  		},
   290  		ok.adaptContractType(currencyPair),
   291  	}
   292  
   293  	if len(opt) > 0 {
   294  		switch opt[0] {
   295  		case PostOnly:
   296  			param.OrderType = "1"
   297  		case Fok:
   298  			param.OrderType = "2"
   299  		case Ioc:
   300  			param.OrderType = "3"
   301  		}
   302  	}
   303  
   304  	reqBody, _, _ := ok.OKEx.BuildRequestBody(param)
   305  
   306  	fOrder := &FutureOrder{
   307  		ClientOid:    cid,
   308  		Currency:     currencyPair,
   309  		ContractName: contractType,
   310  		OType:        openType,
   311  		Price:        ToFloat64(price),
   312  		Amount:       ToFloat64(amount),
   313  	}
   314  
   315  	var resp struct {
   316  		BaseResponse
   317  		OrderID   string `json:"order_id"`
   318  		ClientOid string `json:"client_oid"`
   319  	}
   320  
   321  	err := ok.DoRequest("POST", PLACE_ORDER, reqBody, &resp)
   322  	if err != nil {
   323  		logger.Errorf("[param] %s", param)
   324  		return fOrder, err
   325  	}
   326  
   327  	if resp.ErrorMessage != "" {
   328  		logger.Errorf("[param] %s", param)
   329  		return fOrder, errors.New(fmt.Sprintf("%s:%s", resp.ErrorCode, resp.ErrorMessage))
   330  	}
   331  
   332  	fOrder.OrderID2 = resp.OrderID
   333  
   334  	return fOrder, nil
   335  }
   336  
   337  func (ok *OKExSwap) LimitFuturesOrder(currencyPair CurrencyPair, contractType, price, amount string, openType int, opt ...LimitOrderOptionalParameter) (*FutureOrder, error) {
   338  	return ok.PlaceFutureOrder2(currencyPair, contractType, price, amount, openType, 0, opt...)
   339  }
   340  
   341  func (ok *OKExSwap) MarketFuturesOrder(currencyPair CurrencyPair, contractType, amount string, openType int) (*FutureOrder, error) {
   342  	return ok.PlaceFutureOrder2(currencyPair, contractType, "0", amount, openType, 1)
   343  }
   344  
   345  func (ok *OKExSwap) FutureCancelOrder(currencyPair CurrencyPair, contractType, orderId string) (bool, error) {
   346  	var cancelParam struct {
   347  		OrderId      string `json:"order_id"`
   348  		InstrumentId string `json:"instrument_id"`
   349  	}
   350  
   351  	var resp SwapCancelOrderResult
   352  
   353  	cancelParam.InstrumentId = contractType
   354  	cancelParam.OrderId = orderId
   355  
   356  	//req, _, _ := BuildRequestBody(cancelParam)
   357  
   358  	err := ok.DoRequest("POST", fmt.Sprintf(CANCEL_ORDER, ok.adaptContractType(currencyPair), orderId), "", &resp)
   359  	if err != nil {
   360  		return false, err
   361  	}
   362  
   363  	return resp.Result, nil
   364  }
   365  
   366  func (ok *OKExSwap) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) {
   367  	urlPath := fmt.Sprintf("/api/swap/v3/orders/%s?", ok.adaptContractType(pair))
   368  
   369  	param := url.Values{}
   370  	param.Set("limit", "100")
   371  	param.Set("state", "7")
   372  	MergeOptionalParameter(&param, optional...)
   373  
   374  	var response SwapOrdersInfo
   375  
   376  	err := ok.DoRequest("GET", urlPath+param.Encode(), "", &response)
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  
   381  	orders := make([]FutureOrder, 0, 100)
   382  	for _, info := range response.OrderInfo {
   383  		ord := ok.parseOrder(info)
   384  		ord.Currency = pair
   385  		ord.ContractName = contractType
   386  		orders = append(orders, ord)
   387  	}
   388  
   389  	return orders, nil
   390  }
   391  
   392  func (ok *OKExSwap) parseOrder(ord BaseOrderInfo) FutureOrder {
   393  	oTime, _ := time.Parse(time.RFC3339, ord.Timestamp)
   394  	return FutureOrder{
   395  		ClientOid:  ord.ClientOid,
   396  		OrderID2:   ord.OrderId,
   397  		Amount:     ord.Size,
   398  		Price:      ord.Price,
   399  		DealAmount: ord.FilledQty,
   400  		AvgPrice:   ord.PriceAvg,
   401  		OType:      ord.Type,
   402  		Status:     ok.AdaptTradeStatus(ord.Status),
   403  		Fee:        ord.Fee,
   404  		OrderTime:  oTime.UnixNano() / int64(time.Millisecond)}
   405  }
   406  
   407  func (ok *OKExSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) {
   408  	var (
   409  		resp SwapOrdersInfo
   410  	)
   411  	contractType = ok.adaptContractType(currencyPair)
   412  	err := ok.DoRequest("GET", fmt.Sprintf(GET_UNFINISHED_ORDERS, contractType, 6, 100), "", &resp)
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  
   417  	if resp.Message != "" {
   418  		return nil, errors.New(fmt.Sprintf("{\"ErrCode\":%d,\"ErrMessage\":\"%s\"", resp.Code, resp.Message))
   419  	}
   420  
   421  	var orders []FutureOrder
   422  	for _, info := range resp.OrderInfo {
   423  		ord := ok.parseOrder(info)
   424  		ord.Currency = currencyPair
   425  		ord.ContractName = contractType
   426  		orders = append(orders, ord)
   427  	}
   428  
   429  	//log.Println(len(orders))
   430  	return orders, nil
   431  }
   432  
   433  /**
   434   *获取订单信息
   435   */
   436  func (ok *OKExSwap) GetFutureOrders(orderIds []string, currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) {
   437  	panic("")
   438  }
   439  
   440  /**
   441   *获取单个订单信息
   442   */
   443  func (ok *OKExSwap) GetFutureOrder(orderId string, currencyPair CurrencyPair, contractType string) (*FutureOrder, error) {
   444  	var getOrderParam struct {
   445  		OrderId      string `json:"order_id"`
   446  		InstrumentId string `json:"instrument_id"`
   447  	}
   448  
   449  	var resp struct {
   450  		BizWarmTips
   451  		BaseOrderInfo
   452  	}
   453  
   454  	contractType = ok.adaptContractType(currencyPair)
   455  
   456  	getOrderParam.OrderId = orderId
   457  	getOrderParam.InstrumentId = contractType
   458  
   459  	//reqBody, _, _ := BuildRequestBody(getOrderParam)
   460  
   461  	err := ok.DoRequest("GET", fmt.Sprintf(GET_ORDER, contractType, orderId), "", &resp)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  
   466  	if resp.Message != "" {
   467  		return nil, errors.New(fmt.Sprintf("{\"ErrCode\":%d,\"ErrMessage\":\"%s\"}", resp.Code, resp.Message))
   468  	}
   469  
   470  	oTime, err := time.Parse(time.RFC3339, resp.Timestamp)
   471  
   472  	return &FutureOrder{
   473  		ClientOid:    resp.ClientOid,
   474  		Currency:     currencyPair,
   475  		ContractName: contractType,
   476  		OrderID2:     resp.OrderId,
   477  		Amount:       resp.Size,
   478  		Price:        resp.Price,
   479  		DealAmount:   resp.FilledQty,
   480  		AvgPrice:     resp.PriceAvg,
   481  		OType:        resp.Type,
   482  		Fee:          resp.Fee,
   483  		Status:       ok.AdaptTradeStatus(resp.Status),
   484  		OrderTime:    oTime.UnixNano() / int64(time.Millisecond),
   485  	}, nil
   486  }
   487  
   488  func (ok *OKExSwap) GetFuturePosition(currencyPair CurrencyPair, contractType string) ([]FuturePosition, error) {
   489  	var resp SwapPosition
   490  	contractType = ok.adaptContractType(currencyPair)
   491  
   492  	err := ok.DoRequest("GET", fmt.Sprintf(GET_POSITION, contractType), "", &resp)
   493  	if err != nil {
   494  		return nil, err
   495  	}
   496  
   497  	var positions []FuturePosition
   498  
   499  	positions = append(positions, FuturePosition{
   500  		ContractType: contractType,
   501  		Symbol:       currencyPair})
   502  
   503  	var (
   504  		buyPosition  SwapPositionHolding
   505  		sellPosition SwapPositionHolding
   506  	)
   507  
   508  	if len(resp.Holding) > 0 {
   509  		if resp.Holding[0].Side == "long" {
   510  			buyPosition = resp.Holding[0]
   511  			if len(resp.Holding) == 2 {
   512  				sellPosition = resp.Holding[1]
   513  			}
   514  		} else {
   515  			sellPosition = resp.Holding[0]
   516  			if len(resp.Holding) == 2 {
   517  				buyPosition = resp.Holding[1]
   518  			}
   519  		}
   520  
   521  		positions[0].ForceLiquPrice = buyPosition.LiquidationPrice
   522  		positions[0].BuyAmount = buyPosition.Position
   523  		positions[0].BuyAvailable = buyPosition.AvailPosition
   524  		positions[0].BuyPriceAvg = buyPosition.AvgCost
   525  		positions[0].BuyProfitReal = buyPosition.RealizedPnl
   526  		positions[0].BuyPriceCost = buyPosition.SettlementPrice
   527  
   528  		positions[0].ForceLiquPrice = sellPosition.LiquidationPrice
   529  		positions[0].SellAmount = sellPosition.Position
   530  		positions[0].SellAvailable = sellPosition.AvailPosition
   531  		positions[0].SellPriceAvg = sellPosition.AvgCost
   532  		positions[0].SellProfitReal = sellPosition.RealizedPnl
   533  		positions[0].SellPriceCost = sellPosition.SettlementPrice
   534  
   535  		positions[0].LeverRate = ToFloat64(sellPosition.Leverage)
   536  	}
   537  	return positions, nil
   538  }
   539  
   540  /**
   541   * BTC: 100美元一张合约
   542   * LTC/ETH/ETC/BCH: 10美元一张合约
   543   */
   544  func (ok *OKExSwap) GetContractValue(currencyPair CurrencyPair) (float64, error) {
   545  	if currencyPair.CurrencyA.Eq(BTC) {
   546  		return 100, nil
   547  	}
   548  	return 10, nil
   549  }
   550  
   551  func (ok *OKExSwap) GetFee() (float64, error) {
   552  	panic("not support")
   553  }
   554  
   555  func (ok *OKExSwap) GetFutureEstimatedPrice(currencyPair CurrencyPair) (float64, error) {
   556  	panic("not support")
   557  }
   558  
   559  func (ok *OKExSwap) GetFutureIndex(currencyPair CurrencyPair) (float64, error) {
   560  	panic("not support")
   561  }
   562  
   563  func (ok *OKExSwap) GetDeliveryTime() (int, int, int, int) {
   564  	panic("not support")
   565  }
   566  
   567  func (ok *OKExSwap) GetKlineRecords(contractType string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) {
   568  	granularity := adaptKLinePeriod(KlinePeriod(period))
   569  	if granularity == -1 {
   570  		return nil, errors.New("kline period parameter is error")
   571  	}
   572  	return ok.GetKlineRecords2(contractType, currency, "", "", strconv.Itoa(granularity))
   573  }
   574  
   575  /**
   576    since : 单位秒,开始时间
   577    to : 单位秒,结束时间
   578  */
   579  func (ok *OKExSwap) GetKlineRecordsByRange(currency CurrencyPair, period, since, to int) ([]FutureKline, error) {
   580  	urlPath := "/api/swap/v3/instruments/%s/candles?start=%s&end=%s&granularity=%d"
   581  	sinceTime := time.Unix(int64(since), 0).UTC().Format(time.RFC3339)
   582  	toTime := time.Unix(int64(to), 0).UTC().Format(time.RFC3339)
   583  	contractId := ok.adaptContractType(currency)
   584  	granularity := adaptKLinePeriod(KlinePeriod(period))
   585  	if granularity == -1 {
   586  		return nil, errors.New("kline period parameter is error")
   587  	}
   588  
   589  	var response [][]interface{}
   590  	err := ok.DoRequest("GET", fmt.Sprintf(urlPath, contractId, sinceTime, toTime, granularity), "", &response)
   591  	if err != nil {
   592  		return nil, err
   593  	}
   594  
   595  	var klines []FutureKline
   596  	for _, itm := range response {
   597  		t, _ := time.Parse(time.RFC3339, fmt.Sprint(itm[0]))
   598  		klines = append(klines, FutureKline{
   599  			Kline: &Kline{
   600  				Timestamp: t.Unix(),
   601  				Pair:      currency,
   602  				Open:      ToFloat64(itm[1]),
   603  				High:      ToFloat64(itm[2]),
   604  				Low:       ToFloat64(itm[3]),
   605  				Close:     ToFloat64(itm[4]),
   606  				Vol:       ToFloat64(itm[5])},
   607  			Vol2: ToFloat64(itm[6])})
   608  	}
   609  
   610  	return klines, nil
   611  }
   612  
   613  /**
   614    since : 单位秒,开始时间
   615  */
   616  func (ok *OKExSwap) GetKlineRecords2(contractType string, currency CurrencyPair, start, end, period string) ([]FutureKline, error) {
   617  	urlPath := "/api/swap/v3/instruments/%s/candles?%s"
   618  	params := url.Values{}
   619  	if start != "" {
   620  		params.Set("start", start)
   621  	}
   622  	if end != "" {
   623  		params.Set("end", end)
   624  	}
   625  	if period != "" {
   626  		params.Set("granularity", period)
   627  	}
   628  	contractId := ok.adaptContractType(currency)
   629  
   630  	var response [][]interface{}
   631  	err := ok.DoRequest("GET", fmt.Sprintf(urlPath, contractId, params.Encode()), "", &response)
   632  	if err != nil {
   633  		return nil, err
   634  	}
   635  
   636  	var kline []FutureKline
   637  	for _, itm := range response {
   638  		t, _ := time.Parse(time.RFC3339, fmt.Sprint(itm[0]))
   639  		kline = append(kline, FutureKline{
   640  			Kline: &Kline{
   641  				Timestamp: t.Unix(),
   642  				Pair:      currency,
   643  				Open:      ToFloat64(itm[1]),
   644  				High:      ToFloat64(itm[2]),
   645  				Low:       ToFloat64(itm[3]),
   646  				Close:     ToFloat64(itm[4]),
   647  				Vol:       ToFloat64(itm[5])},
   648  			Vol2: ToFloat64(itm[6])})
   649  	}
   650  
   651  	return kline, nil
   652  }
   653  
   654  func (ok *OKExSwap) GetTrades(contractType string, currencyPair CurrencyPair, since int64) ([]Trade, error) {
   655  	panic("not support")
   656  }
   657  
   658  func (ok *OKExSwap) GetExchangeRate() (float64, error) {
   659  	panic("not support")
   660  }
   661  
   662  func (ok *OKExSwap) GetHistoricalFunding(contractType string, currencyPair CurrencyPair, page int) ([]HistoricalFunding, error) {
   663  	var resp []HistoricalFunding
   664  	uri := fmt.Sprintf("/api/swap/v3/instruments/%s/historical_funding_rate?from=%d", ok.adaptContractType(currencyPair), page)
   665  	err := ok.DoRequest("GET", uri, "", &resp)
   666  	if err != nil {
   667  		return nil, err
   668  	}
   669  	return resp, nil
   670  }
   671  
   672  func (ok *OKExSwap) AdaptTradeStatus(status int) TradeStatus {
   673  	switch status {
   674  	case -1:
   675  		return ORDER_CANCEL
   676  	case 0:
   677  		return ORDER_UNFINISH
   678  	case 1:
   679  		return ORDER_PART_FINISH
   680  	case 2:
   681  		return ORDER_FINISH
   682  	default:
   683  		return ORDER_UNFINISH
   684  	}
   685  }
   686  
   687  func (ok *OKExSwap) adaptContractType(currencyPair CurrencyPair) string {
   688  	return fmt.Sprintf("%s-SWAP", currencyPair.ToSymbol("-"))
   689  }
   690  
   691  type Instrument struct {
   692  	InstrumentID        string    `json:"instrument_id"`
   693  	UnderlyingIndex     string    `json:"underlying_index"`
   694  	QuoteCurrency       string    `json:"quote_currency"`
   695  	Coin                string    `json:"coin"`
   696  	ContractVal         float64   `json:"contract_val,string"`
   697  	Listing             time.Time `json:"listing"`
   698  	Delivery            time.Time `json:"delivery"`
   699  	SizeIncrement       int       `json:"size_increment,string"`
   700  	TickSize            float64   `json:"tick_size,string"`
   701  	BaseCurrency        string    `json:"base_currency"`
   702  	Underlying          string    `json:"underlying"`
   703  	SettlementCurrency  string    `json:"settlement_currency"`
   704  	IsInverse           bool      `json:"is_inverse,string"`
   705  	ContractValCurrency string    `json:"contract_val_currency"`
   706  }
   707  
   708  func (ok *OKExSwap) GetInstruments() ([]Instrument, error) {
   709  	var resp []Instrument
   710  	err := ok.DoRequest("GET", "/api/swap/v3/instruments", "", &resp)
   711  	if err != nil {
   712  		return nil, err
   713  	}
   714  	return resp, nil
   715  }
   716  
   717  type MarginLeverage struct {
   718  	LongLeverage  float64 `json:"long_leverage,string"`
   719  	MarginMode    string  `json:"margin_mode"`
   720  	ShortLeverage float64 `json:"short_leverage,string"`
   721  	InstrumentId  string  `json:"instrument_id"`
   722  }
   723  
   724  func (ok *OKExSwap) GetMarginLevel(currencyPair CurrencyPair) (*MarginLeverage, error) {
   725  	var resp MarginLeverage
   726  	uri := fmt.Sprintf("/api/swap/v3/accounts/%s/settings", ok.adaptContractType(currencyPair))
   727  
   728  	err := ok.DoRequest("GET", uri, "", &resp)
   729  	if err != nil {
   730  		return nil, err
   731  	}
   732  	return &resp, nil
   733  
   734  }
   735  
   736  // marginmode
   737  //1:逐仓-多仓
   738  //2:逐仓-空仓
   739  //3:全仓
   740  func (ok *OKExSwap) SetMarginLevel(currencyPair CurrencyPair, level, marginMode int) (*MarginLeverage, error) {
   741  	var resp MarginLeverage
   742  	uri := fmt.Sprintf("/api/swap/v3/accounts/%s/leverage", ok.adaptContractType(currencyPair))
   743  
   744  	reqBody := make(map[string]string)
   745  	reqBody["leverage"] = strconv.Itoa(level)
   746  	reqBody["side"] = strconv.Itoa(marginMode)
   747  	data, err := json.Marshal(reqBody)
   748  	if err != nil {
   749  		return nil, err
   750  	}
   751  
   752  	err = ok.DoRequest("POST", uri, string(data), &resp)
   753  	if err != nil {
   754  		return nil, err
   755  	}
   756  	return &resp, nil
   757  }
   758  
   759  //委托策略下单 algo_type 1:限价 2:市场价;触发价格类型,默认是限价;为市场价时,委托价格不必填;
   760  func (ok *OKExSwap) PlaceFutureAlgoOrder(ord *FutureOrder) (*FutureOrder, error) {
   761  	var param struct {
   762  		InstrumentId string `json:"instrument_id"`
   763  		Type         int    `json:"type"`
   764  		OrderType    int    `json:"order_type"` //1:止盈止损 2:跟踪委托 3:冰山委托 4:时间加权
   765  		Size         string `json:"size"`
   766  		TriggerPrice string `json:"trigger_price"`
   767  		AlgoPrice    string `json:"algo_price"`
   768  		AlgoType     string `json:"algo_type"`
   769  	}
   770  
   771  	var response struct {
   772  		ErrorMessage string `json:"error_message"`
   773  		ErrorCode    string `json:"error_code"`
   774  		DetailMsg    string `json:"detail_msg"`
   775  
   776  		Data struct {
   777  			Result       string `json:"result"`
   778  			ErrorMessage string `json:"error_message"`
   779  			ErrorCode    string `json:"error_code"`
   780  			AlgoId       string `json:"algo_id"`
   781  			InstrumentId string `json:"instrument_id"`
   782  			OrderType    int    `json:"order_type"`
   783  		} `json:"data"`
   784  	}
   785  	if ord == nil {
   786  		return nil, errors.New("ord param is nil")
   787  	}
   788  	param.InstrumentId = ok.adaptContractType(ord.Currency)
   789  	param.Type = ord.OType
   790  	param.OrderType = ord.OrderType
   791  	param.AlgoType = fmt.Sprint(ord.AlgoType)
   792  	param.TriggerPrice = fmt.Sprint(ord.TriggerPrice)
   793  	param.AlgoPrice = fmt.Sprint(ToFloat64(ord.Price))
   794  	param.Size = fmt.Sprint(ord.Amount)
   795  
   796  	reqBody, _, _ := ok.BuildRequestBody(param)
   797  	err := ok.DoRequest("POST", PLACE_ALGO_ORDER, reqBody, &response)
   798  
   799  	if err != nil {
   800  		return ord, err
   801  	}
   802  
   803  	ord.OrderID2 = response.Data.AlgoId
   804  	ord.OrderTime = time.Now().UnixNano() / int64(time.Millisecond)
   805  
   806  	return ord, nil
   807  }
   808  
   809  //委托策略撤单
   810  func (ok *OKExSwap) FutureCancelAlgoOrder(currencyPair CurrencyPair, orderId []string) (bool, error) {
   811  	if len(orderId) == 0 {
   812  		return false, errors.New("invalid order id")
   813  	}
   814  	var cancelParam struct {
   815  		InstrumentId string   `json:"instrument_id"`
   816  		AlgoIds      []string `json:"algo_ids"`
   817  		OrderType    string   `json:"order_type"`
   818  	}
   819  
   820  	var resp struct {
   821  		ErrorMessage string `json:"error_message"`
   822  		ErrorCode    string `json:"error_code"`
   823  		DetailMsg    string `json:"detailMsg"`
   824  		Data         struct {
   825  			Result       string `json:"result"`
   826  			AlgoIds      string `json:"algo_ids"`
   827  			InstrumentID string `json:"instrument_id"`
   828  			OrderType    string `json:"order_type"`
   829  		} `json:"data"`
   830  	}
   831  
   832  	cancelParam.InstrumentId = ok.adaptContractType(currencyPair)
   833  	cancelParam.OrderType = "1"
   834  	cancelParam.AlgoIds = orderId
   835  
   836  	reqBody, _, _ := ok.BuildRequestBody(cancelParam)
   837  
   838  	err := ok.DoRequest("POST", CANCEL_ALGO_ORDER, reqBody, &resp)
   839  	if err != nil {
   840  		return false, err
   841  	}
   842  
   843  	return resp.Data.Result == "success", nil
   844  }
   845  
   846  //获取委托单列表, status和algo_id必填且只能填其一
   847  func (ok *OKExSwap) GetFutureAlgoOrders(algo_id string, status string, currencyPair CurrencyPair) ([]FutureOrder, error) {
   848  	uri := fmt.Sprintf(GET_ALGO_ORDER, ok.adaptContractType(currencyPair), 1)
   849  	if algo_id != "" {
   850  		uri += "algo_id=" + algo_id
   851  	} else if status != "" {
   852  		uri += "status=" + status
   853  	} else {
   854  		return nil, errors.New("status or algo_id is needed")
   855  	}
   856  
   857  	var resp struct {
   858  		OrderStrategyVOS []struct {
   859  			AlgoId       string `json:"algo_id"`
   860  			AlgoPrice    string `json:"algo_price"`
   861  			InstrumentId string `json:"instrument_id"`
   862  			Leverage     string `json:"leverage"`
   863  			OrderType    string `json:"order_type"`
   864  			RealAmount   string `json:"real_amount"`
   865  			RealPrice    string `json:"real_price"`
   866  			Size         string `json:"size"`
   867  			Status       string `json:"status"`
   868  			Timestamp    string `json:"timestamp"`
   869  			TriggerPrice string `json:"trigger_price"`
   870  			Type         string `json:"type"`
   871  		} `json:"orderStrategyVOS"`
   872  	}
   873  
   874  	err := ok.DoRequest("GET", uri, "", &resp)
   875  	if err != nil {
   876  		return nil, err
   877  	}
   878  
   879  	var orders []FutureOrder
   880  	for _, info := range resp.OrderStrategyVOS {
   881  		oTime, _ := time.Parse(time.RFC3339, info.Timestamp)
   882  
   883  		ord := FutureOrder{
   884  			OrderID2:     info.AlgoId,
   885  			Price:        ToFloat64(info.AlgoPrice),
   886  			Amount:       ToFloat64(info.Size),
   887  			AvgPrice:     ToFloat64(info.RealPrice),
   888  			DealAmount:   ToFloat64(info.RealAmount),
   889  			OrderTime:    oTime.UnixNano() / int64(time.Millisecond),
   890  			Status:       ok.AdaptTradeStatus(ToInt(info.Status)),
   891  			Currency:     CurrencyPair{},
   892  			OrderType:    ToInt(info.OrderType),
   893  			OType:        ToInt(info.Type),
   894  			TriggerPrice: ToFloat64(info.TriggerPrice),
   895  		}
   896  		ord.Currency = currencyPair
   897  		orders = append(orders, ord)
   898  	}
   899  
   900  	//log.Println(len(orders))
   901  	return orders, nil
   902  }