diff --git a/Const.go b/Const.go index 1d137d4e..e193b1fb 100644 --- a/Const.go +++ b/Const.go @@ -154,6 +154,7 @@ const ( HBDM = "hbdm.com" COINBENE = "coinbene.com" ATOP = "a.top" + BITGET_SWAP = "bitget_swap" ) const ( @@ -188,4 +189,4 @@ const ( PostOnly LimitOrderOptionalParameter = iota + 1 Ioc Fok -) \ No newline at end of file +) diff --git a/CurrencyPair.go b/CurrencyPair.go index 55787bb3..c4055085 100644 --- a/CurrencyPair.go +++ b/CurrencyPair.go @@ -17,8 +17,10 @@ func (c Currency) Eq(c2 Currency) bool { // A->B(A兑换为B) type CurrencyPair struct { - CurrencyA Currency - CurrencyB Currency + CurrencyA Currency + CurrencyB Currency + AmountTickSize int // 下单量精度 + PriceTickSize int //交易对价格精度 } var ( @@ -56,99 +58,86 @@ var ( HT = Currency{"HT", "HuoBi Token"} BNB = Currency{"BNB", "BNB, or Binance Coin, is a cryptocurrency created by Binance."} TRX = Currency{"TRX", ""} + GBP = Currency{"GBP", ""} + XLM = Currency{"XLM", ""} //currency pair - - BTC_CNY = CurrencyPair{BTC, CNY} - LTC_CNY = CurrencyPair{LTC, CNY} - BCC_CNY = CurrencyPair{BCC, CNY} - ETH_CNY = CurrencyPair{ETH, CNY} - ETC_CNY = CurrencyPair{ETC, CNY} - EOS_CNY = CurrencyPair{EOS, CNY} - BTS_CNY = CurrencyPair{BTS, CNY} - QTUM_CNY = CurrencyPair{QTUM, CNY} - SC_CNY = CurrencyPair{SC, CNY} - ANS_CNY = CurrencyPair{ANS, CNY} - ZEC_CNY = CurrencyPair{ZEC, CNY} - - BTC_KRW = CurrencyPair{BTC, KRW} - ETH_KRW = CurrencyPair{ETH, KRW} - ETC_KRW = CurrencyPair{ETC, KRW} - LTC_KRW = CurrencyPair{LTC, KRW} - BCH_KRW = CurrencyPair{BCH, KRW} - - BTC_USD = CurrencyPair{BTC, USD} - LTC_USD = CurrencyPair{LTC, USD} - ETH_USD = CurrencyPair{ETH, USD} - ETC_USD = CurrencyPair{ETC, USD} - BCH_USD = CurrencyPair{BCH, USD} - BCC_USD = CurrencyPair{BCC, USD} - XRP_USD = CurrencyPair{XRP, USD} - BCD_USD = CurrencyPair{BCD, USD} - EOS_USD = CurrencyPair{EOS, USD} - BTG_USD = CurrencyPair{BTG, USD} - BSV_USD = CurrencyPair{BSV, USD} - - BTC_USDT = CurrencyPair{BTC, USDT} - LTC_USDT = CurrencyPair{LTC, USDT} - BCH_USDT = CurrencyPair{BCH, USDT} - BCC_USDT = CurrencyPair{BCC, USDT} - ETC_USDT = CurrencyPair{ETC, USDT} - ETH_USDT = CurrencyPair{ETH, USDT} - BCD_USDT = CurrencyPair{BCD, USDT} - NEO_USDT = CurrencyPair{NEO, USDT} - EOS_USDT = CurrencyPair{EOS, USDT} - XRP_USDT = CurrencyPair{XRP, USDT} - HSR_USDT = CurrencyPair{HSR, USDT} - BSV_USDT = CurrencyPair{BSV, USDT} - OKB_USDT = CurrencyPair{OKB, USDT} - HT_USDT = CurrencyPair{HT, USDT} - BNB_USDT = CurrencyPair{BNB, USDT} - PAX_USDT = CurrencyPair{PAX, USDT} - TRX_USDT = CurrencyPair{TRX, USDT} - - XRP_EUR = CurrencyPair{XRP, EUR} - - BTC_JPY = CurrencyPair{BTC, JPY} - LTC_JPY = CurrencyPair{LTC, JPY} - ETH_JPY = CurrencyPair{ETH, JPY} - ETC_JPY = CurrencyPair{ETC, JPY} - BCH_JPY = CurrencyPair{BCH, JPY} - - LTC_BTC = CurrencyPair{LTC, BTC} - ETH_BTC = CurrencyPair{ETH, BTC} - ETC_BTC = CurrencyPair{ETC, BTC} - BCC_BTC = CurrencyPair{BCC, BTC} - BCH_BTC = CurrencyPair{BCH, BTC} - DCR_BTC = CurrencyPair{DCR, BTC} - XRP_BTC = CurrencyPair{XRP, BTC} - BTG_BTC = CurrencyPair{BTG, BTC} - BCD_BTC = CurrencyPair{BCD, BTC} - NEO_BTC = CurrencyPair{NEO, BTC} - EOS_BTC = CurrencyPair{EOS, BTC} - HSR_BTC = CurrencyPair{HSR, BTC} - BSV_BTC = CurrencyPair{BSV, BTC} - OKB_BTC = CurrencyPair{OKB, BTC} - HT_BTC = CurrencyPair{HT, BTC} - BNB_BTC = CurrencyPair{BNB, BTC} - TRX_BTC = CurrencyPair{TRX, BTC} - - ETC_ETH = CurrencyPair{ETC, ETH} - EOS_ETH = CurrencyPair{EOS, ETH} - ZEC_ETH = CurrencyPair{ZEC, ETH} - NEO_ETH = CurrencyPair{NEO, ETH} - HSR_ETH = CurrencyPair{HSR, ETH} - LTC_ETH = CurrencyPair{LTC, ETH} - - UNKNOWN_PAIR = CurrencyPair{UNKNOWN, UNKNOWN} + BTC_KRW = CurrencyPair{CurrencyA: BTC, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 1} + ETH_KRW = CurrencyPair{CurrencyA: ETH, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 2} + ETC_KRW = CurrencyPair{CurrencyA: ETC, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 2} + LTC_KRW = CurrencyPair{CurrencyA: LTC, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 2} + BCH_KRW = CurrencyPair{CurrencyA: BCH, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 2} + + BTC_USD = CurrencyPair{CurrencyA: BTC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 1} + LTC_USD = CurrencyPair{CurrencyA: LTC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + ETH_USD = CurrencyPair{CurrencyA: ETH, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + ETC_USD = CurrencyPair{CurrencyA: ETC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + BCH_USD = CurrencyPair{CurrencyA: BCH, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + XRP_USD = CurrencyPair{CurrencyA: XRP, CurrencyB: USD, AmountTickSize: 3, PriceTickSize: 3} + BCD_USD = CurrencyPair{CurrencyA: BCD, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 3} + EOS_USD = CurrencyPair{CurrencyA: EOS, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + BTG_USD = CurrencyPair{CurrencyA: BTG, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + BSV_USD = CurrencyPair{CurrencyA: BSV, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + + BTC_USDT = CurrencyPair{CurrencyA: BTC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 1} + LTC_USDT = CurrencyPair{CurrencyA: LTC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + BCH_USDT = CurrencyPair{CurrencyA: BCH, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + ETC_USDT = CurrencyPair{CurrencyA: ETC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} + ETH_USDT = CurrencyPair{CurrencyA: ETH, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + BCD_USDT = CurrencyPair{CurrencyA: BCD, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + NEO_USDT = CurrencyPair{CurrencyA: NEO, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + EOS_USDT = CurrencyPair{CurrencyA: EOS, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + XRP_USDT = CurrencyPair{CurrencyA: XRP, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + HSR_USDT = CurrencyPair{CurrencyA: HSR, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + BSV_USDT = CurrencyPair{CurrencyA: BSV, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + OKB_USDT = CurrencyPair{CurrencyA: OKB, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + HT_USDT = CurrencyPair{CurrencyA: HT, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 4} + BNB_USDT = CurrencyPair{CurrencyA: BNB, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + PAX_USDT = CurrencyPair{CurrencyA: PAX, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} + TRX_USDT = CurrencyPair{CurrencyA: TRX, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} + + XRP_EUR = CurrencyPair{CurrencyA: XRP, CurrencyB: EUR, AmountTickSize: 2, PriceTickSize: 4} + + BTC_JPY = CurrencyPair{CurrencyA: BTC, CurrencyB: JPY, AmountTickSize: 2, PriceTickSize: 0} + LTC_JPY = CurrencyPair{CurrencyA: LTC, CurrencyB: JPY, AmountTickSize: 2, PriceTickSize: 0} + ETH_JPY = CurrencyPair{CurrencyA: ETH, CurrencyB: JPY, AmountTickSize: 2, PriceTickSize: 0} + ETC_JPY = CurrencyPair{CurrencyA: ETC, CurrencyB: JPY, AmountTickSize: 2, PriceTickSize: 0} + BCH_JPY = CurrencyPair{CurrencyA: BCH, CurrencyB: JPY, AmountTickSize: 2, PriceTickSize: 0} + + LTC_BTC = CurrencyPair{CurrencyA: LTC, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + ETH_BTC = CurrencyPair{CurrencyA: ETH, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + ETC_BTC = CurrencyPair{CurrencyA: ETC, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + BCC_BTC = CurrencyPair{CurrencyA: BCC, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + BCH_BTC = CurrencyPair{CurrencyA: BCH, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + DCR_BTC = CurrencyPair{CurrencyA: DCR, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + XRP_BTC = CurrencyPair{CurrencyA: XRP, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 6} + BTG_BTC = CurrencyPair{CurrencyA: BTG, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + BCD_BTC = CurrencyPair{CurrencyA: BCD, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + NEO_BTC = CurrencyPair{CurrencyA: NEO, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + EOS_BTC = CurrencyPair{CurrencyA: EOS, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 5} + HSR_BTC = CurrencyPair{CurrencyA: HSR, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + BSV_BTC = CurrencyPair{CurrencyA: BSV, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 4} + OKB_BTC = CurrencyPair{CurrencyA: OKB, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 6} + HT_BTC = CurrencyPair{CurrencyA: HT, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 7} + BNB_BTC = CurrencyPair{CurrencyA: BNB, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 6} + TRX_BTC = CurrencyPair{CurrencyA: TRX, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 7} + + ETC_ETH = CurrencyPair{CurrencyA: ETC, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} + EOS_ETH = CurrencyPair{CurrencyA: EOS, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} + ZEC_ETH = CurrencyPair{CurrencyA: ZEC, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} + NEO_ETH = CurrencyPair{CurrencyA: NEO, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} + HSR_ETH = CurrencyPair{CurrencyA: HSR, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} + LTC_ETH = CurrencyPair{CurrencyA: LTC, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} + + UNKNOWN_PAIR = CurrencyPair{CurrencyA: UNKNOWN, CurrencyB: UNKNOWN} ) -func (c CurrencyPair) String() string { - return c.ToSymbol("_") +func (pair CurrencyPair) String() string { + return pair.ToSymbol("_") } -func (c CurrencyPair) Eq(c2 CurrencyPair) bool { - return c.String() == c2.String() +func (pair CurrencyPair) Eq(c2 CurrencyPair) bool { + return pair.String() == c2.String() } func (c Currency) AdaptBchToBcc() Currency { @@ -213,22 +202,32 @@ func NewCurrency(symbol, desc string) Currency { } func NewCurrencyPair(currencyA Currency, currencyB Currency) CurrencyPair { - return CurrencyPair{currencyA, currencyB} + return CurrencyPair{CurrencyA: currencyA, CurrencyB: currencyB} } func NewCurrencyPair2(currencyPairSymbol string) CurrencyPair { - return NewCurrencyPair3(currencyPairSymbol,"_") + return NewCurrencyPair3(currencyPairSymbol, "_") } func NewCurrencyPair3(currencyPairSymbol string, sep string) CurrencyPair { currencys := strings.Split(currencyPairSymbol, sep) if len(currencys) >= 2 { - return CurrencyPair{NewCurrency(currencys[0], ""), - NewCurrency(currencys[1], "")} + return CurrencyPair{CurrencyA: NewCurrency(currencys[0], ""), + CurrencyB: NewCurrency(currencys[1], "")} } return UNKNOWN_PAIR } +func (pair *CurrencyPair) SetAmountTickSize(tickSize int) CurrencyPair { + pair.AmountTickSize = tickSize + return *pair +} + +func (pair *CurrencyPair) SetPriceTickSize(tickSize int) CurrencyPair { + pair.PriceTickSize = tickSize + return *pair +} + func (pair CurrencyPair) ToSymbol(joinChar string) string { return strings.Join([]string{pair.CurrencyA.Symbol, pair.CurrencyB.Symbol}, joinChar) } @@ -242,7 +241,8 @@ func (pair CurrencyPair) AdaptUsdtToUsd() CurrencyPair { if pair.CurrencyB.Eq(USDT) { CurrencyB = USD } - return CurrencyPair{pair.CurrencyA, CurrencyB} + pair.CurrencyB = CurrencyB + return pair } func (pair CurrencyPair) AdaptUsdToUsdt() CurrencyPair { @@ -250,31 +250,17 @@ func (pair CurrencyPair) AdaptUsdToUsdt() CurrencyPair { if pair.CurrencyB.Eq(USD) { CurrencyB = USDT } - return CurrencyPair{pair.CurrencyA, CurrencyB} -} - -//It is currently applicable to binance and zb -func (pair CurrencyPair) AdaptBchToBcc() CurrencyPair { - CurrencyA := pair.CurrencyA - if pair.CurrencyA.Eq(BCH) { - CurrencyA = BCC - } - return CurrencyPair{CurrencyA, pair.CurrencyB} -} - -func (pair CurrencyPair) AdaptBccToBch() CurrencyPair { - if pair.CurrencyA.Eq(BCC) { - return CurrencyPair{BCH, pair.CurrencyB} - } + pair.CurrencyB = CurrencyB return pair } //for to symbol lower , Not practical '==' operation method func (pair CurrencyPair) ToLower() CurrencyPair { - return CurrencyPair{Currency{strings.ToLower(pair.CurrencyA.Symbol), pair.CurrencyA.Desc}, - Currency{strings.ToLower(pair.CurrencyB.Symbol), pair.CurrencyB.Desc}} + return CurrencyPair{CurrencyA: Currency{Symbol: strings.ToLower(pair.CurrencyA.Symbol), Desc: pair.CurrencyA.Desc}, + CurrencyB: Currency{Symbol: strings.ToLower(pair.CurrencyB.Symbol), Desc: pair.CurrencyB.Desc}} } func (pair CurrencyPair) Reverse() CurrencyPair { - return CurrencyPair{pair.CurrencyB, pair.CurrencyA} + return CurrencyPair{CurrencyA: pair.CurrencyB, CurrencyB: pair.CurrencyA, + AmountTickSize: pair.AmountTickSize, PriceTickSize: pair.PriceTickSize} } diff --git a/Models.go b/Models.go index 99801962..de2a4920 100644 --- a/Models.go +++ b/Models.go @@ -179,6 +179,7 @@ type FuturePosition struct { BuyPriceAvg float64 BuyPriceCost float64 BuyProfitReal float64 + BuyProfit float64 CreateDate int64 LeverRate float64 SellAmount float64 @@ -186,6 +187,7 @@ type FuturePosition struct { SellPriceAvg float64 SellPriceCost float64 SellProfitReal float64 + SellProfit float64 Symbol CurrencyPair //btc_usd:比特币,ltc_usd:莱特币 ContractType string ContractId int64 diff --git a/bitget/bitget_swap.go b/bitget/bitget_swap.go new file mode 100644 index 00000000..6635a78b --- /dev/null +++ b/bitget/bitget_swap.go @@ -0,0 +1,688 @@ +package bitget + +import ( + "encoding/json" + "errors" + "fmt" + . "github.com/nntaoli-project/goex" + "net/http" + "strconv" + "strings" + "time" +) + +const ( + baseUrl = "https://capi.bitget.com" +) + +type BitgetSwap struct { + accessKey string + secretKey string + passphrase string + baseUrl string + httpClient *http.Client + timeOffset int64 +} + +func NewSwap(config *APIConfig) *BitgetSwap { + if config.Endpoint == "" { + config.Endpoint = baseUrl + } + bs := &BitgetSwap{ + baseUrl: config.Endpoint, + accessKey: config.ApiKey, + secretKey: config.ApiSecretKey, + passphrase: config.ApiPassphrase, + httpClient: config.HttpClient, + } + bs.setTimeOffset() + return bs +} + +func (bs *BitgetSwap) SetBaseUri(uri string) { + bs.baseUrl = uri +} + +func (bs *BitgetSwap) GetExchangeName() string { + return BITGET_SWAP +} + +func (bs *BitgetSwap) setTimeOffset() error { + stime, err := bs.GetServerTime() + if err != nil { + return err + } + lt := time.Now().UnixNano() / 1000000 + bs.timeOffset = lt - stime + return nil +} + +/** + *获取交割预估价 + */ +func (bs *BitgetSwap) 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 *BitgetSwap) GetFutureTicker(currency CurrencyPair, contractType string) (*Ticker, error) { + url := fmt.Sprintf("%s/api/swap/v1/instruments/%s/ticker", bs.baseUrl, bs.adaptSymbol(currency)) + tickerMap, err := HttpGet(bs.httpClient, url) + if err != nil { + return nil, err + } + + status, isOk := tickerMap["status"] + if !isOk || status != "ok" { + return nil, errors.New(tickerMap["err_msg"].(string)) + } + + data := tickerMap["data"].(interface{}) + _data := data.(map[string]interface{}) + + var ticker Ticker + ticker.Pair = currency + ticker.Date = ToUint64(_data["timestamp"]) + ticker.Last = ToFloat64(_data["last"]) + ticker.Buy = ToFloat64(_data["bidPrice"]) + ticker.Sell = ToFloat64(_data["best_ask"]) + ticker.High = ToFloat64(_data["high_24h"]) + ticker.Low = ToFloat64(_data["low_24h"]) + ticker.Vol = ToFloat64(_data["volume_24h"]) + return &ticker, nil +} + +/** +* 期货深度 +* @param currencyPair btc_usd:比特币 ltc_usd :莱特币 +* @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 +* @param size 获取深度档数 +* @return + */ + +func (bs *BitgetSwap) GetFutureDepth(currency CurrencyPair, contractType string, size int) (*Depth, error) { + panic("not implement") +} + +func (bs *BitgetSwap) GetTrades(contractType string, currencyPair CurrencyPair, since int64) ([]Trade, error) { + panic("not implement") +} + +/** +* 期货指数 +* @param currencyPair btc_usd:比特币 ltc_usd :莱特币 + */ +func (bs *BitgetSwap) GetFutureIndex(currencyPair CurrencyPair) (float64, error) { + panic("not implement") +} + +func (bs *BitgetSwap) doAuthRequest(method, uri string, param map[string]interface{}) ([]byte, error) { + timestamp := time.Now().Unix() * 1000 + headers := make(map[string]string) + headers["Content-Type"] = "application/json" + headers["ACCESS-KEY"] = bs.accessKey + headers["ACCESS-PASSPHRASE"] = bs.passphrase + headers["ACCESS-TIMESTAMP"] = strconv.Itoa(int(timestamp)) + headers["locale"] = "zh-CN" + + postBody := "" + if param != nil { + postBodyArray, _ := json.Marshal(param) + postBody = string(postBodyArray) + } + + payload := fmt.Sprintf("%d%s%s%s", timestamp, method, uri, postBody) + sign, _ := GetParamHmacSHA256Base64Sign(bs.secretKey, payload) + headers["ACCESS-SIGN"] = sign + resp, err := NewHttpRequest(bs.httpClient, method, bs.baseUrl+uri, postBody, headers) + + return resp, err +} + +/** +*全仓账户 + */ + +func (bs *BitgetSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAccount, error) { + if len(currencyPair) > 1 { + panic("not support") + } + uri := "/api/swap/v3/account/accounts" + + if len(currencyPair) == 1 { + uri = "/api/swap/v3/account/account?symbol=" + bs.adaptSymbol(currencyPair[0]) + } + + resp, err := bs.doAuthRequest(http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + type Account struct { + Equity float64 `json:"equity,string"` + FixedBalance string `json:"fixed_balance"` + ForwardContractFlag bool `json:"forwardContractFlag"` + Margin string `json:"margin"` + MarginFrozen string `json:"margin_frozen"` + MarginMode string `json:"margin_mode"` + RealizedPnl float64 `json:"realized_pnl,string"` + Symbol string `json:"symbol"` + Timestamp string `json:"timestamp"` + TotalAvailBalance float64 `json:"total_avail_balance,string"` + UnrealizedPnl float64 `json:"unrealized_pnl,string"` + } + + subAccount := make(map[Currency]FutureSubAccount) + if len(currencyPair) == 0 { + accs := make([]Account, 0) + err = json.Unmarshal(resp, &accs) + if err != nil { + return nil, err + } + for _, acc := range accs { + currency := Currency{ + Symbol: acc.Symbol, + Desc: "", + } + + subAccount[currency] = FutureSubAccount{ + Currency: currency, + AccountRights: acc.Equity, + KeepDeposit: acc.TotalAvailBalance, + ProfitReal: acc.RealizedPnl, + ProfitUnreal: acc.UnrealizedPnl, + RiskRate: 0, + } + } + } else { + acc := Account{} + err = json.Unmarshal(resp, &acc) + if err != nil { + return nil, err + } + currency := Currency{ + Symbol: acc.Symbol, + Desc: "", + } + + subAccount[currency] = FutureSubAccount{ + Currency: currency, + AccountRights: acc.Equity, + KeepDeposit: acc.TotalAvailBalance, + ProfitReal: acc.RealizedPnl, + ProfitUnreal: acc.UnrealizedPnl, + RiskRate: 0, + } + } + + return &FutureAccount{ + FutureSubAccounts: subAccount, + }, nil +} + +func (bs *BitgetSwap) PlaceFutureOrder(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (string, error) { + fOrder, err := bs.PlaceFutureOrder2(currencyPair, contractType, price, amount, openType, matchPrice, leverRate) + 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 *BitgetSwap) PlaceFutureOrder2(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (*FutureOrder, error) { + fOrder := &FutureOrder{ + Currency: currencyPair, + ClientOid: GenerateOrderClientId(32), + Price: ToFloat64(price), + Amount: ToFloat64(amount), + OrderType: openType, + LeverRate: leverRate, + ContractName: contractType, + } + + symbol := bs.adaptSymbol(currencyPair) + uri := "/api/swap/v3/order/placeOrder" + params := make(map[string]interface{}) + params["symbol"] = symbol + params["size"] = amount + params["client_oid"] = fOrder.ClientOid + params["type"] = strconv.Itoa(int(openType)) + params["match_price"] = strconv.Itoa(int(matchPrice)) + params["order_type"] = "0" + if matchPrice == 0 { + params["price"] = price + } + resp, err := bs.doAuthRequest(http.MethodPost, uri, params) + if err != nil { + return fOrder, err + } + + respmap := make(map[string]interface{}) + err = json.Unmarshal(resp, &respmap) + if err != nil { + return fOrder, err + } + + orderId := ToInt(respmap["order_id"]) + if orderId <= 0 { + return fOrder, errors.New(string(resp)) + } + fOrder.OrderID2 = respmap["order_id"].(string) + + return fOrder, nil +} + +func (bs *BitgetSwap) LimitFuturesOrder(currencyPair CurrencyPair, contractType, price, amount string, openType int, opt ...LimitOrderOptionalParameter) (*FutureOrder, error) { + return bs.PlaceFutureOrder2(currencyPair, contractType, price, amount, openType, 0, 10) +} + +func (bs *BitgetSwap) MarketFuturesOrder(currencyPair CurrencyPair, contractType, amount string, openType int) (*FutureOrder, error) { + 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 *BitgetSwap) FutureCancelOrder(currencyPair CurrencyPair, contractType, orderId string) (bool, error) { + uri := "/api/swap/v3/order/cancel_order" + + params := make(map[string]interface{}) + params["symbol"] = bs.adaptSymbol(currencyPair) + params["orderId"] = orderId + + resp, err := bs.doAuthRequest(http.MethodPost, uri, params) + + respmap := make(map[string]interface{}) + err = json.Unmarshal(resp, &respmap) + if err != nil { + return false, err + } + + result := respmap["result"].(bool) + if !result { + return false, errors.New(respmap["err_msg"].(string)) + } + return true, nil +} + +/** +* 用户持仓查询 +* @param symbol btc_usd:比特币 ltc_usd :莱特币 +* @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 +* @return + */ +func (bs *BitgetSwap) GetFuturePosition(currencyPair CurrencyPair, contractType string) ([]FuturePosition, error) { + symbol := bs.adaptSymbol(currencyPair) + + uri := "/api/swap/v3/position/singlePosition?symbol=" + symbol + + resp, err := bs.doAuthRequest(http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + type PositionRsp struct { + Holding []struct { + AvailPosition float64 `json:"avail_position,string"` + AvgCost float64 `json:"avg_cost,string"` //开仓平均价 + Leverage float64 `json:"leverage,string"` + LiquidationPrice float64 `json:"liquidation_price,string"` + Margin string `json:"margin"` + Position float64 `json:"position,string"` + RealizedPnl float64 `json:"realized_pnl,string"` + Side string `json:"side"` + Symbol string `json:"symbol"` + Timestamp string `json:"timestamp"` + } `json:"holding"` + MarginMode string `json:"margin_mode"` + } + + pos := PositionRsp{} + err = json.Unmarshal(resp, &pos) + if err != nil { + return nil, err + } + if len(pos.Holding) != 2 { + return nil, errors.New(fmt.Sprintf("position is not correct:%s", string(resp))) + } + + var positions []FuturePosition + p := FuturePosition{ + LeverRate: pos.Holding[0].Leverage, + Symbol: currencyPair, + ForceLiquPrice: pos.Holding[0].LiquidationPrice, + } + for _, info := range pos.Holding { + + if info.Symbol != symbol { + continue + } + + if info.Side == "long" { + p.BuyAmount = info.Position + p.BuyAvailable = info.AvailPosition + p.BuyPriceAvg = info.AvgCost + p.BuyProfitReal = info.RealizedPnl + } else { + p.SellAmount = info.Position + p.SellAvailable = info.AvailPosition + p.SellPriceAvg = info.AvgCost + p.SellProfitReal = info.RealizedPnl + } + } + positions = append(positions, p) + return positions, nil +} + +/** +*获取订单信息 + */ +func (bs *BitgetSwap) GetFutureOrders(orderIds []string, currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + panic("not implement") +} + +/** +*获取单个订单信息 + */ +func (bs *BitgetSwap) GetFutureOrder(orderId string, currencyPair CurrencyPair, contractType string) (*FutureOrder, error) { + symbol := bs.adaptSymbol(currencyPair) + + uri := fmt.Sprintf("/api/swap/v3/order/detail?symbol=%s&orderId=%s", symbol, orderId) + + resp, err := bs.doAuthRequest(http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + result := make(map[string]interface{}) + err = json.Unmarshal(resp, &result) + if err != nil { + return nil, err + } + order := &FutureOrder{} + + order.Currency = currencyPair + order.Price = ToFloat64(result["price"]) + order.Amount = ToFloat64(result["size"]) + order.AvgPrice = ToFloat64(result["price_avg"]) + order.OrderID2 = orderId + order.DealAmount = ToFloat64(result["filled_qty"]) + order.Fee = ToFloat64(result["fee"]) + order.OType = ToInt(result["type"]) + order.ClientOid, _ = result["clientOid"].(string) + + status := ToInt(result["status"]) + switch status { + case -1: + order.Status = ORDER_CANCEL + case 0: + order.Status = ORDER_UNFINISH + case 1: + order.Status = ORDER_PART_FINISH + case 2: + order.Status = ORDER_FINISH + default: + order.Status = ORDER_UNFINISH + } + return order, nil +} + +/** +*获取未完成订单信息 + */ +func (bs *BitgetSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { + symbol := bs.adaptSymbol(currencyPair) + + uri := fmt.Sprintf("/api/swap/v3/order/orders?symbol=%s&from=1&to=1&limit=100&status=0", symbol) + + resp, err := bs.doAuthRequest(http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + result := make([]interface{}, 0) + err = json.Unmarshal(resp, &result) + if err != nil { + return nil, err + } + + orders := make([]FutureOrder, 0) + for _, v := range result { + vv := v.(map[string]interface{}) + order := FutureOrder{} + order.Currency = currencyPair + order.Price = ToFloat64(vv["price"]) + order.Amount = ToFloat64(vv["size"]) + order.AvgPrice = ToFloat64(vv["price_avg"]) + order.OrderID2 = vv["order_id"].(string) + order.DealAmount = ToFloat64(vv["filled_qty"]) + order.Fee = ToFloat64(vv["fee"]) + order.OType = ToInt(vv["type"]) + order.ClientOid = vv["client_oid"].(string) + + status := ToInt(vv["status"]) + switch status { + case -1: + order.Status = ORDER_CANCEL + case 0: + order.Status = ORDER_UNFINISH + + case 1: + order.Status = ORDER_PART_FINISH + case 2: + order.Status = ORDER_FINISH + default: + order.Status = ORDER_UNFINISH + } + orders = append(orders, order) + } + + return orders, nil +} + +/** +*获取交易费 + */ +func (bs *BitgetSwap) GetFee() (float64, error) { + panic("not supported.") +} + +/** +*获取每张合约价值 + */ +func (bs *BitgetSwap) GetContractValue(currencyPair CurrencyPair) (float64, error) { + panic("not supported.") +} + +/** +*获取交割时间 星期(0,1,2,3,4,5,6),小时,分,秒 + */ +func (bs *BitgetSwap) GetDeliveryTime() (int, int, int, int) { + panic("not supported.") +} + +/** +* 获取K线数据 + */ +func (bs *BitgetSwap) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { + panic("not supported.") +} + +func (bs *BitgetSwap) GetServerTime() (int64, error) { + respmap, err := HttpGet(bs.httpClient, fmt.Sprintf("%s/api/swap/v3/market/time", bs.baseUrl)) + if err != nil { + return 0, err + } + + stime := int64(ToInt(respmap["timestamp"])) + + return stime, nil +} + +func (bs *BitgetSwap) adaptSymbol(pair CurrencyPair) string { + symbol := strings.ToLower(pair.ToSymbol("")) + if pair.CurrencyB == USDT { + symbol = "cmt_" + symbol + } + + return symbol +} + +type MarginLeverage struct { + LongLeverage float64 `json:"long_leverage,string"` + MarginMode string `json:"margin_mode"` + ShortLeverage float64 `json:"short_leverage,string"` + ForwardContractFlag bool `json:"forwardContractFlag"` + Symbol string `json:"symbol"` +} + +// side +//1:多仓 +//2:空仓 +func (bs *BitgetSwap) SetMarginLevel(currencyPair CurrencyPair, level, side int) (*MarginLeverage, error) { + uri := "/api/swap/v3/account/leverage" + + reqBody := make(map[string]interface{}) + reqBody["leverage"] = strconv.Itoa(level) + reqBody["side"] = strconv.Itoa(side) + reqBody["symbol"] = bs.adaptSymbol(currencyPair) + + resp, err := bs.doAuthRequest(http.MethodPost, uri, reqBody) + if err != nil { + return nil, err + } + margin := MarginLeverage{} + err = json.Unmarshal(resp, &margin) + if err != nil { + return nil, err + } + + return &margin, nil +} + +func (bs *BitgetSwap) GetMarginLevel(currencyPair CurrencyPair) (*MarginLeverage, error) { + uri := "/api/swap/v3/account/settings?symbol=" + bs.adaptSymbol(currencyPair) + + resp, err := bs.doAuthRequest(http.MethodGet, uri, nil) + if err != nil { + return nil, err + } + margin := MarginLeverage{} + err = json.Unmarshal(resp, &margin) + if err != nil { + return nil, err + } + + return &margin, nil +} + +type Instrument struct { + Coin string `json:"coin"` + ContractVal string `json:"contract_val"` + Delivery []interface{} `json:"delivery"` + ForwardContractFlag bool `json:"forwardContractFlag"` + Listing interface{} `json:"listing"` + PriceEndStep int `json:"priceEndStep"` + QuoteCurrency string `json:"quote_currency"` + SizeIncrement int `json:"size_increment"` + Symbol string `json:"symbol"` + TickSize int `json:"tick_size"` + UnderlyingIndex string `json:"underlying_index"` +} + +func (bs *BitgetSwap) GetContractInfo(pair CurrencyPair) (*Instrument, error) { + url := fmt.Sprintf("%s/api/swap/v3/market/contracts", bs.baseUrl) + resp, err := HttpGet3(bs.httpClient, url, nil) + if err != nil { + return nil, err + } + for _, v := range resp { + contract := v.(map[string]interface{}) + if contract["quote_currency"].(string) == pair.CurrencyB.String() && contract["underlying_index"].(string) == pair.CurrencyA.String() { + return &Instrument{ + Coin: contract["coin"].(string), + ContractVal: contract["contract_val"].(string), + Delivery: contract["delivery"].([]interface{}), + ForwardContractFlag: contract["forwardContractFlag"].(bool), + PriceEndStep: ToInt(contract["priceEndStep"]), + QuoteCurrency: contract["quote_currency"].(string), + SizeIncrement: ToInt(contract["size_increment"]), + Symbol: contract["contract_val"].(string), + TickSize: ToInt(contract["tick_size"]), + UnderlyingIndex: contract["underlying_index"].(string), + }, nil + } + } + return nil, errors.New("not found") +} + +func (bs *BitgetSwap) GetInstruments() ([]Instrument, error) { + url := fmt.Sprintf("%s/api/swap/v3/market/contracts", bs.baseUrl) + resp, err := HttpGet3(bs.httpClient, url, nil) + if err != nil { + return nil, err + } + ins := make([]Instrument, 0) + for _, v := range resp { + contract := v.(map[string]interface{}) + ins = append(ins, Instrument{ + Coin: contract["coin"].(string), + ContractVal: contract["contract_val"].(string), + Delivery: contract["delivery"].([]interface{}), + ForwardContractFlag: contract["forwardContractFlag"].(bool), + PriceEndStep: ToInt(contract["priceEndStep"]), + QuoteCurrency: contract["quote_currency"].(string), + SizeIncrement: ToInt(contract["size_increment"]), + Symbol: contract["contract_val"].(string), + TickSize: ToInt(contract["tick_size"]), + UnderlyingIndex: contract["underlying_index"].(string), + }) + } + return ins, nil +} + +// side +//1:多仓 +//2:空仓 +// autoAppend追加保证金类型 +//0 不自动追加 1 自动追加 +func (bs *BitgetSwap) ModifyAutoAppendMargin(currencyPair CurrencyPair, side int, autoAppend int) (bool, error) { + uri := "/api/swap/v3/account/modifyAutoAppendMargin" + reqBody := make(map[string]interface{}) + reqBody["append_type"] = autoAppend + reqBody["side"] = side + reqBody["symbol"] = bs.adaptSymbol(currencyPair) + + resp, err := bs.doAuthRequest(http.MethodPost, uri, reqBody) + if err != nil { + return false, err + } + + autoMargin := make(map[string]interface{}) + err = json.Unmarshal(resp, &autoMargin) + if err != nil { + return false, err + } + if !autoMargin["result"].(bool) { + return false, nil + } + + return true, nil + +} diff --git a/bitget/bitget_swap_test.go b/bitget/bitget_swap_test.go new file mode 100644 index 00000000..8a5a1918 --- /dev/null +++ b/bitget/bitget_swap_test.go @@ -0,0 +1,71 @@ +package bitget + +import ( + "github.com/nntaoli-project/goex" + "net/http" + "net/url" + "testing" +) + +var bg = NewSwap(&goex.APIConfig{ + HttpClient: &http.Client{ + Transport: &http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { + return &url.URL{ + Scheme: "socks5", + Host: "127.0.0.1:1080"}, nil + }, + }, + }, //需要代理的这样配置 + Endpoint: "https://capi.bitget.io", + ClientId: "", + Lever: 0, +}) + +func TestBitgetSwap_GetFutureTicker(t *testing.T) { + t.Log(bg.GetFutureTicker(goex.ETH_USDT, "")) +} + +func TestBitgetSwap_GetServerTime(t *testing.T) { + t.Log(bg.GetServerTime()) +} + +func TestBitgetSwap_GetFutureUserinfo(t *testing.T) { + t.Log(bg.GetFutureUserinfo(goex.ETH_USDT)) +} + +func TestBitgetSwap_LimitFuturesOrder(t *testing.T) { + t.Log(bg.LimitFuturesOrder(goex.ETH_USDT, "", "350", "1", goex.CLOSE_BUY)) +} + +func TestBitgetSwap_GetFuturePosition(t *testing.T) { + t.Log(bg.GetFuturePosition(goex.ETH_USDT, "")) +} + +func TestBitgetSwap_GetUnfinishFutureOrders(t *testing.T) { + t.Log(bg.GetUnfinishFutureOrders(goex.ETH_USDT, "")) +} + +func TestBitgetSwap_SetMarginLevel(t *testing.T) { + t.Log(bg.SetMarginLevel(goex.ETH_USDT, 10, 2)) +} + +func TestBitgetSwap_GetMarginLevel(t *testing.T) { + t.Log(bg.GetMarginLevel(goex.ETH_USDT)) +} + +func TestBitgetSwap_GetContractInfo(t *testing.T) { + t.Log(bg.GetContractInfo(goex.ETH_USDT)) +} + +func TestBitgetSwap_GetFutureOrder(t *testing.T) { + t.Log(bg.GetFutureOrder("671529783552638913", goex.ETH_USDT, "")) +} + +func TestBitgetSwap_FutureCancelOrder(t *testing.T) { + t.Log(bg.FutureCancelOrder(goex.ETH_USDT, "", "671529783552638913")) +} + +func TestBitgetSwap_ModifyAutoAppendMargin(t *testing.T) { + t.Log(bg.ModifyAutoAppendMargin(goex.ETH_USDT, 1, 1)) +} diff --git a/bitmex/bitmex.go b/bitmex/bitmex.go index a95fe0ac..b124d257 100644 --- a/bitmex/bitmex.go +++ b/bitmex/bitmex.go @@ -119,12 +119,12 @@ type BitmexOrder struct { Timestamp time.Time `json:"timestamp"` } -func (bm *bitmex) PlaceFutureOrder(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice, leverRate int) (string, error) { +func (bm *bitmex) PlaceFutureOrder(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (string, error) { fOrder, err := bm.PlaceFutureOrder2(currencyPair, contractType, price, amount, openType, matchPrice, leverRate) return fOrder.OrderID2, err } -func (bm *bitmex) PlaceFutureOrder2(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice, leverRate int) (*FutureOrder, error) { +func (bm *bitmex) PlaceFutureOrder2(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice int, leverRate float64) (*FutureOrder, error) { var createOrderParameter BitmexOrder var resp struct { @@ -216,7 +216,7 @@ func (bm *bitmex) GetFuturePosition(currencyPair CurrencyPair, contractType stri OpenOrderSellQty float64 `json:"OpenOrderSellQty"` OpeningTimestamp time.Time `json:"openingTimestamp"` LiquidationPrice float64 `json:"liquidationPrice"` - Leverage int `json:"leverage"` + Leverage float64 `json:"leverage"` } param = url.Values{} ) diff --git a/bitstamp/Bitstamp.go b/bitstamp/Bitstamp.go index e2690ea0..80b3a943 100644 --- a/bitstamp/Bitstamp.go +++ b/bitstamp/Bitstamp.go @@ -97,6 +97,21 @@ func (bitstamp *Bitstamp) GetAccount() (*Account, error) { Amount: ToFloat64(respmap["bch_available"]), ForzenAmount: ToFloat64(respmap["bch_reserved"]), LoanAmount: 0} + acc.SubAccounts[GBP] = SubAccount{ + Currency: GBP, + Amount: ToFloat64(respmap["gbp_available"]), + ForzenAmount: ToFloat64(respmap["gbp_reserved"]), + LoanAmount: 0} + acc.SubAccounts[PAX] = SubAccount{ + Currency: PAX, + Amount: ToFloat64(respmap["pax_available"]), + ForzenAmount: ToFloat64(respmap["pax_reserved"]), + LoanAmount: 0} + acc.SubAccounts[XLM] = SubAccount{ + Currency: XLM, + Amount: ToFloat64(respmap["xlm_available"]), + ForzenAmount: ToFloat64(respmap["xlm_reserved"]), + LoanAmount: 0} return &acc, nil } diff --git a/huobi/Hbdm.go b/huobi/Hbdm.go index 78ef73f7..df17e3a7 100644 --- a/huobi/Hbdm.go +++ b/huobi/Hbdm.go @@ -199,35 +199,72 @@ func (dm *Hbdm) GetFuturePosition(currencyPair CurrencyPair, contractType string // log.Println(data) - var positions []FuturePosition + var ( + positions []FuturePosition + positionMap = make(map[string]FuturePosition, 1) + ) + for _, d := range data { + if d.ContractType == "next_quarter" { + d.ContractType = BI_QUARTER_CONTRACT + } + if d.ContractType != contractType { continue } + pos := positionMap[d.ContractCode] + pos.ContractType = d.ContractType + pos.ContractId = int64(ToInt(d.ContractCode[3:])) + pos.Symbol = currencyPair + switch d.Direction { case "buy": - positions = append(positions, FuturePosition{ - ContractType: d.ContractType, - ContractId: int64(ToInt(d.ContractCode[3:])), - Symbol: currencyPair, - BuyAmount: d.Volume, - BuyAvailable: d.Available, - BuyPriceAvg: d.CostOpen, - BuyPriceCost: d.CostHold, - BuyProfitReal: d.ProfitRate, - LeverRate: d.LeverRate}) + //positions = append(positions, FuturePosition{ + // ContractType: d.ContractType, + // ContractId: int64(ToInt(d.ContractCode[3:])), + // Symbol: currencyPair, + // BuyAmount: d.Volume, + // BuyAvailable: d.Available, + // BuyPriceAvg: d.CostOpen, + // BuyPriceCost: d.CostHold, + // BuyProfitReal: d.ProfitRate, + // BuyProfit: d.Profit, + // LeverRate: d.LeverRate}) + pos.BuyAmount = d.Volume + pos.BuyAvailable = d.Available + pos.BuyPriceAvg = d.CostOpen + pos.BuyPriceCost = d.CostHold + pos.BuyProfit = d.Profit + pos.BuyProfitReal = d.ProfitRate + pos.LeverRate = d.LeverRate case "sell": - positions = append(positions, FuturePosition{ - ContractType: d.ContractType, - ContractId: int64(ToInt(d.ContractCode[3:])), - Symbol: currencyPair, - SellAmount: d.Volume, - SellAvailable: d.Available, - SellPriceAvg: d.CostOpen, - SellPriceCost: d.CostHold, - SellProfitReal: d.ProfitRate, - LeverRate: d.LeverRate}) + // positions = append(positions, FuturePosition{ + // ContractType: d.ContractType, + // ContractId: int64(ToInt(d.ContractCode[3:])), + // Symbol: currencyPair, + // SellAmount: d.Volume, + // SellAvailable: d.Available, + // SellPriceAvg: d.CostOpen, + // SellPriceCost: d.CostHold, + // SellProfitReal: d.ProfitRate, + // SellProfit: d.Profit, + // LeverRate: d.LeverRate}) + pos.SellAmount = d.Volume + pos.SellAvailable = d.Available + pos.SellPriceAvg = d.CostOpen + pos.SellPriceCost = d.CostHold + pos.SellProfit = d.Profit + pos.SellProfitReal = d.ProfitRate + pos.LeverRate = d.LeverRate + } + + positionMap[d.ContractCode] = pos + } + + for _, pos := range positionMap { + if pos.BuyAmount > 0 || pos.SellAmount > 0 { + positions = append(positions, pos) } } diff --git a/okex/OKEx.go b/okex/OKEx.go index fd202caa..4256ef6b 100644 --- a/okex/OKEx.go +++ b/okex/OKEx.go @@ -16,14 +16,15 @@ import ( const baseUrl = "https://www.okex.com" type OKEx struct { - config *APIConfig - OKExSpot *OKExSpot - OKExFuture *OKExFuture - OKExSwap *OKExSwap - OKExWallet *OKExWallet - OKExMargin *OKExMargin - OKExV3FutureWs *OKExV3FuturesWs - OKExV3SpotWs *OKExV3SpotWs + config *APIConfig + OKExSpot *OKExSpot + OKExFuture *OKExFuture + OKExSwap *OKExSwap + OKExWallet *OKExWallet + OKExMargin *OKExMargin + OKExV3FuturesWs *OKExV3FuturesWs + OKExV3SpotWs *OKExV3SpotWs + OKExV3SwapWs *OKExV3SwapWs } func NewOKEx(config *APIConfig) *OKEx { @@ -36,8 +37,9 @@ func NewOKEx(config *APIConfig) *OKEx { okex.OKExWallet = &OKExWallet{okex} okex.OKExMargin = &OKExMargin{okex} okex.OKExSwap = &OKExSwap{okex, config} - okex.OKExV3FutureWs = NewOKExV3FuturesWs(okex) + okex.OKExV3FuturesWs = NewOKExV3FuturesWs(okex) okex.OKExV3SpotWs = NewOKExSpotV3Ws(okex) + okex.OKExV3SwapWs = NewOKExV3SwapWs(okex) return okex } @@ -52,7 +54,7 @@ func (ok *OKEx) UUID() string { func (ok *OKEx) DoRequest(httpMethod, uri, reqBody string, response interface{}) error { url := ok.config.Endpoint + uri sign, timestamp := ok.doParamSign(httpMethod, uri, reqBody) - logger.Log.Debug("timestamp=", timestamp, ", sign=", sign) + //logger.Log.Debug("timestamp=", timestamp, ", sign=", sign) resp, err := NewHttpRequest(ok.config.HttpClient, httpMethod, url, reqBody, map[string]string{ CONTENT_TYPE: APPLICATION_JSON_UTF8, ACCEPT: APPLICATION_JSON, diff --git a/okex/OKExFuturesWs_test.go b/okex/OKExFuturesWs_test.go index c266021c..c58b6337 100644 --- a/okex/OKExFuturesWs_test.go +++ b/okex/OKExFuturesWs_test.go @@ -22,17 +22,17 @@ func TestNewOKExV3FuturesWs(t *testing.T) { ok := NewOKEx(&goex.APIConfig{ HttpClient: http.DefaultClient, }) - ok.OKExV3FutureWs.TickerCallback(func(ticker *goex.FutureTicker) { + ok.OKExV3FuturesWs.TickerCallback(func(ticker *goex.FutureTicker) { t.Log(ticker.Ticker, ticker.ContractType) }) - ok.OKExV3FutureWs.DepthCallback(func(depth *goex.Depth) { + ok.OKExV3FuturesWs.DepthCallback(func(depth *goex.Depth) { t.Log(depth) }) - ok.OKExV3FutureWs.TradeCallback(func(trade *goex.Trade, s string) { + ok.OKExV3FuturesWs.TradeCallback(func(trade *goex.Trade, s string) { t.Log(s, trade) }) - //ok.OKExV3FutureWs.SubscribeTicker(goex.EOS_USD, goex.QUARTER_CONTRACT) - ok.OKExV3FutureWs.SubscribeDepth(goex.EOS_USD, goex.QUARTER_CONTRACT) - //ok.OKExV3FutureWs.SubscribeTrade(goex.EOS_USD, goex.QUARTER_CONTRACT) + //ok.OKExV3FuturesWs.SubscribeTicker(goex.EOS_USD, goex.QUARTER_CONTRACT) + ok.OKExV3FuturesWs.SubscribeDepth(goex.EOS_USD, goex.QUARTER_CONTRACT) + //ok.OKExV3FuturesWs.SubscribeTrade(goex.EOS_USD, goex.QUARTER_CONTRACT) time.Sleep(1 * time.Minute) } diff --git a/okex/OKExSwapWs.go b/okex/OKExSwapWs.go new file mode 100644 index 00000000..7ca1d6cb --- /dev/null +++ b/okex/OKExSwapWs.go @@ -0,0 +1,317 @@ +package okex + +import ( + "encoding/json" + "errors" + "fmt" + . "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "sort" + "strconv" + "strings" + "time" +) + +type OKExV3SwapWs struct { + base *OKEx + v3Ws *OKExV3Ws + tickerCallback func(*FutureTicker) + depthCallback func(*Depth) + tradeCallback func(*Trade, string) + klineCallback func(*FutureKline, int) +} + +func NewOKExV3SwapWs(base *OKEx) *OKExV3SwapWs { + okV3Ws := &OKExV3SwapWs{ + base: base, + } + okV3Ws.v3Ws = NewOKExV3Ws(base, okV3Ws.handle) + return okV3Ws +} + +func (okV3Ws *OKExV3SwapWs) TickerCallback(tickerCallback func(*FutureTicker)) { + okV3Ws.tickerCallback = tickerCallback +} + +func (okV3Ws *OKExV3SwapWs) DepthCallback(depthCallback func(*Depth)) { + okV3Ws.depthCallback = depthCallback +} + +func (okV3Ws *OKExV3SwapWs) TradeCallback(tradeCallback func(*Trade, string)) { + okV3Ws.tradeCallback = tradeCallback +} + +func (okV3Ws *OKExV3SwapWs) KlineCallback(klineCallback func(*FutureKline, int)) { + okV3Ws.klineCallback = klineCallback +} + +func (okV3Ws *OKExV3SwapWs) SetCallbacks(tickerCallback func(*FutureTicker), + depthCallback func(*Depth), + tradeCallback func(*Trade, string), + klineCallback func(*FutureKline, int)) { + okV3Ws.tickerCallback = tickerCallback + okV3Ws.depthCallback = depthCallback + okV3Ws.tradeCallback = tradeCallback + okV3Ws.klineCallback = klineCallback +} + +func (okV3Ws *OKExV3SwapWs) getChannelName(currencyPair CurrencyPair, contractType string) string { + var ( + prefix string + contractId string + channelName string + ) + + if contractType == SWAP_CONTRACT { + prefix = "swap" + contractId = fmt.Sprintf("%s-SWAP", currencyPair.ToSymbol("-")) + } else { + prefix = "futures" + contractId = okV3Ws.base.OKExFuture.GetFutureContractId(currencyPair, contractType) + // logger.Info("contractid=", contractId) + } + + if contractId == "" { + return "" + } + + channelName = prefix + "/%s:" + contractId + + return channelName +} + +func (okV3Ws *OKExV3SwapWs) SubscribeDepth(currencyPair CurrencyPair, contractType string) error { + if okV3Ws.depthCallback == nil { + return errors.New("please set depth callback func") + } + + chName := okV3Ws.getChannelName(currencyPair, contractType) + if chName == "" { + return errors.New("subscribe error, get channel name fail") + } + + return okV3Ws.v3Ws.Subscribe(map[string]interface{}{ + "op": "subscribe", + "args": []string{fmt.Sprintf(chName, "depth5")}}) +} + +func (okV3Ws *OKExV3SwapWs) SubscribeTicker(currencyPair CurrencyPair, contractType string) error { + if okV3Ws.tickerCallback == nil { + return errors.New("please set ticker callback func") + } + + chName := okV3Ws.getChannelName(currencyPair, contractType) + if chName == "" { + return errors.New("subscribe error, get channel name fail") + } + + return okV3Ws.v3Ws.Subscribe(map[string]interface{}{ + "op": "subscribe", + "args": []string{fmt.Sprintf(chName, "ticker")}}) +} + +func (okV3Ws *OKExV3SwapWs) SubscribeTrade(currencyPair CurrencyPair, contractType string) error { + if okV3Ws.tradeCallback == nil { + return errors.New("please set trade callback func") + } + + chName := okV3Ws.getChannelName(currencyPair, contractType) + if chName == "" { + return errors.New("subscribe error, get channel name fail") + } + + return okV3Ws.v3Ws.Subscribe(map[string]interface{}{ + "op": "subscribe", + "args": []string{fmt.Sprintf(chName, "trade")}}) +} + +func (okV3Ws *OKExV3SwapWs) SubscribeKline(currencyPair CurrencyPair, contractType string, period int) error { + if okV3Ws.klineCallback == nil { + return errors.New("place set kline callback func") + } + + seconds := adaptKLinePeriod(KlinePeriod(period)) + if seconds == -1 { + return fmt.Errorf("unsupported kline period %d in okex", period) + } + + chName := okV3Ws.getChannelName(currencyPair, contractType) + if chName == "" { + return errors.New("subscribe error, get channel name fail") + } + + return okV3Ws.v3Ws.Subscribe(map[string]interface{}{ + "op": "subscribe", + "args": []string{fmt.Sprintf(chName, fmt.Sprintf("candle%ds", seconds))}}) +} + +func (okV3Ws *OKExV3SwapWs) getContractAliasAndCurrencyPairFromInstrumentId(instrumentId string) (alias string, pair CurrencyPair) { + if strings.HasSuffix(instrumentId, "SWAP") { + ar := strings.Split(instrumentId, "-") + return instrumentId, NewCurrencyPair2(fmt.Sprintf("%s_%s", ar[0], ar[1])) + } else { + contractInfo, err := okV3Ws.base.OKExFuture.GetContractInfo(instrumentId) + if err != nil { + logger.Error("instrument id invalid:", err) + return "", UNKNOWN_PAIR + } + alias = contractInfo.Alias + pair = NewCurrencyPair2(fmt.Sprintf("%s_%s", contractInfo.UnderlyingIndex, contractInfo.QuoteCurrency)) + return alias, pair + } +} + +func (okV3Ws *OKExV3SwapWs) handle(channel string, data json.RawMessage) error { + var ( + err error + ch string + tickers []tickerResponse + depthResp []depthResponse + dep Depth + tradeResponse []struct { + Side string `json:"side"` + TradeId int64 `json:"trade_id,string"` + Price float64 `json:"price,string"` + Qty float64 `json:"qty,string"` + InstrumentId string `json:"instrument_id"` + Timestamp string `json:"timestamp"` + } + klineResponse []struct { + Candle []string `json:"candle"` + InstrumentId string `json:"instrument_id"` + } + ) + + if strings.Contains(channel, "futures/candle") || + strings.Contains(channel, "swap/candle") { + ch = "candle" + } else { + ch, err = okV3Ws.v3Ws.parseChannel(channel) + if err != nil { + logger.Errorf("[%s] parse channel err=%s , originChannel=%s", okV3Ws.base.GetExchangeName(), err, ch) + return nil + } + } + + switch ch { + case "ticker": + err = json.Unmarshal(data, &tickers) + if err != nil { + return err + } + + for _, t := range tickers { + alias, pair := okV3Ws.getContractAliasAndCurrencyPairFromInstrumentId(t.InstrumentId) + date, _ := time.Parse(time.RFC3339, t.Timestamp) + okV3Ws.tickerCallback(&FutureTicker{ + Ticker: &Ticker{ + Pair: pair, + Last: t.Last, + Buy: t.BestBid, + Sell: t.BestAsk, + High: t.High24h, + Low: t.Low24h, + Vol: t.Volume24h, + Date: uint64(date.UnixNano() / int64(time.Millisecond)), + }, + ContractId: t.InstrumentId, + ContractType: alias, + }) + } + return nil + case "candle": + err = json.Unmarshal(data, &klineResponse) + if err != nil { + return err + } + + for _, t := range klineResponse { + _, pair := okV3Ws.getContractAliasAndCurrencyPairFromInstrumentId(t.InstrumentId) + ts, _ := time.Parse(time.RFC3339, t.Candle[0]) + //granularity := adaptKLinePeriod(KlinePeriod(period)) + okV3Ws.klineCallback(&FutureKline{ + Kline: &Kline{ + Pair: pair, + High: ToFloat64(t.Candle[2]), + Low: ToFloat64(t.Candle[3]), + Timestamp: ts.Unix(), + Open: ToFloat64(t.Candle[1]), + Close: ToFloat64(t.Candle[4]), + Vol: ToFloat64(t.Candle[5]), + }, + Vol2: ToFloat64(t.Candle[6]), + }, 1) + } + return nil + case "depth5": + err := json.Unmarshal(data, &depthResp) + if err != nil { + logger.Error(err) + return err + } + if len(depthResp) == 0 { + return nil + } + alias, pair := okV3Ws.getContractAliasAndCurrencyPairFromInstrumentId(depthResp[0].InstrumentId) + dep.Pair = pair + dep.ContractType = alias + dep.ContractId = depthResp[0].InstrumentId + dep.UTime, _ = time.Parse(time.RFC3339, depthResp[0].Timestamp) + for _, itm := range depthResp[0].Asks { + dep.AskList = append(dep.AskList, DepthRecord{ + Price: ToFloat64(itm[0]), + Amount: ToFloat64(itm[1])}) + } + for _, itm := range depthResp[0].Bids { + dep.BidList = append(dep.BidList, DepthRecord{ + Price: ToFloat64(itm[0]), + Amount: ToFloat64(itm[1])}) + } + sort.Sort(sort.Reverse(dep.AskList)) + //call back func + okV3Ws.depthCallback(&dep) + return nil + case "trade": + err := json.Unmarshal(data, &tradeResponse) + if err != nil { + logger.Error("unmarshal error :", err) + return err + } + + for _, resp := range tradeResponse { + alias, pair := okV3Ws.getContractAliasAndCurrencyPairFromInstrumentId(resp.InstrumentId) + + tradeSide := SELL + switch resp.Side { + case "buy": + tradeSide = BUY + } + + t, err := time.Parse(time.RFC3339, resp.Timestamp) + if err != nil { + logger.Warn("parse timestamp error:", err) + } + + okV3Ws.tradeCallback(&Trade{ + Tid: resp.TradeId, + Type: tradeSide, + Amount: resp.Qty, + Price: resp.Price, + Date: t.Unix(), + Pair: pair, + }, alias) + } + return nil + } + + return fmt.Errorf("[%s] unknown websocket message: %s", ch, string(data)) +} + +func (okV3Ws *OKExV3SwapWs) getKlinePeriodFormChannel(channel string) int { + metas := strings.Split(channel, ":") + if len(metas) != 2 { + return 0 + } + i, _ := strconv.ParseInt(metas[1], 10, 64) + return int(i) +} diff --git a/okex/OKExSwapWs_test.go b/okex/OKExSwapWs_test.go new file mode 100644 index 00000000..4f9076c6 --- /dev/null +++ b/okex/OKExSwapWs_test.go @@ -0,0 +1,32 @@ +package okex + +import ( + "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "net/http" + "os" + "testing" + "time" +) + +func init() { + logger.SetLevel(logger.DEBUG) +} + +func TestNewOKExV3SwapWs(t *testing.T) { + os.Setenv("HTTPS_PROXY", "socks5://127.0.0.1:1080") + ok := NewOKEx(&goex.APIConfig{ + HttpClient: http.DefaultClient, + }) + ok.OKExV3SwapWs.TickerCallback(func(ticker *goex.FutureTicker) { + t.Log(ticker.Ticker, ticker.ContractType) + }) + ok.OKExV3SwapWs.DepthCallback(func(depth *goex.Depth) { + t.Log(depth) + }) + ok.OKExV3SwapWs.TradeCallback(func(trade *goex.Trade, s string) { + t.Log(s, trade) + }) + ok.OKExV3SwapWs.SubscribeTicker(goex.BTC_USDT, goex.SWAP_CONTRACT) + time.Sleep(1 * time.Minute) +} diff --git a/okex/OKExSwap_test.go b/okex/OKExSwap_test.go index 1e412bba..65afd1cb 100644 --- a/okex/OKExSwap_test.go +++ b/okex/OKExSwap_test.go @@ -31,15 +31,19 @@ func TestOKExSwap_GetFutureUserinfo(t *testing.T) { } func TestOKExSwap_PlaceFutureOrder(t *testing.T) { - t.Log(okExSwap.PlaceFutureOrder(goex.XRP_USD, goex.SWAP_CONTRACT, "0.2", "10", goex.OPEN_BUY, 0, 0)) + t.Log(okExSwap.PlaceFutureOrder(goex.BTC_USDT, goex.SWAP_CONTRACT, "10000", "1", goex.OPEN_BUY, 0, 0)) +} + +func TestOKExSwap_PlaceFutureOrder2(t *testing.T) { + t.Log(okExSwap.PlaceFutureOrder2(goex.BTC_USDT, goex.SWAP_CONTRACT, "10000", "1", goex.OPEN_BUY, 0, goex.Ioc)) } func TestOKExSwap_FutureCancelOrder(t *testing.T) { - t.Log(okExSwap.FutureCancelOrder(goex.XRP_USD, goex.SWAP_CONTRACT, "309935122485305344")) + t.Log(okExSwap.FutureCancelOrder(goex.BTC_USDT, goex.SWAP_CONTRACT, "309935122485305344")) } func TestOKExSwap_GetFutureOrder(t *testing.T) { - t.Log(okExSwap.GetFutureOrder("309935122485305344", goex.XRP_USD, goex.SWAP_CONTRACT)) + t.Log(okExSwap.GetFutureOrder("581084124456583168", goex.BTC_USDT, goex.SWAP_CONTRACT)) } func TestOKExSwap_GetFuturePosition(t *testing.T) { diff --git a/okex/OKEx_test.go b/okex/OKEx_test.go index e4502658..ffff25f3 100644 --- a/okex/OKEx_test.go +++ b/okex/OKEx_test.go @@ -77,7 +77,7 @@ func TestOKExSpot_CancelOrder(t *testing.T) { } func TestOKExSpot_GetOneOrder(t *testing.T) { - t.Log(okex.OKExSpot.GetOneOrder("42152275c599444aa8ec1d33bd8003fb", goex.BTC_USD)) + t.Log(okex.OKExSpot.GetOneOrder("5502594029936640", goex.BTC_USD)) } func TestOKExSpot_GetUnfinishOrders(t *testing.T) { @@ -160,25 +160,25 @@ func TestOKExWallet_GetAccount(t *testing.T) { t.Log(okex.OKExWallet.GetAccount()) } -func TestOKExWallet_Transfer(t *testing.T) { - t.Log(okex.OKExWallet.Transfer(TransferParameter{ - Currency: goex.EOS.Symbol, - From: SPOT, - To: SPOT_MARGIN, - Amount: 20, - InstrumentId: goex.EOS_USDT.ToLower().ToSymbol("-")})) -} - -func TestOKExWallet_Withdrawal(t *testing.T) { - t.Log(okex.OKExWallet.Withdrawal(WithdrawParameter{ - Currency: goex.EOS.Symbol, - Amount: 100, - Destination: 2, - ToAddress: "", - TradePwd: "", - Fee: "0.01", - })) -} +//func TestOKExWallet_Transfer(t *testing.T) { +// t.Log(okex.OKExWallet.Transfer(TransferParameter{ +// Currency: goex.EOS.Symbol, +// From: SPOT, +// To: SPOT_MARGIN, +// Amount: 20, +// InstrumentId: goex.EOS_USDT.ToLower().ToSymbol("-")})) +//} +// +//func TestOKExWallet_Withdrawal(t *testing.T) { +// t.Log(okex.OKExWallet.Withdrawal(WithdrawParameter{ +// Currency: goex.EOS.Symbol, +// Amount: 100, +// Destination: 2, +// ToAddress: "", +// TradePwd: "", +// Fee: "0.01", +// })) +//} func TestOKExWallet_GetDepositAddress(t *testing.T) { t.Log(okex.OKExWallet.GetDepositAddress(goex.BTC)) @@ -193,7 +193,7 @@ func TestOKExWallet_GetDepositHistory(t *testing.T) { } func TestOKExWallet_GetWithDrawalHistory(t *testing.T) { - t.Log(okex.OKExWallet.GetWithDrawalHistory(&goex.XRP)) + //t.Log(okex.OKExWallet.GetWithDrawalHistory(&goex.XRP)) } func TestOKExMargin_GetMarginAccount(t *testing.T) {