├── .gitignore ├── API.go ├── APIUtils.go ├── Adapter.go ├── ApiErrors.go ├── Const.go ├── CurrencyPair.go ├── CurrencyPair_test.go ├── FutureRestAPI.go ├── HttpUtils.go ├── IMG_1177.jpg ├── Metas.go ├── ParamSignUtils.go ├── README.md ├── Utils.go ├── aacoin ├── aacoin.go └── aacoin_test.go ├── acx ├── Acx.go └── Acx_test.go ├── aex ├── Aex.go └── Aex_test.go ├── allcoin ├── allcoin.go └── allcoin_test.go ├── banner.jpg ├── bigone ├── Bigone.go └── Bigone_test.go ├── binance ├── Binance.go └── Binance_test.go ├── bitfinex ├── BitfinexLending.go ├── BitfinexMarginTrading.go ├── bitfinex.go └── bitfinex_test.go ├── bithumb ├── bithumb.go └── bithumb_test.go ├── bitmex ├── bitmex.go └── bitmex_test.go ├── bitstamp ├── Bitstamp.go └── Bitstamp_test.go ├── bittrex ├── bittrex.go └── bittrex_test.go ├── btcbox ├── btcbox.go └── btcbox_test.go ├── btcc ├── BTCChina.go └── BTCChina_test.go ├── btcmarkets ├── Btcmarkets.go └── Btcmarkets_test.go ├── builder ├── APIBuilder.go └── APIBuilder_test.go ├── c-cex ├── C-cex.go └── C-cex_test.go ├── coin58 ├── coin58.go └── coin58_test.go ├── coinbig ├── coinbig.go └── coinbig_test.go ├── coincheck ├── Coincheck.go └── Coincheck_test.go ├── coinex ├── coinex.go └── coinex_test.go ├── coinpark ├── coinpark.go └── coinpark_test.go ├── cryptopia ├── Cryptopia.go ├── Cryptopia_test.go └── README.md ├── exx ├── exx.go └── exx_test.go ├── fcoin ├── fcoin.go └── fcoin_test.go ├── gateio ├── gateio.go └── gateio_test.go ├── gdax ├── gdax.go └── gdax_test.go ├── hashkey ├── Hashkey.go └── Hashkey_test.go ├── hitbtc ├── Hitbtc.go └── Hitbtc_test.go ├── huobi ├── Hbdm.go ├── Hbdm_test.go ├── HuobiPro.go └── HuobiPro_test.go ├── kraken ├── Kraken.go └── Kraken_test.go ├── liqui └── Liqui.go ├── ocx ├── ocx.go └── ocx_test.go ├── okcoin ├── OKCoin_CN.go ├── OKCoin_CN_test.go ├── OKCoin_COM.go ├── OKEx.go ├── OKExSpot.go ├── OKExSpot_test.go ├── OKEx_Future_Ws.go ├── OKEx_Future_Ws_test.go ├── OKEx_Spot_Ws.go ├── OKEx_Spot_Ws_test.go ├── OKEx_test.go └── OKcoin_COM_test.go ├── okex ├── OKExSwap.go ├── OKExSwap_test.go ├── ParamSignUtil.go └── SwapResponse.go ├── poloniex ├── Poloniex.go └── PoloniexMarginTrading.go ├── websocket.go ├── websocket_test.go ├── wx_pay.JPG └── zb ├── Zb.go └── Zb_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /API.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | // api interface 4 | 5 | type API interface { 6 | LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) 7 | LimitSell(amount, price string, currency CurrencyPair) (*Order, error) 8 | MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) 9 | MarketSell(amount, price string, currency CurrencyPair) (*Order, error) 10 | CancelOrder(orderId string, currency CurrencyPair) (bool, error) 11 | GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) 12 | GetUnfinishOrders(currency CurrencyPair) ([]Order, error) 13 | GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) 14 | GetAccount() (*Account, error) 15 | 16 | GetTicker(currency CurrencyPair) (*Ticker, error) 17 | GetDepth(size int, currency CurrencyPair) (*Depth, error) 18 | GetKlineRecords(currency CurrencyPair, period , size, since int) ([]Kline, error) 19 | //非个人,整个交易所的交易记录 20 | GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) 21 | 22 | GetExchangeName() string 23 | } 24 | -------------------------------------------------------------------------------- /APIUtils.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "reflect" 7 | "time" 8 | ) 9 | 10 | /** 11 | @retry 重试次数 12 | @method 调用的函数,比如: api.GetTicker ,注意:不是api.GetTicker(...) 13 | @params 参数,顺序一定要按照实际调用函数入参顺序一样 14 | @return 返回 15 | */ 16 | func RE(retry int, method interface{}, params ...interface{}) interface{} { 17 | 18 | invokeM := reflect.ValueOf(method) 19 | if invokeM.Kind() != reflect.Func { 20 | panic("method not a function") 21 | return nil 22 | } 23 | 24 | var value []reflect.Value = make([]reflect.Value, len(params)) 25 | var i int = 0 26 | for ; i < len(params); i++ { 27 | value[i] = reflect.ValueOf(params[i]) 28 | } 29 | 30 | var retV interface{} 31 | var retryC int = 0 32 | _CALL: 33 | if retryC > 0 { 34 | log.Println("sleep....", time.Duration(retryC*200*int(time.Millisecond))) 35 | time.Sleep(time.Duration(retryC * 200 * int(time.Millisecond))) 36 | } 37 | 38 | retValues := invokeM.Call(value) 39 | 40 | for _, vl := range retValues { 41 | if vl.Type().String() == "error" { 42 | if !vl.IsNil() { 43 | log.Println(vl) 44 | retryC++ 45 | if retryC <= retry { 46 | log.Printf("Invoke Method[%s] Error , Begin Retry Call [%d] ...", invokeM.String(), retryC) 47 | goto _CALL 48 | } else { 49 | panic("Invoke Method Fail ???" + invokeM.String()) 50 | } 51 | } 52 | } else { 53 | retV = vl.Interface() 54 | } 55 | } 56 | 57 | return retV 58 | } 59 | 60 | /** 61 | * call all unfinished orders 62 | */ 63 | func CancelAllUnfinishedOrders(api API, currencyPair CurrencyPair) int { 64 | if api == nil { 65 | log.Println("api instance is nil ??? , please new a api instance") 66 | return -1 67 | } 68 | 69 | orders := RE(10, api.GetUnfinishOrders, currencyPair) 70 | if orders != nil { 71 | c := 0 72 | for _, ord := range orders.([]Order) { 73 | _, err := api.CancelOrder(ord.OrderID2, currencyPair) 74 | if err != nil { 75 | log.Println(err) 76 | } 77 | c++ 78 | time.Sleep(100 * time.Millisecond) //控制频率 79 | } 80 | 81 | return c 82 | } 83 | return 0 84 | } 85 | 86 | /** 87 | * call all unfinished future orders 88 | */ 89 | func CancelAllUnfinishedFutureOrders(api FutureRestAPI, contractType string, currencyPair CurrencyPair) { 90 | if api == nil { 91 | log.Println("api instance is nil ??? , please new a api instance") 92 | return 93 | } 94 | 95 | orders := RE(10, api.GetUnfinishFutureOrders, currencyPair, contractType) 96 | if orders != nil { 97 | for _, ord := range orders.([]Order) { 98 | _, err := api.FutureCancelOrder(currencyPair, contractType, fmt.Sprintf("%d", ord.OrderID)) 99 | if err != nil { 100 | log.Println(err) 101 | } 102 | time.Sleep(100 * time.Millisecond) //控制频率 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Adapter.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import "strings" 4 | 5 | func AdaptTradeSide(side string) TradeSide { 6 | side2 := strings.ToUpper(side) 7 | switch side2 { 8 | case "SELL": 9 | return SELL 10 | case "BUY": 11 | return BUY 12 | case "BUY_MARKET": 13 | return BUY_MARKET 14 | case "SELL_MARKET": 15 | return SELL_MARKET 16 | default: 17 | return -1 18 | } 19 | } 20 | 21 | func AdaptKlinePeriodForOKEx(period int) string { 22 | switch period { 23 | case KLINE_PERIOD_1MIN: 24 | return "1min" 25 | case KLINE_PERIOD_5MIN: 26 | return "5min" 27 | case KLINE_PERIOD_15MIN: 28 | return "15min" 29 | case KLINE_PERIOD_30MIN: 30 | return "30min" 31 | case KLINE_PERIOD_1H: 32 | return "1hour" 33 | case KLINE_PERIOD_4H: 34 | return "4hour" 35 | case KLINE_PERIOD_1DAY: 36 | return "day" 37 | case KLINE_PERIOD_2H: 38 | return "2hour" 39 | case KLINE_PERIOD_1WEEK: 40 | return "week" 41 | default: 42 | return "day" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ApiErrors.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | type ApiError struct { 4 | ErrCode, 5 | ErrMsg, 6 | OriginErrMsg string 7 | } 8 | 9 | func (e ApiError) Error() string { 10 | return e.ErrMsg 11 | } 12 | 13 | func (e ApiError) OriginErr(err string) ApiError { 14 | e.ErrMsg = err 15 | return e 16 | } 17 | 18 | var ( 19 | API_ERR = ApiError{ErrCode: "EX_ERR_0000", ErrMsg: "unknown error"} 20 | HTTP_ERR_CODE = ApiError{ErrCode: "HTTP_ERR_0001", ErrMsg: "http request error"} 21 | EX_ERR_API_LIMIT = ApiError{ErrCode: "EX_ERR_1000", ErrMsg: "api limited"} 22 | EX_ERR_SIGN = ApiError{ErrCode: "EX_ERR_0001", ErrMsg: "signature error"} 23 | EX_ERR_NOT_FIND_SECRETKEY = ApiError{ErrCode: "EX_ERR_0002", ErrMsg: "not find secretkey"} 24 | EX_ERR_NOT_FIND_APIKEY = ApiError{ErrCode: "EX_ERR_0003", ErrMsg: "not find apikey"} 25 | EX_ERR_INSUFFICIENT_BALANCE = ApiError{ErrCode: "EX_ERR_0004", ErrMsg: "Insufficient Balance"} 26 | EX_ERR_PLACE_ORDER_FAIL = ApiError{ErrCode: "EX_ERR_0005", ErrMsg: "place order failure"} 27 | EX_ERR_CANCEL_ORDER_FAIL = ApiError{ErrCode: "EX_ERR_0006", ErrMsg: "cancel order failure"} 28 | EX_ERR_INVALID_CURRENCY_PAIR = ApiError{ErrCode: "EX_ERR_0007", ErrMsg: "invalid currency pair"} 29 | EX_ERR_NOT_FIND_ORDER = ApiError{ErrCode: "EX_ERR_0008", ErrMsg: "not find order"} 30 | EX_ERR_SYMBOL_ERR = ApiError{ErrCode: "EX_ERR_0009", ErrMsg: "symbol error"} 31 | ) 32 | -------------------------------------------------------------------------------- /Const.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | type TradeSide int 4 | 5 | const ( 6 | BUY = 1 + iota 7 | SELL 8 | BUY_MARKET 9 | SELL_MARKET 10 | ) 11 | 12 | func (ts TradeSide) String() string { 13 | switch ts { 14 | case 1: 15 | return "BUY" 16 | case 2: 17 | return "SELL" 18 | case 3: 19 | return "BUY_MARKET" 20 | case 4: 21 | return "SELL_MARKET" 22 | default: 23 | return "UNKNOWN" 24 | } 25 | } 26 | 27 | type TradeStatus int 28 | 29 | func (ts TradeStatus) String() string { 30 | return tradeStatusSymbol[ts] 31 | } 32 | 33 | var tradeStatusSymbol = [...]string{"UNFINISH", "PART_FINISH", "FINISH", "CANCEL", "REJECT", "CANCEL_ING"} 34 | 35 | const ( 36 | ORDER_UNFINISH = iota 37 | ORDER_PART_FINISH 38 | ORDER_FINISH 39 | ORDER_CANCEL 40 | ORDER_REJECT 41 | ORDER_CANCEL_ING 42 | ) 43 | 44 | const ( 45 | OPEN_BUY = 1 + iota //开多 46 | OPEN_SELL //开空 47 | CLOSE_BUY //平多 48 | CLOSE_SELL //平空 49 | ) 50 | 51 | //k线周期 52 | const ( 53 | KLINE_PERIOD_1MIN = 1 + iota 54 | KLINE_PERIOD_3MIN 55 | KLINE_PERIOD_5MIN 56 | KLINE_PERIOD_15MIN 57 | KLINE_PERIOD_30MIN 58 | KLINE_PERIOD_60MIN 59 | KLINE_PERIOD_1H 60 | KLINE_PERIOD_2H 61 | KLINE_PERIOD_4H 62 | KLINE_PERIOD_6H 63 | KLINE_PERIOD_8H 64 | KLINE_PERIOD_12H 65 | KLINE_PERIOD_1DAY 66 | KLINE_PERIOD_3DAY 67 | KLINE_PERIOD_1WEEK 68 | KLINE_PERIOD_1MONTH 69 | KLINE_PERIOD_1YEAR 70 | ) 71 | 72 | var ( 73 | THIS_WEEK_CONTRACT = "this_week" //周合约 74 | NEXT_WEEK_CONTRACT = "next_week" //次周合约 75 | QUARTER_CONTRACT = "quarter" //季度合约 76 | ) 77 | 78 | //exchanges const 79 | const ( 80 | OKCOIN_CN = "okcoin.cn" 81 | OKCOIN_COM = "okcoin.com" 82 | OKEX = "okex.com" 83 | OKEX_FUTURE = "okex.com" 84 | OKEX_SWAP = "okex.com_swap" 85 | HUOBI = "huobi.com" 86 | HUOBI_PRO = "huobi.pro" 87 | BITSTAMP = "bitstamp.net" 88 | KRAKEN = "kraken.com" 89 | ZB = "zb.com" 90 | BITFINEX = "bitfinex.com" 91 | BINANCE = "binance.com" 92 | POLONIEX = "poloniex.com" 93 | COINEX = "coinex.com" 94 | BITHUMB = "bithumb.com" 95 | GATEIO = "gate.io" 96 | BITTREX = "bittrex.com" 97 | GDAX = "gdax.com" 98 | WEX_NZ = "wex.nz" 99 | BIGONE = "big.one" 100 | COIN58 = "58coin.com" 101 | FCOIN = "fcoin.com" 102 | HITBTC = "hitbtc.com" 103 | BITMEX = "bitmex.com" 104 | CRYPTOPIA = "cryptopia.co.nz" 105 | HBDM = "hbdm.com" 106 | ) 107 | -------------------------------------------------------------------------------- /CurrencyPair.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import "strings" 4 | 5 | type Currency struct { 6 | Symbol string 7 | Desc string 8 | } 9 | 10 | func (c Currency) String() string { 11 | return c.Symbol 12 | } 13 | 14 | func (c Currency) Eq(c2 Currency) bool { 15 | return c.Symbol == c2.Symbol 16 | } 17 | 18 | // A->B(A兑换为B) 19 | type CurrencyPair struct { 20 | CurrencyA Currency 21 | CurrencyB Currency 22 | } 23 | 24 | var ( 25 | UNKNOWN = Currency{"UNKNOWN", ""} 26 | CNY = Currency{"CNY", ""} 27 | USD = Currency{"USD", ""} 28 | USDT = Currency{"USDT", ""} 29 | PAX = Currency{"PAX", "https://www.paxos.com/"} 30 | USDC = Currency{"USDC", "https://www.centre.io/"} 31 | EUR = Currency{"EUR", ""} 32 | KRW = Currency{"KRW", ""} 33 | JPY = Currency{"JPY", ""} 34 | BTC = Currency{"BTC", "https://bitcoin.org/"} 35 | XBT = Currency{"XBT", ""} 36 | BCC = Currency{"BCC", ""} 37 | BCH = Currency{"BCH", ""} 38 | BCX = Currency{"BCX", ""} 39 | LTC = Currency{"LTC", ""} 40 | ETH = Currency{"ETH", ""} 41 | ETC = Currency{"ETC", ""} 42 | EOS = Currency{"EOS", ""} 43 | BTS = Currency{"BTS", ""} 44 | QTUM = Currency{"QTUM", ""} 45 | SC = Currency{"SC", ""} 46 | ANS = Currency{"ANS", ""} 47 | ZEC = Currency{"ZEC", ""} 48 | DCR = Currency{"DCR", ""} 49 | XRP = Currency{"XRP", ""} 50 | BTG = Currency{"BTG", ""} 51 | BCD = Currency{"BCD", ""} 52 | NEO = Currency{"NEO", ""} 53 | HSR = Currency{"HSR", ""} 54 | BSV = Currency{"BSV", ""} 55 | OKB = Currency{"OKB", "OKB is a global utility token issued by OK Blockchain Foundation"} 56 | HT = Currency{"HT", "HuoBi Token"} 57 | BNB = Currency{"BNB", "BNB, or Binance Coin, is a cryptocurrency created by Binance."} 58 | 59 | //currency pair 60 | 61 | BTC_CNY = CurrencyPair{BTC, CNY} 62 | LTC_CNY = CurrencyPair{LTC, CNY} 63 | BCC_CNY = CurrencyPair{BCC, CNY} 64 | ETH_CNY = CurrencyPair{ETH, CNY} 65 | ETC_CNY = CurrencyPair{ETC, CNY} 66 | EOS_CNY = CurrencyPair{EOS, CNY} 67 | BTS_CNY = CurrencyPair{BTS, CNY} 68 | QTUM_CNY = CurrencyPair{QTUM, CNY} 69 | SC_CNY = CurrencyPair{SC, CNY} 70 | ANS_CNY = CurrencyPair{ANS, CNY} 71 | ZEC_CNY = CurrencyPair{ZEC, CNY} 72 | 73 | BTC_KRW = CurrencyPair{BTC, KRW} 74 | ETH_KRW = CurrencyPair{ETH, KRW} 75 | ETC_KRW = CurrencyPair{ETC, KRW} 76 | LTC_KRW = CurrencyPair{LTC, KRW} 77 | BCH_KRW = CurrencyPair{BCH, KRW} 78 | 79 | BTC_USD = CurrencyPair{BTC, USD} 80 | LTC_USD = CurrencyPair{LTC, USD} 81 | ETH_USD = CurrencyPair{ETH, USD} 82 | ETC_USD = CurrencyPair{ETC, USD} 83 | BCH_USD = CurrencyPair{BCH, USD} 84 | BCC_USD = CurrencyPair{BCC, USD} 85 | XRP_USD = CurrencyPair{XRP, USD} 86 | BCD_USD = CurrencyPair{BCD, USD} 87 | EOS_USD = CurrencyPair{EOS, USD} 88 | BTG_USD = CurrencyPair{BTG, USD} 89 | BSV_USD = CurrencyPair{BSV, USD} 90 | 91 | BTC_USDT = CurrencyPair{BTC, USDT} 92 | LTC_USDT = CurrencyPair{LTC, USDT} 93 | BCH_USDT = CurrencyPair{BCH, USDT} 94 | BCC_USDT = CurrencyPair{BCC, USDT} 95 | ETC_USDT = CurrencyPair{ETC, USDT} 96 | ETH_USDT = CurrencyPair{ETH, USDT} 97 | BCD_USDT = CurrencyPair{BCD, USDT} 98 | NEO_USDT = CurrencyPair{NEO, USDT} 99 | EOS_USDT = CurrencyPair{EOS, USDT} 100 | XRP_USDT = CurrencyPair{XRP, USDT} 101 | HSR_USDT = CurrencyPair{HSR, USDT} 102 | BSV_USDT = CurrencyPair{BSV, USDT} 103 | OKB_USDT = CurrencyPair{OKB, USDT} 104 | HT_USDT = CurrencyPair{HT, USDT} 105 | BNB_USDT = CurrencyPair{BNB, USDT} 106 | 107 | XRP_EUR = CurrencyPair{XRP, EUR} 108 | 109 | BTC_JPY = CurrencyPair{BTC, JPY} 110 | LTC_JPY = CurrencyPair{LTC, JPY} 111 | ETH_JPY = CurrencyPair{ETH, JPY} 112 | ETC_JPY = CurrencyPair{ETC, JPY} 113 | BCH_JPY = CurrencyPair{BCH, JPY} 114 | 115 | LTC_BTC = CurrencyPair{LTC, BTC} 116 | ETH_BTC = CurrencyPair{ETH, BTC} 117 | ETC_BTC = CurrencyPair{ETC, BTC} 118 | BCC_BTC = CurrencyPair{BCC, BTC} 119 | BCH_BTC = CurrencyPair{BCH, BTC} 120 | DCR_BTC = CurrencyPair{DCR, BTC} 121 | XRP_BTC = CurrencyPair{XRP, BTC} 122 | BTG_BTC = CurrencyPair{BTG, BTC} 123 | BCD_BTC = CurrencyPair{BCD, BTC} 124 | NEO_BTC = CurrencyPair{NEO, BTC} 125 | EOS_BTC = CurrencyPair{EOS, BTC} 126 | HSR_BTC = CurrencyPair{HSR, BTC} 127 | BSV_BTC = CurrencyPair{BSV, BTC} 128 | OKB_BTC = CurrencyPair{OKB, BTC} 129 | HT_BTC = CurrencyPair{HT, BTC} 130 | BNB_BTC = CurrencyPair{BNB, BTC} 131 | 132 | ETC_ETH = CurrencyPair{ETC, ETH} 133 | EOS_ETH = CurrencyPair{EOS, ETH} 134 | ZEC_ETH = CurrencyPair{ZEC, ETH} 135 | NEO_ETH = CurrencyPair{NEO, ETH} 136 | HSR_ETH = CurrencyPair{HSR, ETH} 137 | LTC_ETH = CurrencyPair{LTC, ETH} 138 | 139 | UNKNOWN_PAIR = CurrencyPair{UNKNOWN, UNKNOWN} 140 | ) 141 | 142 | func (c CurrencyPair) String() string { 143 | return c.ToSymbol("_") 144 | } 145 | 146 | func (c CurrencyPair) Eq(c2 CurrencyPair) bool { 147 | return c.String() == c2.String() 148 | } 149 | 150 | func (c Currency) AdaptBchToBcc() Currency { 151 | if c.Symbol == "BCH" || c.Symbol == "bch" { 152 | return BCC 153 | } 154 | return c 155 | } 156 | 157 | func (c Currency) AdaptBccToBch() Currency { 158 | if c.Symbol == "BCC" || c.Symbol == "bcc" { 159 | return BCH 160 | } 161 | return c 162 | } 163 | 164 | func NewCurrency(symbol, desc string) Currency { 165 | switch symbol { 166 | case "cny", "CNY": 167 | return CNY 168 | case "usdt", "USDT": 169 | return USDT 170 | case "usd", "USD": 171 | return USD 172 | case "usdc", "USDC": 173 | return USDC 174 | case "pax", "PAX": 175 | return PAX 176 | case "jpy", "JPY": 177 | return JPY 178 | case "krw", "KRW": 179 | return KRW 180 | case "eur", "EUR": 181 | return EUR 182 | case "btc", "BTC": 183 | return BTC 184 | case "xbt", "XBT": 185 | return XBT 186 | case "bch", "BCH": 187 | return BCH 188 | case "bcc", "BCC": 189 | return BCC 190 | case "ltc", "LTC": 191 | return LTC 192 | case "sc", "SC": 193 | return SC 194 | case "ans", "ANS": 195 | return ANS 196 | case "neo", "NEO": 197 | return NEO 198 | case "okb", "OKB": 199 | return OKB 200 | case "ht", "HT": 201 | return HT 202 | case "bnb", "BNB": 203 | return BNB 204 | default: 205 | return Currency{strings.ToUpper(symbol), desc} 206 | } 207 | } 208 | 209 | func NewCurrencyPair(currencyA Currency, currencyB Currency) CurrencyPair { 210 | return CurrencyPair{currencyA, currencyB} 211 | } 212 | 213 | func NewCurrencyPair2(currencyPairSymbol string) CurrencyPair { 214 | currencys := strings.Split(currencyPairSymbol, "-") 215 | if len(currencys) == 2 { 216 | return CurrencyPair{NewCurrency(currencys[0], ""), 217 | NewCurrency(currencys[1], "")} 218 | } 219 | return UNKNOWN_PAIR 220 | } 221 | 222 | func (pair CurrencyPair) ToSymbol(joinChar string) string { 223 | return strings.Join([]string{pair.CurrencyA.Symbol, pair.CurrencyB.Symbol}, joinChar) 224 | } 225 | 226 | func (pair CurrencyPair) ToSymbol2(joinChar string) string { 227 | return strings.Join([]string{pair.CurrencyB.Symbol, pair.CurrencyA.Symbol}, joinChar) 228 | } 229 | 230 | func (pair CurrencyPair) AdaptUsdtToUsd() CurrencyPair { 231 | CurrencyB := pair.CurrencyB 232 | if pair.CurrencyB.Eq(USDT) { 233 | CurrencyB = USD 234 | } 235 | return CurrencyPair{pair.CurrencyA, CurrencyB} 236 | } 237 | 238 | func (pair CurrencyPair) AdaptUsdToUsdt() CurrencyPair { 239 | CurrencyB := pair.CurrencyB 240 | if pair.CurrencyB.Eq(USD) { 241 | CurrencyB = USDT 242 | } 243 | return CurrencyPair{pair.CurrencyA, CurrencyB} 244 | } 245 | 246 | //It is currently applicable to binance and zb 247 | func (pair CurrencyPair) AdaptBchToBcc() CurrencyPair { 248 | CurrencyA := pair.CurrencyA 249 | if pair.CurrencyA.Eq(BCH) { 250 | CurrencyA = BCC 251 | } 252 | return CurrencyPair{CurrencyA, pair.CurrencyB} 253 | } 254 | 255 | func (pair CurrencyPair) AdaptBccToBch() CurrencyPair { 256 | if pair.CurrencyA.Eq(BCC) { 257 | return CurrencyPair{BCH, pair.CurrencyB} 258 | } 259 | return pair 260 | } 261 | 262 | //for to symbol lower , Not practical '==' operation method 263 | func (pair CurrencyPair) ToLower() CurrencyPair { 264 | return CurrencyPair{Currency{strings.ToLower(pair.CurrencyA.Symbol), ""}, 265 | Currency{strings.ToLower(pair.CurrencyB.Symbol), ""}} 266 | } 267 | 268 | func (pair CurrencyPair) Reverse() CurrencyPair { 269 | return CurrencyPair{pair.CurrencyB, pair.CurrencyA} 270 | } 271 | -------------------------------------------------------------------------------- /CurrencyPair_test.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCurrency2_String(t *testing.T) { 8 | btc := NewCurrency("btc", "bitcoin") 9 | btc2 := Currency{"BTC", "bitcoin.org"} 10 | ltc := NewCurrency("ltc", "litecoin") 11 | t.Log(btc == BTC) 12 | t.Log(ltc.Desc, btc.Desc) 13 | t.Log(btc == btc2) 14 | } 15 | 16 | func TestCurrencyPair2_String(t *testing.T) { 17 | btc_usd := NewCurrencyPair(NewCurrency("btc", ""), NewCurrency("usd", "")) 18 | t.Log(btc_usd.String() == "BTC_USD") 19 | t.Log(btc_usd.ToLower().ToSymbol("") == "btcusd") 20 | t.Log(btc_usd.ToLower().String() == "btc_usd") 21 | t.Log(btc_usd.Reverse().String() == "USD_BTC") 22 | t.Log(btc_usd.Eq(BTC_USD)) 23 | } 24 | -------------------------------------------------------------------------------- /FutureRestAPI.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | type FutureRestAPI interface { 4 | /** 5 | *获取交易所名字 6 | */ 7 | GetExchangeName() string 8 | 9 | /** 10 | *获取交割预估价 11 | */ 12 | GetFutureEstimatedPrice(currencyPair CurrencyPair) (float64, error) 13 | 14 | /** 15 | * 期货行情 16 | * @param currency_pair btc_usd:比特币 ltc_usd :莱特币 17 | * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 18 | */ 19 | GetFutureTicker(currencyPair CurrencyPair, contractType string) (*Ticker, error) 20 | 21 | /** 22 | * 期货深度 23 | * @param currencyPair btc_usd:比特币 ltc_usd :莱特币 24 | * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 25 | * @param size 获取深度档数 26 | * @return 27 | */ 28 | GetFutureDepth(currencyPair CurrencyPair, contractType string, size int) (*Depth, error) 29 | 30 | /** 31 | * 期货指数 32 | * @param currencyPair btc_usd:比特币 ltc_usd :莱特币 33 | */ 34 | GetFutureIndex(currencyPair CurrencyPair) (float64, error) 35 | 36 | /** 37 | *全仓账户 38 | */ 39 | GetFutureUserinfo() (*FutureAccount, error) 40 | 41 | /** 42 | * 期货下单 43 | * @param currencyPair btc_usd:比特币 ltc_usd :莱特币 44 | * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 45 | * @param price 价格 46 | * @param amount 委托数量 47 | * @param openType 1:开多 2:开空 3:平多 4:平空 48 | * @param matchPrice 是否为对手价 0:不是 1:是 ,当取值为1时,price无效 49 | */ 50 | PlaceFutureOrder(currencyPair CurrencyPair, contractType, price, amount string, openType, matchPrice, leverRate int) (string, error) 51 | 52 | /** 53 | * 取消订单 54 | * @param symbol btc_usd:比特币 ltc_usd :莱特币 55 | * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 56 | * @param orderId 订单ID 57 | 58 | */ 59 | FutureCancelOrder(currencyPair CurrencyPair, contractType, orderId string) (bool, error) 60 | 61 | /** 62 | * 用户持仓查询 63 | * @param symbol btc_usd:比特币 ltc_usd :莱特币 64 | * @param contractType 合约类型: this_week:当周 next_week:下周 month:当月 quarter:季度 65 | * @return 66 | */ 67 | GetFuturePosition(currencyPair CurrencyPair, contractType string) ([]FuturePosition, error) 68 | 69 | /** 70 | *获取订单信息 71 | */ 72 | GetFutureOrders(orderIds []string, currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) 73 | 74 | /** 75 | *获取单个订单信息 76 | */ 77 | GetFutureOrder(orderId string, currencyPair CurrencyPair, contractType string) (*FutureOrder, error) 78 | 79 | /** 80 | *获取未完成订单信息 81 | */ 82 | GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) 83 | 84 | /** 85 | *获取交易费 86 | */ 87 | GetFee() (float64, error) 88 | 89 | /** 90 | *获取交易所的美元人民币汇率 91 | */ 92 | GetExchangeRate() (float64, error) 93 | 94 | /** 95 | *获取每张合约价值 96 | */ 97 | GetContractValue(currencyPair CurrencyPair) (float64, error) 98 | 99 | /** 100 | *获取交割时间 星期(0,1,2,3,4,5,6),小时,分,秒 101 | */ 102 | GetDeliveryTime() (int, int, int, int) 103 | 104 | /** 105 | * 获取K线数据 106 | */ 107 | GetKlineRecords(contract_type string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) 108 | 109 | /** 110 | * 获取Trade数据 111 | */ 112 | GetTrades(contract_type string, currencyPair CurrencyPair, since int64) ([]Trade, error) 113 | } 114 | -------------------------------------------------------------------------------- /HttpUtils.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | //http request 工具函数 4 | import ( 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | "net/url" 12 | "strings" 13 | ) 14 | 15 | func NewHttpRequest(client *http.Client, reqType string, reqUrl string, postData string, requstHeaders map[string]string) ([]byte, error) { 16 | req, _ := http.NewRequest(reqType, reqUrl, strings.NewReader(postData)) 17 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36") 18 | 19 | if requstHeaders != nil { 20 | for k, v := range requstHeaders { 21 | req.Header.Add(k, v) 22 | } 23 | } 24 | 25 | resp, err := client.Do(req) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | defer resp.Body.Close() 31 | 32 | bodyData, err := ioutil.ReadAll(resp.Body) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | if resp.StatusCode != 200 { 38 | return nil, errors.New(fmt.Sprintf("HttpStatusCode:%d ,Desc:%s", resp.StatusCode, string(bodyData))) 39 | } 40 | 41 | return bodyData, nil 42 | } 43 | 44 | func HttpGet(client *http.Client, reqUrl string) (ret map[string]interface{}, err error) { 45 | defer func() { 46 | if p := recover(); p != nil { 47 | err = p.(error) 48 | ret = nil 49 | return 50 | } 51 | }() 52 | respData, err := NewHttpRequest(client, "GET", reqUrl, "", nil) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | var bodyDataMap map[string]interface{} 58 | err = json.Unmarshal(respData, &bodyDataMap) 59 | if err != nil { 60 | log.Println(string(respData)) 61 | return nil, err 62 | } 63 | return bodyDataMap, nil 64 | } 65 | 66 | func HttpGet2(client *http.Client, reqUrl string, headers map[string]string) (map[string]interface{}, error) { 67 | if headers == nil { 68 | headers = map[string]string{} 69 | } 70 | headers["Content-Type"] = "application/x-www-form-urlencoded" 71 | respData, err := NewHttpRequest(client, "GET", reqUrl, "", headers) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | var bodyDataMap map[string]interface{} 77 | err = json.Unmarshal(respData, &bodyDataMap) 78 | if err != nil { 79 | log.Println("respData", string(respData)) 80 | return nil, err 81 | } 82 | return bodyDataMap, nil 83 | } 84 | 85 | func HttpGet3(client *http.Client, reqUrl string, headers map[string]string) ([]interface{}, error) { 86 | if headers == nil { 87 | headers = map[string]string{} 88 | } 89 | headers["Content-Type"] = "application/x-www-form-urlencoded" 90 | respData, err := NewHttpRequest(client, "GET", reqUrl, "", headers) 91 | if err != nil { 92 | return nil, err 93 | } 94 | 95 | var bodyDataMap []interface{} 96 | err = json.Unmarshal(respData, &bodyDataMap) 97 | if err != nil { 98 | log.Println("respData", string(respData)) 99 | return nil, err 100 | } 101 | return bodyDataMap, nil 102 | } 103 | 104 | func HttpGet4(client *http.Client, reqUrl string, headers map[string]string, result interface{}) error { 105 | if headers == nil { 106 | headers = map[string]string{} 107 | } 108 | headers["Content-Type"] = "application/x-www-form-urlencoded" 109 | respData, err := NewHttpRequest(client, "GET", reqUrl, "", headers) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | err = json.Unmarshal(respData, result) 115 | if err != nil { 116 | log.Printf("HttpGet4 - json.Unmarshal failed : %v, resp %s", err, string(respData)) 117 | return err 118 | } 119 | 120 | return nil 121 | } 122 | func HttpGet5(client *http.Client, reqUrl string, headers map[string]string) ([]byte, error) { 123 | if headers == nil { 124 | headers = map[string]string{} 125 | } 126 | headers["Content-Type"] = "application/x-www-form-urlencoded" 127 | respData, err := NewHttpRequest(client, "GET", reqUrl, "", headers) 128 | if err != nil { 129 | return nil, err 130 | } 131 | 132 | return respData, nil 133 | } 134 | 135 | func HttpPostForm(client *http.Client, reqUrl string, postData url.Values) ([]byte, error) { 136 | headers := map[string]string{ 137 | "Content-Type": "application/x-www-form-urlencoded"} 138 | return NewHttpRequest(client, "POST", reqUrl, postData.Encode(), headers) 139 | } 140 | 141 | func HttpPostForm2(client *http.Client, reqUrl string, postData url.Values, headers map[string]string) ([]byte, error) { 142 | if headers == nil { 143 | headers = map[string]string{} 144 | } 145 | headers["Content-Type"] = "application/x-www-form-urlencoded" 146 | return NewHttpRequest(client, "POST", reqUrl, postData.Encode(), headers) 147 | } 148 | 149 | func HttpPostForm3(client *http.Client, reqUrl string, postData string, headers map[string]string) ([]byte, error) { 150 | return NewHttpRequest(client, "POST", reqUrl, postData, headers) 151 | } 152 | 153 | func HttpPostForm4(client *http.Client, reqUrl string, postData map[string]string, headers map[string]string) ([]byte, error) { 154 | if headers == nil { 155 | headers = map[string]string{} 156 | } 157 | headers["Content-Type"] = "application/json" 158 | data, _ := json.Marshal(postData) 159 | return NewHttpRequest(client, "POST", reqUrl, string(data), headers) 160 | } 161 | 162 | func HttpDeleteForm(client *http.Client, reqUrl string, postData url.Values, headers map[string]string) ([]byte, error) { 163 | if headers == nil { 164 | headers = map[string]string{} 165 | } 166 | headers["Content-Type"] = "application/x-www-form-urlencoded" 167 | return NewHttpRequest(client, "DELETE", reqUrl, postData.Encode(), headers) 168 | } 169 | -------------------------------------------------------------------------------- /IMG_1177.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudian/GoEx/ca421ea49f0649ad6713e5e6c8897a5e61888e18/IMG_1177.jpg -------------------------------------------------------------------------------- /Metas.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | type Order struct { 8 | Price, 9 | Amount, 10 | AvgPrice, 11 | DealAmount, 12 | Fee float64 13 | OrderID2 string 14 | OrderID int 15 | OrderTime int 16 | Status TradeStatus 17 | Currency CurrencyPair 18 | Side TradeSide 19 | } 20 | 21 | type Trade struct { 22 | Tid int64 `json:"tid"` 23 | Type TradeSide `json:"type"` 24 | Amount float64 `json:"amount,string"` 25 | Price float64 `json:"price,string"` 26 | Date int64 `json:"date_ms"` 27 | Pair CurrencyPair `json:"omitempty"` 28 | } 29 | 30 | type SubAccount struct { 31 | Currency Currency 32 | Amount, 33 | ForzenAmount, 34 | LoanAmount float64 35 | } 36 | 37 | type Account struct { 38 | Exchange string 39 | Asset float64 //总资产 40 | NetAsset float64 //净资产 41 | SubAccounts map[Currency]SubAccount 42 | } 43 | 44 | type Ticker struct { 45 | Pair CurrencyPair `json:"omitempty"` 46 | Last float64 `json:"last,string"` 47 | Buy float64 `json:"buy,string"` 48 | Sell float64 `json:"sell,string"` 49 | High float64 `json:"high,string"` 50 | Low float64 `json:"low,string"` 51 | Vol float64 `json:"vol,string"` 52 | Date int64 `json:"date"` // 单位:秒(second) 53 | } 54 | 55 | func NewTicker() *Ticker { 56 | var tk Ticker 57 | tk.Last = 0 58 | tk.Buy = 0 59 | tk.Sell = 0 60 | tk.High =0 61 | tk.Low = 0 62 | tk.Vol=0 63 | return &tk 64 | } 65 | 66 | func (tk *Ticker)Add(ticker *Ticker) { 67 | tk.Last += ticker.Last 68 | tk.Buy += ticker.Buy 69 | tk.Sell += ticker.Sell 70 | tk.High += ticker.High 71 | tk.Low += ticker.Low 72 | tk.Vol += ticker.Vol 73 | } 74 | //division 75 | func (tk *Ticker)Div(n float64) *Ticker { 76 | tk.Last /= n 77 | tk.Buy /= n 78 | tk.Sell /= n 79 | tk.High /= n 80 | tk.Low /= n 81 | tk.Vol /= n 82 | return tk 83 | } 84 | 85 | //multiplication 86 | func (tk *Ticker)Multi(n float64) *Ticker { 87 | tk.Last *= n 88 | tk.Buy *= n 89 | tk.Sell *= n 90 | tk.High *= n 91 | tk.Low *= n 92 | tk.Vol *= n 93 | return tk 94 | } 95 | 96 | func (tk *Ticker)Decimal() *Ticker { 97 | tk.Last = Decimal(tk.Last) 98 | tk.Buy = Decimal(tk.Buy) 99 | tk.Sell = Decimal(tk.Sell) 100 | tk.High = Decimal(tk.High) 101 | tk.Low = Decimal(tk.Low) 102 | tk.Vol = Decimal(tk.Vol) 103 | return tk 104 | } 105 | 106 | type FutureTicker struct { 107 | *Ticker 108 | ContractType string `json:"omitempty"` 109 | ContractId int `json:"contractId"` 110 | LimitHigh float64 `json:"limitHigh,string"` 111 | LimitLow float64 `json:"limitLow,string"` 112 | HoldAmount float64 `json:"hold_amount,string"` 113 | UnitAmount float64 `json:"unitAmount,string"` 114 | } 115 | 116 | type DepthRecord struct { 117 | Price, 118 | Amount float64 119 | } 120 | 121 | type DepthRecords []DepthRecord 122 | 123 | func (dr DepthRecords) Len() int { 124 | return len(dr) 125 | } 126 | 127 | func (dr DepthRecords) Swap(i, j int) { 128 | dr[i], dr[j] = dr[j], dr[i] 129 | } 130 | 131 | func (dr DepthRecords) Less(i, j int) bool { 132 | return dr[i].Price < dr[j].Price 133 | } 134 | 135 | type Depth struct { 136 | ContractType string //for future 137 | Pair CurrencyPair 138 | //UTime time.Time 139 | Date int64 // 单位:秒(second) 140 | AskList, 141 | BidList DepthRecords 142 | } 143 | 144 | func NewDepth() *Depth { 145 | var depth Depth 146 | for i:=0; i<10; i++ { 147 | var dr DepthRecord 148 | dr.Price = ToFloat64(0) 149 | dr.Amount = ToFloat64(0) 150 | depth.AskList = append(depth.AskList, dr) 151 | depth.BidList = append(depth.BidList, dr) 152 | } 153 | return &depth 154 | } 155 | 156 | type APIConfig struct { 157 | HttpClient *http.Client 158 | Endpoint string 159 | ApiKey string 160 | ApiSecretKey string 161 | ApiPassphrase string //for okex.com v3 api 162 | ClientId string //for bitstamp.net , huobi.pro 163 | 164 | Lever int //杠杆倍数 , for future 165 | } 166 | 167 | type Kline struct { 168 | Pair CurrencyPair 169 | Timestamp int64 170 | Open, 171 | Close, 172 | High, 173 | Low, 174 | Vol float64 175 | } 176 | 177 | type FutureKline struct { 178 | *Kline 179 | Vol2 float64 //个数 180 | } 181 | 182 | type FutureSubAccount struct { 183 | Currency Currency 184 | AccountRights float64 //账户权益 185 | KeepDeposit float64 //保证金 186 | ProfitReal float64 //已实现盈亏 187 | ProfitUnreal float64 188 | RiskRate float64 //保证金率 189 | } 190 | 191 | type FutureAccount struct { 192 | FutureSubAccounts map[Currency]FutureSubAccount 193 | } 194 | 195 | type FutureOrder struct { 196 | OrderID2 string //请尽量用这个字段替代OrderID字段 197 | Price float64 198 | Amount float64 199 | AvgPrice float64 200 | DealAmount float64 201 | OrderID int64 202 | OrderTime int64 203 | Status TradeStatus 204 | Currency CurrencyPair 205 | OType int //1:开多 2:开空 3:平多 4: 平空 206 | LeverRate int //倍数 207 | Fee float64 //手续费 208 | ContractName string 209 | } 210 | 211 | type FuturePosition struct { 212 | BuyAmount float64 213 | BuyAvailable float64 214 | BuyPriceAvg float64 215 | BuyPriceCost float64 216 | BuyProfitReal float64 217 | CreateDate int64 218 | LeverRate int 219 | SellAmount float64 220 | SellAvailable float64 221 | SellPriceAvg float64 222 | SellPriceCost float64 223 | SellProfitReal float64 224 | Symbol CurrencyPair //btc_usd:比特币,ltc_usd:莱特币 225 | ContractType string 226 | ContractId int64 227 | ForceLiquPrice float64 //预估爆仓价 228 | } 229 | -------------------------------------------------------------------------------- /ParamSignUtils.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/md5" 6 | "crypto/sha1" 7 | "crypto/sha256" 8 | "crypto/sha512" 9 | "encoding/base64" 10 | "encoding/hex" 11 | ) 12 | 13 | /** 14 | *md5签名,okcoin和huobi适用 15 | */ 16 | func GetParamMD5Sign(secret, params string) (string, error) { 17 | hash := md5.New() 18 | _, err := hash.Write([]byte(params)) 19 | 20 | if err != nil { 21 | return "", err 22 | } 23 | 24 | return hex.EncodeToString(hash.Sum(nil)), nil 25 | } 26 | 27 | func GetSHA(text string) (string, error) { 28 | sha := sha1.New() 29 | _, err := sha.Write([]byte(text)) 30 | if err != nil { 31 | return "", err 32 | } 33 | return hex.EncodeToString(sha.Sum(nil)), nil 34 | } 35 | 36 | func GetParamHmacSHA256Sign(secret, params string) (string, error) { 37 | mac := hmac.New(sha256.New, []byte(secret)) 38 | _, err := mac.Write([]byte(params)) 39 | if err != nil { 40 | return "", err 41 | } 42 | return hex.EncodeToString(mac.Sum(nil)), nil 43 | } 44 | 45 | func GetParamHmacSHA512Sign(secret, params string) (string, error) { 46 | mac := hmac.New(sha512.New, []byte(secret)) 47 | _, err := mac.Write([]byte(params)) 48 | if err != nil { 49 | return "", err 50 | } 51 | return hex.EncodeToString(mac.Sum(nil)), nil 52 | } 53 | 54 | func GetParamHmacSHA1Sign(secret, params string) (string, error) { 55 | mac := hmac.New(sha1.New, []byte(secret)) 56 | _, err := mac.Write([]byte(params)) 57 | if err != nil { 58 | return "", err 59 | } 60 | return hex.EncodeToString(mac.Sum(nil)), nil 61 | } 62 | 63 | func GetParamHmacMD5Sign(secret, params string) (string, error) { 64 | mac := hmac.New(md5.New, []byte(secret)) 65 | _, err := mac.Write([]byte(params)) 66 | if err != nil { 67 | return "", err 68 | } 69 | return hex.EncodeToString(mac.Sum(nil)), nil 70 | } 71 | 72 | /*for bitfinex.com*/ 73 | func GetParamHmacSha384Sign(secret, params string) (string, error) { 74 | mac := hmac.New(sha512.New384, []byte(secret)) 75 | _, err := mac.Write([]byte(params)) 76 | if err != nil { 77 | return "", nil 78 | } 79 | return hex.EncodeToString(mac.Sum(nil)), nil 80 | } 81 | 82 | func GetParamHmacSHA256Base64Sign(secret, params string) (string, error) { 83 | mac := hmac.New(sha256.New, []byte(secret)) 84 | _, err := mac.Write([]byte(params)) 85 | if err != nil { 86 | return "", err 87 | } 88 | signByte := mac.Sum(nil) 89 | return base64.StdEncoding.EncodeToString(signByte), nil 90 | } 91 | 92 | func GetParamHmacSHA512Base64Sign(hmac_key string, hmac_data string) string { 93 | hmh := hmac.New(sha512.New, []byte(hmac_key)) 94 | hmh.Write([]byte(hmac_data)) 95 | 96 | hex_data := hex.EncodeToString(hmh.Sum(nil)) 97 | hash_hmac_bytes := []byte(hex_data) 98 | hmh.Reset() 99 | 100 | return base64.StdEncoding.EncodeToString(hash_hmac_bytes) 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### GoEx目标 2 | GoEx项目是为了统一并标准化各个数字资产交易平台的接口而设计,同一个策略可以随时切换到任意一个交易平台,而不需要更改任何代码。 3 | 4 | ### GoEx已支持交易所 `22+` 5 | | 交易所 | 行情接口 | 交易接口 | 版本号 | 6 | | --- | --- | --- | --- | 7 | | hbg.com | Y | Y | 1 | 8 | | hbdm.com | Y| Y | 1 | 9 | | okex.com (spot/future)| Y (REST / WS) | Y | 1 | 10 | | okex.com (swap future) | Y | Y | 2 | 11 | | binance.com | Y | Y | 1 | 12 | | bitstamp.net | Y | Y | 1 | 13 | | bitfinex.com | Y | Y | 1 | 14 | | zb.com | Y | Y | 1 | 15 | | kraken.com | Y | Y | * | 16 | | poloniex.com | Y | Y | * | 17 | | aacoin.com | Y | Y | 1 | 18 | | allcoin.ca | Y | Y | * | 19 | | big.one | Y | Y | 2 | 20 | | fcoin.com | Y | Y | 2 | 21 | | hitbtc.com | Y | Y | * | 22 | | coinex.com | Y | Y | 1 | 23 | | exx.com | Y | Y | 1 | 24 | | bithumb.com | Y | Y | * | 25 | | gate.io | Y | N | 1 | 26 | | btcbox.co.jp | Y | N | * | 27 | | bittrex.com | Y | N | 1.1 | 28 | | btcchina.com | Y | Y | 1 | 29 | | coinbig.com | Y | Y | * | 30 | 31 | ###安装GoEx库 ``` go get github.com/wudian/GoEx ``` 32 | 33 | ### 例子 34 | ```golang 35 | 36 | package main 37 | 38 | import ( 39 | "github.com/wudian/GoEx" 40 | "github.com/wudian/GoEx/builder" 41 | "log" 42 | "time" 43 | ) 44 | 45 | func main() { 46 | apiBuilder := builder.NewAPIBuilder().HttpTimeout(5 * time.Second) 47 | 48 | //build spot api 49 | api := apiBuilder.APIKey("").APISecretkey("").ClientID("123").Build(goex.BITSTAMP) 50 | log.Println(api.GetExchangeName()) 51 | log.Println(api.GetTicker(goex.BTC_USD)) 52 | log.Println(api.GetDepth(2, goex.BTC_USD)) 53 | //log.Println(api.GetAccount()) 54 | //log.Println(api.GetUnfinishOrders(goex.BTC_USD)) 55 | 56 | //build future api 57 | futureApi := apiBuilder.APIKey("").APISecretkey("").BuildFuture(goex.HBDM) 58 | log.Println(futureApi.GetExchangeName()) 59 | log.Println(futureApi.GetFutureTicker(goex.BTC_USD, goex.QUARTER_CONTRACT)) 60 | log.Println(futureApi.GetFutureDepth(goex.BTC_USD, goex.QUARTER_CONTRACT, 5)) 61 | //log.Println(futureApi.GetFutureUserinfo()) // account 62 | //log.Println(futureApi.GetFuturePosition(goex.BTC_USD , goex.QUARTER_CONTRACT))//position info 63 | } 64 | 65 | ``` 66 | 67 | ### 注意事项 68 | 1. 推荐使用GoLand开发。 69 | 2. 推荐关闭自动格式化功能。 70 | 3. 不建议对现已存在的文件进行重新格式化,这样会导致commit特别糟糕。 71 | 4. 请用OrderID2这个字段代替OrderID 72 | 73 | ----------------- 74 | -------------------------------------------------------------------------------- /Utils.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import ( 4 | "bytes" 5 | "compress/flate" 6 | "compress/gzip" 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "net/url" 11 | "strconv" 12 | ) 13 | 14 | func Decimal(value float64) float64 { 15 | value, _ = strconv.ParseFloat(fmt.Sprintf("%.6f", value), 64) 16 | return value 17 | } 18 | 19 | func ToFloat64(v interface{}) float64 { 20 | if v == nil { 21 | return 0.0 22 | } 23 | 24 | switch v.(type) { 25 | case float64: 26 | return v.(float64) 27 | case string: 28 | vStr := v.(string) 29 | vF, _ := strconv.ParseFloat(vStr, 64) 30 | return vF 31 | default: 32 | panic("to float64 error.") 33 | } 34 | } 35 | 36 | func ToInt(v interface{}) int { 37 | if v == nil { 38 | return 0 39 | } 40 | 41 | switch v.(type) { 42 | case string: 43 | vStr := v.(string) 44 | vInt, _ := strconv.Atoi(vStr) 45 | return vInt 46 | case int: 47 | return v.(int) 48 | case float64: 49 | vF := v.(float64) 50 | return int(vF) 51 | default: 52 | panic("to int error.") 53 | } 54 | } 55 | 56 | func ToUint64(v interface{}) uint64 { 57 | if v == nil { 58 | return 0 59 | } 60 | 61 | switch v.(type) { 62 | case int: 63 | return uint64(v.(int)) 64 | case float64: 65 | return uint64((v.(float64))) 66 | case string: 67 | uV, _ := strconv.ParseUint(v.(string), 10, 64) 68 | return uV 69 | default: 70 | panic("to uint64 error.") 71 | } 72 | } 73 | 74 | func ToInt64(v interface{}) int64 { 75 | if v == nil { 76 | return 0 77 | } 78 | 79 | switch v.(type) { 80 | case float64: 81 | return int64(v.(float64)) 82 | default: 83 | vv := fmt.Sprint(v) 84 | 85 | if vv == "" { 86 | return 0 87 | } 88 | 89 | vvv, err := strconv.ParseInt(vv, 0, 64) 90 | if err != nil { 91 | return 0 92 | } 93 | 94 | return vvv 95 | } 96 | } 97 | 98 | func ValuesToJson(v url.Values) ([]byte, error) { 99 | parammap := make(map[string]interface{}) 100 | for k, vv := range v { 101 | if len(vv) == 1 { 102 | parammap[k] = vv[0] 103 | } else { 104 | parammap[k] = vv 105 | } 106 | } 107 | return json.Marshal(parammap) 108 | } 109 | 110 | func GzipUnCompress(data []byte) ([]byte, error) { 111 | r, err := gzip.NewReader(bytes.NewReader(data)) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return ioutil.ReadAll(r) 116 | } 117 | 118 | func FlateUnCompress(data []byte) ([]byte, error) { 119 | return ioutil.ReadAll(flate.NewReader(bytes.NewReader(data))) 120 | } 121 | -------------------------------------------------------------------------------- /aacoin/aacoin_test.go: -------------------------------------------------------------------------------- 1 | package aacoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "net/url" 7 | "testing" 8 | ) 9 | 10 | var aa = New(http.DefaultClient, "xxx", "b0xxxxxx-c6xxxxxx-94xxxxxx-dxxxx") 11 | 12 | func TestAacoin_GetExchangeName(t *testing.T) { 13 | return 14 | params := url.Values{} 15 | params.Set("accessKey", aa.accessKey) 16 | params.Set("price", aa.accessKey) 17 | params.Set("quantity", aa.accessKey) 18 | params.Set("symbol", aa.accessKey) 19 | params.Set("type", aa.accessKey) 20 | t.Log(aa.buildSigned(¶ms)) 21 | } 22 | func TestAacoin_GetAccount(t *testing.T) { 23 | return 24 | t.Log(aa.GetAccount()) 25 | } 26 | func TestAacoin_GetTicker(t *testing.T) { 27 | return 28 | t.Log(aa.GetTicker(goex.BTC_USDT)) 29 | } 30 | func TestAacoin_GetDepth(t *testing.T) { 31 | return 32 | t.Log(aa.GetDepth(1, goex.BTC_USDT)) 33 | } 34 | func TestAacoin_LimitSell(t *testing.T) { 35 | //return 36 | t.Log(aa.LimitSell("1", "1000000", goex.BTC_USDT)) 37 | } 38 | 39 | func TestAacoin_LimitBuy(t *testing.T) { 40 | t.Log(aa.LimitBuy("1", "1", goex.BTC_USDT)) 41 | } 42 | 43 | func TestAacoin_GetUnfinishOrders(t *testing.T) { 44 | t.Log(aa.GetUnfinishOrders(goex.BTC_USDT)) 45 | } 46 | -------------------------------------------------------------------------------- /acx/Acx.go: -------------------------------------------------------------------------------- 1 | package acx 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/wudian/GoEx" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | const ( 11 | EXCHANGE_NAME = "acx.io" 12 | 13 | API_BASE_URL = "https://acx.io/" 14 | API_V1 = API_BASE_URL + "/api/v2/" 15 | 16 | TICKER_URI = "/tickers/%s.json" 17 | //DEPTH_URI = "depth.php?c=%s&mk_type=%s" 18 | //ACCOUNT_URI = "getMyBalance.php" 19 | //TRADE_URI = "trades.php?c=%s&mk_type=%s" 20 | //CANCEL_URI = "cancelOrder.php" 21 | //ORDERS_INFO = "getMyTradeList.php" 22 | //UNFINISHED_ORDERS_INFO = "getOrderList.php" 23 | 24 | ) 25 | 26 | type Acx struct { 27 | accessKey, 28 | secretKey string 29 | httpClient *http.Client 30 | } 31 | 32 | func New(client *http.Client, api_key, secret_key string) *Acx { 33 | return &Acx{api_key, secret_key, client} 34 | } 35 | 36 | func (acx *Acx) GetExchangeName() string { 37 | return EXCHANGE_NAME 38 | } 39 | 40 | func (acx *Acx) GetTicker(currency CurrencyPair) (*Ticker, error) { 41 | tickerUri := API_V1 + fmt.Sprintf(TICKER_URI, strings.ToLower(currency.ToSymbol(""))) 42 | bodyDataMap, err := HttpGet(acx.httpClient, tickerUri) 43 | 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | //log.Println("acx uri:", tickerUri) 49 | //log.Println("acx bodyDataMap:", currency, bodyDataMap) 50 | 51 | tickerMap := bodyDataMap["ticker"].(map[string]interface{}) 52 | var ticker Ticker 53 | 54 | ticker.Date = uint64(bodyDataMap["at"].(float64)) 55 | ticker.Last = ToFloat64(tickerMap["last"]) 56 | ticker.Buy = ToFloat64(tickerMap["buy"]) 57 | ticker.Sell = ToFloat64(tickerMap["sell"]) 58 | ticker.Low = ToFloat64(tickerMap["low"]) 59 | ticker.High = ToFloat64(tickerMap["high"]) 60 | ticker.Vol = ToFloat64(tickerMap["vol"]) 61 | return &ticker, nil 62 | } 63 | func (acx *Acx) GetTickers(currency CurrencyPair) (*Ticker, error) { 64 | return acx.GetTicker(currency) 65 | } 66 | func (acx *Acx) GetTickerInBuf(currency CurrencyPair) (*Ticker, error) { 67 | return acx.GetTicker(currency) 68 | } 69 | 70 | func (acx *Acx) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 71 | panic("not implements") 72 | } 73 | 74 | func (acx *Acx) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 75 | panic("not implements") 76 | } 77 | 78 | func (acx *Acx) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 79 | panic("not implements") 80 | } 81 | 82 | func (acx *Acx) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 83 | panic("not implements") 84 | } 85 | 86 | func (acx *Acx) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 87 | panic("not implements") 88 | } 89 | 90 | func (acx *Acx) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 91 | panic("not implements") 92 | } 93 | func (acx *Acx) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 94 | panic("not implements") 95 | } 96 | 97 | func (acx *Acx) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 98 | panic("not implements") 99 | } 100 | 101 | func (acx *Acx) GetAccount() (*Account, error) { 102 | panic("not implements") 103 | } 104 | 105 | func (acx *Acx) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 106 | return nil, nil 107 | } 108 | 109 | func (acx *Acx) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 110 | panic("not implements") 111 | } 112 | 113 | //非个人,整个交易所的交易记录 114 | func (acx *Acx) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 115 | panic("not implements") 116 | } 117 | -------------------------------------------------------------------------------- /acx/Acx_test.go: -------------------------------------------------------------------------------- 1 | package acx 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var acx = New(http.DefaultClient, "", "") 10 | 11 | func TestAcx_GetTicker(t *testing.T) { 12 | AUD := goex.NewCurrency("AUD", "") 13 | BTC_AUD := goex.NewCurrencyPair(goex.BTC, AUD) 14 | ticker, err := acx.GetTicker(BTC_AUD) 15 | t.Log("err=>", err) 16 | t.Log("ticker=>", ticker) 17 | } 18 | -------------------------------------------------------------------------------- /aex/Aex.go: -------------------------------------------------------------------------------- 1 | package aex 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | . "github.com/wudian/GoEx" 7 | //"log" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | //因服务器有防CC攻击策略,每60秒内调用次数不可超过120次,超过部分将被防火墙拦截。 13 | const ( 14 | EXCHANGE_NAME = "aex.com" 15 | 16 | API_BASE_URL = "https://api.aex.com/" 17 | API_V1 = API_BASE_URL 18 | 19 | TICKER_URI = "ticker.php?c=%s&mk_type=%s" 20 | DEPTH_URI = "depth.php?c=%s&mk_type=%s" 21 | ACCOUNT_URI = "getMyBalance.php" 22 | TRADE_URI = "trades.php?c=%s&mk_type=%s" 23 | CANCEL_URI = "cancelOrder.php" 24 | ORDERS_INFO = "getMyTradeList.php" 25 | UNFINISHED_ORDERS_INFO = "getOrderList.php" 26 | ) 27 | 28 | type Aex struct { 29 | accessKey, 30 | secretKey, 31 | accountId string 32 | httpClient *http.Client 33 | } 34 | 35 | func New(client *http.Client, accessKey, secretKey, accountId string) *Aex { 36 | return &Aex{accessKey, secretKey, accountId, client} 37 | } 38 | 39 | func (aex *Aex) GetExchangeName() string { 40 | return EXCHANGE_NAME 41 | } 42 | 43 | func (aex *Aex) GetTicker(currency CurrencyPair) (*Ticker, error) { 44 | cur := currency.CurrencyA.String() 45 | money := currency.CurrencyB.String() 46 | if cur == "UNKNOWN" { 47 | //log.Println("Unsupport The CurrencyPair") 48 | return nil, errors.New("Unsupport The CurrencyPair") 49 | } 50 | tickerUri := API_V1 + fmt.Sprintf(TICKER_URI, cur, money) 51 | timestamp := time.Now().Unix() 52 | 53 | bodyDataMap, err := HttpGet(aex.httpClient, tickerUri) 54 | 55 | if err != nil { 56 | //log.Println(err) 57 | return nil, err 58 | } 59 | // log.Println("Aex bodyDataMap:", bodyDataMap) 60 | var tickerMap map[string]interface{} 61 | var ticker Ticker 62 | 63 | switch bodyDataMap["ticker"].(type) { 64 | case map[string]interface{}: 65 | tickerMap = bodyDataMap["ticker"].(map[string]interface{}) 66 | default: 67 | return nil, errors.New(fmt.Sprintf("Type Convert Error ? \n %s", bodyDataMap)) 68 | } 69 | 70 | ticker.Date = uint64(timestamp) 71 | ticker.Last = tickerMap["last"].(float64) 72 | ticker.Buy = tickerMap["buy"].(float64) 73 | ticker.Sell = tickerMap["sell"].(float64) 74 | ticker.Low = tickerMap["low"].(float64) 75 | ticker.High = tickerMap["high"].(float64) 76 | ticker.Vol = tickerMap["vol"].(float64) 77 | //log.Println("Aex", currency, "ticker:", ticker) 78 | 79 | return &ticker, nil 80 | } 81 | 82 | func (aex *Aex) GetTickers(currency CurrencyPair) (*Ticker, error) { 83 | return aex.GetTicker(currency) 84 | } 85 | 86 | func (aex *Aex) GetTickerInBuf(currency CurrencyPair) (*Ticker, error) { 87 | return aex.GetTicker(currency) 88 | } 89 | 90 | func (aex *Aex) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 91 | panic("not implement") 92 | } 93 | 94 | func (aex *Aex) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 95 | panic("not implements") 96 | } 97 | 98 | func (aex *Aex) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 99 | panic("not implements") 100 | } 101 | 102 | func (aex *Aex) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 103 | panic("not implements") 104 | } 105 | 106 | func (aex *Aex) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 107 | panic("not implements") 108 | } 109 | 110 | func (aex *Aex) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 111 | panic("not implements") 112 | } 113 | 114 | func (aex *Aex) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 115 | panic("not implements") 116 | } 117 | func (aex *Aex) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 118 | panic("not implements") 119 | } 120 | 121 | func (aex *Aex) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 122 | panic("not implements") 123 | } 124 | 125 | func (aex *Aex) GetAccount() (*Account, error) { 126 | panic("not implements") 127 | } 128 | 129 | func (aex *Aex) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 130 | panic("not implements") 131 | } 132 | 133 | //非个人,整个交易所的交易记录 134 | func (aex *Aex) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 135 | panic("not implements") 136 | } 137 | -------------------------------------------------------------------------------- /aex/Aex_test.go: -------------------------------------------------------------------------------- 1 | package aex 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var acx = New(http.DefaultClient, "", "", "") 10 | 11 | func TestAex_GetTicker(t *testing.T) { 12 | ticker, err := acx.GetTicker(goex.ETH_BTC) 13 | t.Log("err=>", err) 14 | t.Log("ticker=>", ticker) 15 | } 16 | -------------------------------------------------------------------------------- /allcoin/allcoin_test.go: -------------------------------------------------------------------------------- 1 | package allcoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var ac = New(http.DefaultClient, "", "") 10 | 11 | func TestAllcoin_GetAccount(t *testing.T) { 12 | return 13 | t.Log(ac.GetAccount()) 14 | } 15 | func TestAllcoin_GetUnfinishOrders(t *testing.T) { 16 | return 17 | t.Log(ac.GetUnfinishOrders(goex.ETH_BTC)) 18 | } 19 | func TestAllcoin_GetTicker(t *testing.T) { 20 | return 21 | t.Log(ac.GetTicker(goex.ETH_BTC)) 22 | } 23 | 24 | func TestAllcoin_GetDepth(t *testing.T) { 25 | return 26 | dep, _ := ac.GetDepth(1, goex.ETH_BTC) 27 | t.Log(dep) 28 | } 29 | 30 | func TestAllcoin_LimitBuy(t *testing.T) { 31 | t.Log(ac.LimitBuy("1", "0.07", goex.ETH_BTC)) 32 | } 33 | -------------------------------------------------------------------------------- /banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudian/GoEx/ca421ea49f0649ad6713e5e6c8897a5e61888e18/banner.jpg -------------------------------------------------------------------------------- /bigone/Bigone_test.go: -------------------------------------------------------------------------------- 1 | package bigone 2 | 3 | import ( 4 | . "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | bo = New(http.DefaultClient, "", "") 11 | ) 12 | 13 | func TestBigone_GetTicker(t *testing.T) { 14 | return 15 | t.Log(bo.GetTicker(ETH_BTC)) 16 | } 17 | func TestBigone_GetDepth(t *testing.T) { 18 | return 19 | t.Log(bo.GetDepth(1, ETH_BTC)) 20 | } 21 | func TestBigone_GetAccount(t *testing.T) { 22 | return 23 | t.Log(bo.GetAccount()) 24 | } 25 | func TestBigone_GetUnfinishOrders(t *testing.T) { 26 | return 27 | BIG_BTC := NewCurrencyPair2("BIG_BTC") 28 | t.Log(bo.GetUnfinishOrders(BIG_BTC)) 29 | } 30 | func TestBigone_GetOrderHistorys(t *testing.T) { 31 | return 32 | TCT_BTC := NewCurrencyPair2("TCT_BTC") 33 | t.Log(bo.GetOrderHistorys(TCT_BTC, 1, 1)) 34 | } 35 | func TestBigone_LimitSell(t *testing.T) { 36 | return 37 | TCT_BTC := NewCurrencyPair2("TCT_BTC") 38 | t.Log(bo.LimitSell("322", "1", TCT_BTC)) 39 | } 40 | func TestBigone_CancelOrder(t *testing.T) { 41 | return 42 | t.Log(bo.CancelOrder("9f352ec4-3502-4dea-bdd4-0860d22f80e3", EOS_BTC)) 43 | } 44 | func TestBigone_GetOneOrder(t *testing.T) { 45 | return 46 | TCT_BTC := NewCurrencyPair2("TCT_BTC") 47 | 48 | t.Log(bo.GetOneOrder("ccfe4661-82b5-4dfa-bc55-1f4ec61b1611", TCT_BTC)) 49 | } 50 | -------------------------------------------------------------------------------- /binance/Binance_test.go: -------------------------------------------------------------------------------- 1 | package binance 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | var ba = New(http.DefaultClient, "zRuNMVWQrFRswwaccIlZ9liNrMlJboEKnopZMfyjaEvAvHdbI8F8tg4wH7ruRpEv", "vTSIXM9xNyOMDMgvrML4GPGKfFpnY45f89xUoBayw2xC8b15NND1HSp8e9mXX5py") 11 | 12 | func TestBinance_GetTicker(t *testing.T) { 13 | //return 14 | ticker, _ := ba.GetTicker(goex.LTC_BTC) 15 | t.Log(ticker) 16 | } 17 | func TestBinance_LimitSell(t *testing.T) { 18 | return 19 | order, err := ba.LimitSell("1", "1", goex.LTC_BTC) 20 | t.Log(order, err) 21 | } 22 | 23 | func TestBinance_GetDepth(t *testing.T) { 24 | return 25 | dep, err := ba.GetDepth(5, goex.ETH_BTC) 26 | t.Log(err) 27 | if err == nil { 28 | t.Log(dep.AskList) 29 | t.Log(dep.BidList) 30 | } 31 | } 32 | 33 | func TestBinance_GetAccount(t *testing.T) { 34 | return 35 | account, err := ba.GetAccount() 36 | t.Log(account, err) 37 | } 38 | 39 | func TestBinance_GetUnfinishOrders(t *testing.T) { 40 | return 41 | orders, err := ba.GetUnfinishOrders(goex.ETH_BTC) 42 | t.Log(orders, err) 43 | } 44 | func TestBinance_GetKlineRecords(t *testing.T) { 45 | 46 | t.Log(ba.GetKlineRecords(goex.ETH_BTC, goex.KLINE_PERIOD_1MIN, 100, int(time.Now().Add(-2*time.Hour).UnixNano()))) 47 | } 48 | -------------------------------------------------------------------------------- /bitfinex/BitfinexLending.go: -------------------------------------------------------------------------------- 1 | package bitfinex 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | . "github.com/wudian/GoEx" 8 | "io/ioutil" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | type LendBookItem struct { 14 | Rate float64 `json:",string"` 15 | Amount float64 `json:",string"` 16 | Period int `json:"period"` 17 | Timestamp string `json:"timestamp"` 18 | Frr string `json:"frr"` 19 | } 20 | 21 | type LendBook struct { 22 | Bids []LendBookItem `json:"bids"` 23 | Asks []LendBookItem `json:"asks"` 24 | } 25 | 26 | type LendOrder struct { 27 | Id int `json:"id"` 28 | Currency string `json:"currency"` 29 | Rate float64 `json:"rate,string"` 30 | Period int `json:"period"` 31 | Direction string `json:"direction"` 32 | IsLive bool `json:"is_live"` 33 | IsCancelled bool `json:"is_cancelled"` 34 | Amount float64 `json:"amount,string"` 35 | ExecutedAmount float64 `json:"executed_amount,string"` 36 | RemainingAmount float64 `json:"remaining_amount,string"` 37 | OriginalAmount float64 `json:"original_amount,string"` 38 | Timestamp string `json:"timestamp"` 39 | } 40 | 41 | type LendTicker struct { 42 | Ticker 43 | Coin Currency 44 | DailyChangePerc float64 45 | } 46 | 47 | func (bfx *Bitfinex) GetLendTickers() ([]LendTicker, error) { 48 | 49 | resp, err := bfx.httpClient.Get("https://api.bitfinex.com/v2/tickers?symbols=ALL") 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | body, _ := ioutil.ReadAll(resp.Body) 55 | 56 | var ret []interface{} 57 | json.Unmarshal(body, &ret) 58 | 59 | var tickers []LendTicker 60 | 61 | for _, v := range ret { 62 | vv := v.([]interface{}) 63 | symbol := vv[0].(string) 64 | if strings.HasPrefix(symbol, "f") { 65 | tickers = append(tickers, LendTicker{ 66 | Ticker: Ticker{ 67 | Last: ToFloat64(vv[10]) * 100, 68 | Vol: ToFloat64(vv[11])}, 69 | DailyChangePerc: ToFloat64(vv[9]) * 100, 70 | Coin: NewCurrency(symbol[1:], "")}) 71 | } 72 | } 73 | 74 | return tickers, nil 75 | } 76 | 77 | func (bfx *Bitfinex) GetDepositWalletBalance() (*Account, error) { 78 | wallets, err := bfx.GetWalletBalances() 79 | if err != nil { 80 | return nil, err 81 | } 82 | return wallets["deposit"], nil 83 | } 84 | 85 | func (bfx *Bitfinex) GetLendBook(currency Currency) (error, *LendBook) { 86 | path := fmt.Sprintf("/lendbook/%s", currency.Symbol) 87 | resp, err := bfx.httpClient.Get(BASE_URL + path) 88 | if err != nil { 89 | return err, nil 90 | } 91 | 92 | body, _ := ioutil.ReadAll(resp.Body) 93 | 94 | if resp.StatusCode != 200 { 95 | return errors.New(fmt.Sprintf("HttpCode: %d , errmsg: %s", resp.StatusCode, string(body))), nil 96 | } 97 | //println(string(body)) 98 | var lendBook LendBook 99 | err = json.Unmarshal(body, &lendBook) 100 | if err != nil { 101 | return err, nil 102 | } 103 | 104 | return nil, &lendBook 105 | } 106 | 107 | func (bfx *Bitfinex) Transfer(amount float64, currency Currency, fromWallet, toWallet string) error { 108 | path := "transfer" 109 | params := map[string]interface{}{ 110 | "amount": strconv.FormatFloat(amount, 'f', -1, 32), 111 | "currency": strings.ToUpper(currency.Symbol), 112 | "walletfrom": fromWallet, 113 | "walletto": toWallet, 114 | } 115 | 116 | var resp []map[string]interface{} 117 | 118 | err := bfx.doAuthenticatedRequest("POST", path, params, &resp) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | if "success" == resp[0]["status"] { 124 | return nil 125 | } 126 | 127 | return errors.New(resp[0]["message"].(string)) 128 | } 129 | 130 | func (bfx *Bitfinex) newOffer(currency Currency, amount, rate string, period int, direction string) (error, *LendOrder) { 131 | path := "offer/new" 132 | params := map[string]interface{}{ 133 | "amount": amount, 134 | "currency": currency.Symbol, 135 | "rate": rate, 136 | "period": period, 137 | "direction": direction, 138 | } 139 | 140 | var lendOrder LendOrder 141 | err := bfx.doAuthenticatedRequest("POST", path, params, &lendOrder) 142 | if err != nil { 143 | return err, nil 144 | } 145 | 146 | return nil, &lendOrder 147 | } 148 | 149 | func (bfx *Bitfinex) NewLendOrder(currency Currency, amount, rate string, period int) (error, *LendOrder) { 150 | return bfx.newOffer(currency, amount, rate, period, "lend") 151 | } 152 | 153 | func (bfx *Bitfinex) NewLoanOrder(currency Currency, amount, rate string, period int) (error, *LendOrder) { 154 | return bfx.newOffer(currency, amount, rate, period, "loan") 155 | } 156 | 157 | func (bfx *Bitfinex) CancelLendOrder(id int) (error, *LendOrder) { 158 | println("id=", id) 159 | path := "offer/cancel" 160 | var lendOrder LendOrder 161 | err := bfx.doAuthenticatedRequest("POST", path, map[string]interface{}{"offer_id": id}, &lendOrder) 162 | if err != nil { 163 | return err, nil 164 | } 165 | return nil, &lendOrder 166 | } 167 | 168 | func (bfx *Bitfinex) GetLendOrderStatus(id int) (error, *LendOrder) { 169 | path := "offer/status" 170 | var lendOrder LendOrder 171 | err := bfx.doAuthenticatedRequest("POST", path, map[string]interface{}{"offer_id": id}, &lendOrder) 172 | if err != nil { 173 | return err, nil 174 | } 175 | return nil, &lendOrder 176 | } 177 | 178 | func (bfx *Bitfinex) ActiveLendOrders() (error, []LendOrder) { 179 | var lendOrders []LendOrder 180 | err := bfx.doAuthenticatedRequest("POST", "offers", map[string]interface{}{}, &lendOrders) 181 | if err != nil { 182 | return err, nil 183 | } 184 | return nil, lendOrders 185 | } 186 | 187 | func (bfx *Bitfinex) OffersHistory(limit int) (error, []LendOrder) { 188 | var offerOrders []LendOrder 189 | err := bfx.doAuthenticatedRequest("POST", "offers/hist", map[string]interface{}{"limit": limit}, &offerOrders) 190 | if err != nil { 191 | return err, nil 192 | } 193 | return nil, offerOrders 194 | } 195 | 196 | func (bfx *Bitfinex) ActiveCredits() (error, []LendOrder) { 197 | var offerOrders []LendOrder 198 | err := bfx.doAuthenticatedRequest("POST", "credits", map[string]interface{}{}, &offerOrders) 199 | if err != nil { 200 | return err, nil 201 | } 202 | return nil, offerOrders 203 | } 204 | 205 | type TradeFunding struct { 206 | Rate string `json:"rate"` 207 | Period string `json:"period"` 208 | Amount string `json:"amount"` 209 | Timestamp string `json:"timestamp"` 210 | Type string `json:"type"` 211 | Tid int64 `json:"tid"` 212 | OfferId int64 `json:"offer_id"` 213 | } 214 | 215 | func (bfx *Bitfinex) MytradesFunding(currency Currency, limit int) (error, []TradeFunding) { 216 | var trades []TradeFunding 217 | err := bfx.doAuthenticatedRequest("POST", "mytrades_funding", map[string]interface{}{"limit_trades": limit, "symbol": currency.Symbol}, &trades) 218 | if err != nil { 219 | return err, nil 220 | } 221 | return nil, trades 222 | } 223 | -------------------------------------------------------------------------------- /bitfinex/BitfinexMarginTrading.go: -------------------------------------------------------------------------------- 1 | package bitfinex 2 | 3 | import . "github.com/wudian/GoEx" 4 | 5 | type MarginLimits struct { 6 | Pair string `json:"on_pair"` 7 | InitialMargin float64 `json:"initial_margin,string"` 8 | MarginRequirement float64 `json:"margin_requirement,string"` 9 | TradableBalance float64 `json:"tradable_balance,string"` 10 | } 11 | 12 | type MarginInfo struct { 13 | MarginBalance float64 `json:"margin_balance,string"` 14 | TradableBalance float64 `json:"tradable_balance,string"` 15 | UnrealizedPl float64 `json:"unrealized_pl,string"` 16 | UnrealizedSwap float64 `json:"unrealized_swap,string"` 17 | NetValue float64 `json:"net_value,string"` 18 | RequiredMargin float64 `json:"required_margin,string"` 19 | Leverage float64 `json:"leverage,string"` 20 | MarginRequirement float64 `json:"margin_requirement,string"` 21 | MarginLimits []MarginLimits `json:"margin_limits"` 22 | } 23 | 24 | func (bfx *Bitfinex) GetMarginTradingWalletBalance() (*Account, error) { 25 | balancemap, err := bfx.GetWalletBalances() 26 | if err != nil { 27 | return nil, err 28 | } 29 | return balancemap["trading"], nil 30 | } 31 | 32 | func (bfx *Bitfinex) MarginLimitBuy(amount, price string, currencyPair CurrencyPair) (*Order, error) { 33 | return bfx.placeOrder("limit", "buy", amount, price, currencyPair) 34 | } 35 | 36 | func (bfx *Bitfinex) MarginLimitSell(amount, price string, currencyPair CurrencyPair) (*Order, error) { 37 | return bfx.placeOrder("limit", "sell", amount, price, currencyPair) 38 | } 39 | 40 | func (bfx *Bitfinex) MarginMarketBuy(amount, price string, currencyPair CurrencyPair) (*Order, error) { 41 | return bfx.placeOrder("Market", "buy", amount, price, currencyPair) 42 | } 43 | 44 | func (bfx *Bitfinex) MarginMarketSell(amount, price string, currencyPair CurrencyPair) (*Order, error) { 45 | return bfx.placeOrder("Market", "sell", amount, price, currencyPair) 46 | } 47 | 48 | func (bfx *Bitfinex) GetMarginInfos() ([]MarginInfo, error) { 49 | var marginInfo []MarginInfo 50 | err := bfx.doAuthenticatedRequest("POST", "margin_infos", map[string]interface{}{}, &marginInfo) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return marginInfo, nil 55 | } 56 | -------------------------------------------------------------------------------- /bitfinex/bitfinex_test.go: -------------------------------------------------------------------------------- 1 | package bitfinex 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var bfx = New(http.DefaultClient, "", "") 10 | 11 | func TestBitfinex_GetTicker(t *testing.T) { 12 | ticker, _ := bfx.GetTicker(goex.ETH_BTC) 13 | t.Log(ticker) 14 | } 15 | 16 | func TestBitfinex_GetDepth(t *testing.T) { 17 | dep, _ := bfx.GetDepth(2, goex.ETH_BTC) 18 | t.Log(dep.AskList) 19 | t.Log(dep.BidList) 20 | } 21 | -------------------------------------------------------------------------------- /bithumb/bithumb_test.go: -------------------------------------------------------------------------------- 1 | package bithumb 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var bh = New(http.DefaultClient, "", "") 10 | 11 | func TestBithumb_GetTicker(t *testing.T) { 12 | ticker, err := bh.GetTicker(goex.BTC_USDT) 13 | t.Log("err=>", err) 14 | t.Log("ticker=>", ticker) 15 | } 16 | 17 | func TestBithumb_GetDepth(t *testing.T) { 18 | return 19 | dep, err := bh.GetDepth(1, goex.BTC_KRW) 20 | t.Log("err=>", err) 21 | t.Log("asks=>", dep.AskList) 22 | t.Log("bids=>", dep.BidList) 23 | } 24 | -------------------------------------------------------------------------------- /bitmex/bitmex.go: -------------------------------------------------------------------------------- 1 | package bitmex 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/wudian/GoEx" 6 | "net/http" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | var ( 12 | base_url = "https://www.bitmex.com/api/v1/" 13 | ) 14 | 15 | //bitmex register link https://www.bitmex.com/register/0fcQP7 16 | 17 | type Bitmex struct { 18 | httpClient *http.Client 19 | accessKey, 20 | secretKey string 21 | } 22 | 23 | func New(client *http.Client, accesskey, secretkey string) *Bitmex { 24 | return &Bitmex{client, accesskey, secretkey} 25 | } 26 | 27 | func (Bitmex *Bitmex) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 28 | panic("not implements") 29 | } 30 | 31 | func (Bitmex *Bitmex) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 32 | panic("not implements") 33 | } 34 | 35 | func (Bitmex *Bitmex) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 36 | panic("not implements") 37 | } 38 | 39 | func (Bitmex *Bitmex) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 40 | panic("not implements") 41 | } 42 | 43 | func (Bitmex *Bitmex) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 44 | panic("not implements") 45 | } 46 | 47 | func (Bitmex *Bitmex) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 48 | panic("not implements") 49 | } 50 | func (Bitmex *Bitmex) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 51 | panic("not implements") 52 | } 53 | 54 | func (Bitmex *Bitmex) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 55 | panic("not implements") 56 | } 57 | 58 | func (Bitmex *Bitmex) GetAccount() (*Account, error) { 59 | panic("not implements") 60 | } 61 | 62 | func (Bitmex *Bitmex) GetTicker(currency CurrencyPair) (*Ticker, error) { 63 | panic("not implements") 64 | } 65 | 66 | func (Bitmex *Bitmex) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 67 | uri := fmt.Sprintf("orderBook/L2?symbol=%s&depth=%d", Bitmex.pairToSymbol(currency), size) 68 | resp, err := HttpGet3(Bitmex.httpClient, base_url+uri, nil) 69 | if err != nil { 70 | return nil, HTTP_ERR_CODE.OriginErr(err.Error()) 71 | } 72 | 73 | //log.Println(resp) 74 | 75 | dep := new(Depth) 76 | dep.UTime = time.Now() 77 | dep.Pair = currency 78 | 79 | for _, r := range resp { 80 | rr := r.(map[string]interface{}) 81 | switch strings.ToLower(rr["side"].(string)) { 82 | case "sell": 83 | dep.AskList = append(dep.AskList, DepthRecord{Price: ToFloat64(rr["price"]), Amount: ToFloat64(rr["size"])}) 84 | case "buy": 85 | dep.BidList = append(dep.BidList, DepthRecord{Price: ToFloat64(rr["price"]), Amount: ToFloat64(rr["size"])}) 86 | } 87 | } 88 | 89 | return dep, nil 90 | } 91 | 92 | func (Bitmex *Bitmex) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 93 | panic("not implements") 94 | } 95 | 96 | //非个人,整个交易所的交易记录 97 | func (Bitmex *Bitmex) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 98 | panic("not implements") 99 | } 100 | 101 | func (Bitmex *Bitmex) GetExchangeName() string { 102 | return BITMEX 103 | } 104 | 105 | func (mex *Bitmex) pairToSymbol(pair CurrencyPair) string { 106 | if pair.CurrencyA.Symbol == BTC.Symbol { 107 | return NewCurrencyPair(XBT, USD).ToSymbol("") 108 | } 109 | return pair.AdaptUsdtToUsd().ToSymbol("") 110 | } 111 | -------------------------------------------------------------------------------- /bitmex/bitmex_test.go: -------------------------------------------------------------------------------- 1 | package bitmex 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net/http" 7 | "testing" 8 | "net/url" 9 | "net" 10 | "time" 11 | ) 12 | 13 | var httpProxyClient = &http.Client{ 14 | Transport: &http.Transport{ 15 | Proxy: func(req *http.Request) (*url.URL, error) { 16 | return &url.URL{ 17 | Scheme: "socks5", 18 | Host: "127.0.0.1:55307"}, nil 19 | }, 20 | Dial: (&net.Dialer{ 21 | Timeout: 10 * time.Second, 22 | }).Dial, 23 | }, 24 | Timeout: 10 * time.Second, 25 | } 26 | var mex = New(httpProxyClient, "", "") 27 | 28 | func TestBitmex_GetTicker(t *testing.T) { 29 | //return 30 | ticker, err := mex.GetTicker(goex.BTC_USDT) 31 | assert.Nil(t, err) 32 | t.Log(ticker) 33 | } 34 | 35 | func TestBitmex_GetDepth(t *testing.T) { 36 | return 37 | dep, err := mex.GetDepth(2, goex.NewCurrencyPair(goex.XBT, goex.USD)) 38 | assert.Nil(t, err) 39 | t.Log(dep) 40 | } 41 | -------------------------------------------------------------------------------- /bitstamp/Bitstamp_test.go: -------------------------------------------------------------------------------- 1 | package bitstamp 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "log" 7 | "net/http" 8 | "testing" 9 | ) 10 | 11 | var client = http.Client{ 12 | CheckRedirect: func(req *http.Request, via []*http.Request) error { 13 | log.Println("======") 14 | return nil 15 | }, 16 | } 17 | var btmp = NewBitstamp(&client, "", "", "") 18 | 19 | func TestBitstamp_GetAccount(t *testing.T) { 20 | acc, err := btmp.GetAccount() 21 | assert.Nil(t, err) 22 | t.Log(acc) 23 | } 24 | 25 | func TestBitstamp_GetTicker(t *testing.T) { 26 | ticker, err := btmp.GetTicker(goex.BTC_USD) 27 | assert.Nil(t, err) 28 | t.Log(ticker) 29 | } 30 | 31 | func TestBitstamp_GetDepth(t *testing.T) { 32 | dep, err := btmp.GetDepth(5, goex.BTC_USD) 33 | assert.Nil(t, err) 34 | t.Log(dep.BidList) 35 | t.Log(dep.AskList) 36 | } 37 | 38 | func TestBitstamp_LimitBuy(t *testing.T) { 39 | ord, err := btmp.LimitBuy("55", "0.12", goex.XRP_USD) 40 | assert.Nil(t, err) 41 | t.Log(ord) 42 | } 43 | 44 | func TestBitstamp_LimitSell(t *testing.T) { 45 | ord, err := btmp.LimitSell("40", "0.22", goex.XRP_USD) 46 | assert.Nil(t, err) 47 | t.Log(ord) 48 | } 49 | 50 | 51 | func TestBitstamp_MarketBuy(t *testing.T) { 52 | ord, err := btmp.MarketBuy("1", goex.XRP_USD) 53 | assert.Nil(t, err) 54 | t.Log(ord) 55 | } 56 | 57 | 58 | func TestBitstamp_MarketSell(t *testing.T) { 59 | ord, err := btmp.MarketSell("2", goex.XRP_USD) 60 | assert.Nil(t, err) 61 | t.Log(ord) 62 | } 63 | 64 | func TestBitstamp_CancelOrder(t *testing.T) { 65 | r, err := btmp.CancelOrder("311242779", goex.XRP_USD) 66 | assert.Nil(t, err) 67 | t.Log(r) 68 | } 69 | 70 | func TestBitstamp_GetUnfinishOrders(t *testing.T) { 71 | ords, err := btmp.GetUnfinishOrders(goex.XRP_USD) 72 | assert.Nil(t, err) 73 | t.Log(ords) 74 | } 75 | 76 | func TestBitstamp_GetOneOrder(t *testing.T) { 77 | ord, err := btmp.GetOneOrder("311752078", goex.XRP_USD) 78 | assert.Nil(t, err) 79 | t.Log(ord) 80 | } 81 | -------------------------------------------------------------------------------- /bittrex/bittrex.go: -------------------------------------------------------------------------------- 1 | package bittrex 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/wudian/GoEx" 6 | "net/http" 7 | "sort" 8 | "errors" 9 | ) 10 | 11 | type Bittrex struct { 12 | client *http.Client 13 | baseUrl, 14 | accesskey, 15 | secretkey string 16 | } 17 | 18 | func New(client *http.Client, accesskey, secretkey string) *Bittrex { 19 | return &Bittrex{client: client, accesskey: accesskey, secretkey: secretkey, baseUrl: "https://bittrex.com/api/v1.1"} 20 | } 21 | 22 | func (bx *Bittrex) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 23 | panic("not implement") 24 | } 25 | func (bx *Bittrex) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 26 | panic("not implement") 27 | } 28 | func (bx *Bittrex) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 29 | panic("not implement") 30 | } 31 | func (bx *Bittrex) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 32 | panic("not implement") 33 | } 34 | func (bx *Bittrex) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 35 | panic("not implement") 36 | } 37 | func (bx *Bittrex) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 38 | panic("not implement") 39 | } 40 | func (bx *Bittrex) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 41 | panic("not implement") 42 | } 43 | func (bx *Bittrex) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 44 | panic("not implement") 45 | } 46 | func (bx *Bittrex) GetAccount() (*Account, error) { 47 | panic("not implement") 48 | } 49 | 50 | func (bx *Bittrex) GetTicker(currency CurrencyPair) (*Ticker, error) { 51 | resp, err := HttpGet(bx.client, fmt.Sprintf("%s/public/getmarketsummary?market=%s", bx.baseUrl, currency.ToSymbol2("-"))) 52 | if err != nil { 53 | errCode := HTTP_ERR_CODE 54 | errCode.OriginErrMsg = err.Error() 55 | return nil, errCode 56 | } 57 | 58 | result, _ := resp["result"].([]interface{}) 59 | if len(result) <= 0 { 60 | return nil, API_ERR 61 | } 62 | 63 | tickermap := result[0].(map[string]interface{}) 64 | 65 | return &Ticker{ 66 | Last: ToFloat64(tickermap["Last"]), 67 | Sell: ToFloat64(tickermap["Ask"]), 68 | Buy: ToFloat64(tickermap["Bid"]), 69 | Low: ToFloat64(tickermap["Low"]), 70 | High: ToFloat64(tickermap["High"]), 71 | Vol: ToFloat64(tickermap["Volume"]), 72 | }, nil 73 | } 74 | 75 | func (bx *Bittrex) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 76 | 77 | resp, err := HttpGet(bx.client, fmt.Sprintf("%s/public/getorderbook?market=%s&type=both", bx.baseUrl, currency.ToSymbol2("-"))) 78 | if err != nil { 79 | errCode := HTTP_ERR_CODE 80 | errCode.OriginErrMsg = err.Error() 81 | return nil, errCode 82 | } 83 | 84 | result, err2 := resp["result"].(map[string]interface{}) 85 | if err2 != true { 86 | return nil, errors.New(resp["message"].(string)) 87 | } 88 | bids, _ := result["buy"].([]interface{}) 89 | asks, _ := result["sell"].([]interface{}) 90 | 91 | dep := new(Depth) 92 | 93 | for _, v := range bids { 94 | r := v.(map[string]interface{}) 95 | dep.BidList = append(dep.BidList, DepthRecord{ToFloat64(r["Rate"]), ToFloat64(r["Quantity"])}) 96 | } 97 | 98 | for _, v := range asks { 99 | r := v.(map[string]interface{}) 100 | dep.AskList = append(dep.AskList, DepthRecord{ToFloat64(r["Rate"]), ToFloat64(r["Quantity"])}) 101 | } 102 | 103 | sort.Sort(sort.Reverse(dep.AskList)) 104 | 105 | return dep, nil 106 | } 107 | 108 | func (bx *Bittrex) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 109 | panic("not implement") 110 | } 111 | 112 | //非个人,整个交易所的交易记录 113 | func (bx *Bittrex) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 114 | panic("not implement") 115 | } 116 | 117 | func (bx *Bittrex) GetExchangeName() string { 118 | return BITTREX 119 | } 120 | -------------------------------------------------------------------------------- /bittrex/bittrex_test.go: -------------------------------------------------------------------------------- 1 | package bittrex 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var b = New(http.DefaultClient, "", "") 10 | 11 | func TestBittrex_GetTicker(t *testing.T) { 12 | ticker, err := b.GetTicker(goex.BTC_USDT) 13 | t.Log("err=>", err) 14 | t.Log("ticker=>", ticker) 15 | } 16 | 17 | func TestBittrex_GetDepth(t *testing.T) { 18 | dep, err := b.GetDepth(1, goex.BTC_USDT) 19 | t.Log("err=>", err) 20 | t.Log("ask=>", dep.AskList) 21 | t.Log("bid=>", dep.BidList) 22 | } 23 | -------------------------------------------------------------------------------- /btcbox/btcbox.go: -------------------------------------------------------------------------------- 1 | package btcbox 2 | 3 | import ( 4 | . "github.com/wudian/GoEx" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | baseurl = "https://www.btcbox.co.jp/api/v1/" 11 | ) 12 | 13 | type BtcBox struct { 14 | client *http.Client 15 | accessKey, 16 | secretkey string 17 | } 18 | 19 | func New(client *http.Client, apikey, secretkey string) *BtcBox { 20 | return &BtcBox{client: client, accessKey: apikey, secretkey: secretkey} 21 | } 22 | 23 | func (btcbox *BtcBox) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 24 | panic("not implements") 25 | } 26 | 27 | func (btcbox *BtcBox) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 28 | panic("not implements") 29 | } 30 | 31 | func (btcbox *BtcBox) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 32 | panic("not implements") 33 | } 34 | 35 | func (btcbox *BtcBox) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 36 | panic("not implements") 37 | } 38 | 39 | func (btcbox *BtcBox) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 40 | panic("not implements") 41 | } 42 | 43 | func (btcbox *BtcBox) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 44 | panic("not implements") 45 | } 46 | func (btcbox *BtcBox) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 47 | panic("not implements") 48 | } 49 | 50 | func (btcbox *BtcBox) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 51 | panic("not implements") 52 | } 53 | 54 | func (btcbox *BtcBox) GetAccount() (*Account, error) { 55 | panic("not implements") 56 | } 57 | 58 | func (btcbox *BtcBox) GetTicker(currency CurrencyPair) (*Ticker, error) { 59 | respmap, err := HttpGet(btcbox.client, baseurl+"ticker?coin="+strings.ToLower(currency.CurrencyA.Symbol)) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | return &Ticker{ 65 | Low: ToFloat64(respmap["low"]), 66 | Buy: ToFloat64(respmap["buy"]), 67 | Sell: ToFloat64(respmap["sell"]), 68 | Last: ToFloat64(respmap["last"]), 69 | Vol: ToFloat64(respmap["vol"]), 70 | High: ToFloat64(respmap["high"])}, nil 71 | } 72 | 73 | func (btcbox *BtcBox) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 74 | respmap, err := HttpGet(btcbox.client, baseurl+"depth?coin="+strings.ToLower(currency.CurrencyA.Symbol)) 75 | if err != nil { 76 | return nil, err 77 | } 78 | //log.Println(respmap) 79 | dep := new(Depth) 80 | asksmap := respmap["asks"].([]interface{}) 81 | bidsmap := respmap["bids"].([]interface{}) 82 | 83 | var ( 84 | l = len(asksmap) 85 | i int = l - size 86 | c int = 0 87 | ) 88 | 89 | for ; i < l; i++ { 90 | ask := asksmap[i] 91 | var dr DepthRecord 92 | for i, vv := range ask.([]interface{}) { 93 | switch i { 94 | case 0: 95 | dr.Price = vv.(float64) 96 | case 1: 97 | dr.Amount = vv.(float64) 98 | } 99 | } 100 | dep.AskList = append(dep.AskList, dr) 101 | } 102 | 103 | c = 0 104 | for _, v := range bidsmap { 105 | var dr DepthRecord 106 | for i, vv := range v.([]interface{}) { 107 | switch i { 108 | case 0: 109 | dr.Price = vv.(float64) 110 | case 1: 111 | dr.Amount = vv.(float64) 112 | } 113 | } 114 | dep.BidList = append(dep.BidList, dr) 115 | c++ 116 | if c == size { 117 | break 118 | } 119 | } 120 | 121 | return dep, nil 122 | } 123 | 124 | func (btcbox *BtcBox) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 125 | panic("not implements") 126 | } 127 | 128 | //非个人,整个交易所的交易记录 129 | func (btcbox *BtcBox) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 130 | panic("not implements") 131 | } 132 | 133 | func (btcbox *BtcBox) GetExchangeName() string { 134 | return "btcbox.co.jp" 135 | } 136 | -------------------------------------------------------------------------------- /btcbox/btcbox_test.go: -------------------------------------------------------------------------------- 1 | package btcbox 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | var btcbox = New(http.DefaultClient, "", "") 11 | 12 | func TestBtcBox_GetTicker(t *testing.T) { 13 | ticker, err := btcbox.GetTicker(goex.BTC_JPY) 14 | assert.Nil(t, err) 15 | t.Log(ticker) 16 | } 17 | 18 | func TestBtcBox_GetDepth(t *testing.T) { 19 | dep, err := btcbox.GetDepth(5, goex.ETH_JPY) 20 | assert.Nil(t, err) 21 | t.Log(dep.BidList) 22 | } 23 | 24 | func TestBtcBox_CancelOrder(t *testing.T) { 25 | 26 | } 27 | -------------------------------------------------------------------------------- /btcc/BTCChina.go: -------------------------------------------------------------------------------- 1 | package btcc 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | . "github.com/wudian/GoEx" 9 | "net/http" 10 | "strconv" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | type BTCChina struct { 16 | httpClient *http.Client 17 | accessKey, 18 | secretKey string 19 | } 20 | 21 | type ReqBody struct { 22 | Id int `json:"id"` 23 | Params []interface{} `json:"params"` 24 | Method string `json:"method"` 25 | Sign string `json:"-"` 26 | Tonce string `json:"-"` 27 | } 28 | 29 | var ( 30 | _MARKET_API_URL = "https://data.btcchina.com/data" 31 | _TRADE_API_V1_URL = "https://api.btcchina.com/api_trade_v1.php" 32 | ) 33 | 34 | func NewBTCChina(client *http.Client, accessKey, secretKey string) *BTCChina { 35 | return &BTCChina{client, accessKey, secretKey} 36 | } 37 | 38 | func (btch *BTCChina) GetTicker(currency CurrencyPair) (*Ticker, error) { 39 | tickerResp, err := HttpGet(btch.httpClient, fmt.Sprintf("%s/ticker?market=%s", 40 | _MARKET_API_URL, strings.ToLower(currency.ToSymbol("")))) 41 | 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | //log.Println(tickerResp) 47 | 48 | tickermap, ok := tickerResp["ticker"].(map[string]interface{}) 49 | if !ok { 50 | return nil, errors.New("Get Ticker Error") 51 | } 52 | 53 | ticker := Ticker{} 54 | ticker.Buy = ToFloat64(tickermap["buy"]) 55 | ticker.Sell = ToFloat64(tickermap["sell"]) 56 | ticker.High = ToFloat64(tickermap["high"]) 57 | ticker.Low = ToFloat64(tickermap["low"]) 58 | ticker.Last = ToFloat64(tickermap["last"]) 59 | ticker.Vol = ToFloat64(tickermap["vol"]) 60 | date := tickermap["date"].(float64) 61 | ticker.Date = uint64(date) 62 | 63 | return &ticker, nil 64 | } 65 | 66 | func (btch *BTCChina) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 67 | depthresp, err := HttpGet(btch.httpClient, fmt.Sprintf("%s/orderbook?market=%s&limit=%d", 68 | _MARKET_API_URL, strings.ToLower(currency.ToSymbol("")), size)) 69 | 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | depth := Depth{} 75 | asks := depthresp["asks"].([]interface{}) 76 | bids := depthresp["bids"].([]interface{}) 77 | 78 | //log.Println(asks) 79 | //log.Println(bids) 80 | 81 | for _, v := range asks { 82 | var dr DepthRecord 83 | for i, vv := range v.([]interface{}) { 84 | switch i { 85 | case 0: 86 | dr.Price = ToFloat64(vv) 87 | case 1: 88 | dr.Amount = ToFloat64(vv) 89 | } 90 | } 91 | depth.AskList = append(depth.AskList, dr) 92 | } 93 | 94 | for _, v := range bids { 95 | var dr DepthRecord 96 | for i, vv := range v.([]interface{}) { 97 | switch i { 98 | case 0: 99 | dr.Price = ToFloat64(vv) 100 | case 1: 101 | dr.Amount = ToFloat64(vv) 102 | } 103 | } 104 | depth.BidList = append(depth.BidList, dr) 105 | } 106 | 107 | return &depth, nil 108 | } 109 | 110 | func (btch *BTCChina) GetKlineRecords(currency CurrencyPair, period , size, since int) ([]Kline, error) { 111 | panic("unimplement") 112 | } 113 | 114 | func (btch *BTCChina) GetAccount() (*Account, error) { 115 | respmap, err := btch.sendAuthorizationRequst("getAccountInfo", []interface{}{}) 116 | if err != nil { 117 | return nil, err 118 | } 119 | 120 | result := respmap["result"].(map[string]interface{}) 121 | balance := result["balance"].(map[string]interface{}) 122 | frozen := result["frozen"].(map[string]interface{}) 123 | 124 | acc := new(Account) 125 | acc.SubAccounts = make(map[Currency]SubAccount, 3) 126 | //log.Println(balance) 127 | //log.Println(frozen) 128 | 129 | for c, v := range balance { 130 | vv := v.(map[string]interface{}) 131 | _frozen := frozen[c].(map[string]interface{}) 132 | 133 | sub := SubAccount{ 134 | Amount: ToFloat64(vv["amount"]), 135 | ForzenAmount: ToFloat64(_frozen["amount"])} 136 | var currency Currency 137 | 138 | switch c { 139 | case "cny": 140 | currency = CNY 141 | case "btc": 142 | currency = BTC 143 | case "ltc": 144 | currency = LTC 145 | default: 146 | currency = UNKNOWN 147 | } 148 | 149 | acc.SubAccounts[currency] = sub 150 | } 151 | 152 | return acc, nil 153 | } 154 | 155 | func (btch *BTCChina) placeorder(method, amount, price string, currencyPair CurrencyPair) (*Order, error) { 156 | respmap, err := btch.sendAuthorizationRequst(method, []interface{}{ 157 | price, amount, strings.ToUpper(currencyPair.ToSymbol(""))}) 158 | 159 | if err != nil { 160 | return nil, err 161 | } 162 | 163 | ord := new(Order) 164 | ord.OrderID = ToInt(respmap["result"]) 165 | ord.Currency = currencyPair 166 | ord.Amount = ToFloat64(amount) 167 | ord.Price = ToFloat64(price) 168 | 169 | return ord, nil 170 | } 171 | 172 | func (btch *BTCChina) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 173 | ord, err := btch.placeorder("buyOrder2", amount, price, currency) 174 | ord.Side = BUY 175 | return ord, err 176 | } 177 | 178 | func (btch *BTCChina) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 179 | ord, err := btch.placeorder("sellOrder2", amount, price, currency) 180 | ord.Side = SELL 181 | return ord, err 182 | } 183 | 184 | func (btch *BTCChina) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 185 | panic("unimplement") 186 | } 187 | 188 | func (btch *BTCChina) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 189 | panic("unimplement") 190 | } 191 | 192 | func (btch *BTCChina) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 193 | respmap, err := btch.sendAuthorizationRequst("cancelOrder", 194 | []interface{}{ToInt(orderId), strings.ToUpper(currency.ToSymbol(""))}) 195 | if err != nil { 196 | return false, err 197 | } 198 | return respmap["result"].(bool), nil 199 | } 200 | 201 | func (btch *BTCChina) toOrder(ordermap map[string]interface{}) Order { 202 | ord := Order{} 203 | ord.OrderID = ToInt(ordermap["id"]) 204 | ord.Price = ToFloat64(ordermap["price"]) 205 | ord.Amount = ToFloat64(ordermap["amount_original"]) 206 | ord.DealAmount = ToFloat64(ordermap["amount"]) 207 | ord.AvgPrice = ToFloat64(ordermap["price"]) 208 | 209 | switch ordermap["status"].(string) { 210 | case "closed": 211 | ord.Status = ORDER_FINISH 212 | case "cancelled": 213 | ord.Status = ORDER_CANCEL 214 | case "open": 215 | ord.Status = ORDER_UNFINISH 216 | case "pending": 217 | ord.Status = ORDER_UNFINISH 218 | default: 219 | ord.Status = ORDER_UNFINISH 220 | } 221 | 222 | switch ordermap["type"].(string) { 223 | case "ask": 224 | ord.Side = SELL 225 | case "bid": 226 | ord.Side = BUY 227 | } 228 | 229 | return ord 230 | } 231 | 232 | func (btch *BTCChina) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 233 | respmap, err := btch.sendAuthorizationRequst("getOrder", 234 | []interface{}{ToInt(orderId), strings.ToUpper(currency.ToSymbol(""))}) 235 | if err != nil { 236 | return nil, err 237 | } 238 | 239 | result := respmap["result"].(map[string]interface{}) 240 | ordermap := result["order"].(map[string]interface{}) 241 | 242 | ord := btch.toOrder(ordermap) 243 | return &ord, nil 244 | } 245 | 246 | func (btch *BTCChina) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 247 | respmap, err := btch.sendAuthorizationRequst("getOrders", []interface{}{ 248 | true, strings.ToUpper(currency.ToSymbol(""))}) 249 | if err != nil { 250 | return nil, err 251 | } 252 | 253 | orders := make([]Order, 0) 254 | result := respmap["result"].(map[string]interface{}) 255 | ordersmap := result["order"].([]interface{}) 256 | for _, ord := range ordersmap { 257 | orders = append(orders, btch.toOrder(ord.(map[string]interface{}))) 258 | } 259 | 260 | return orders, nil 261 | } 262 | 263 | func (btch *BTCChina) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 264 | panic("unimplement") 265 | } 266 | 267 | //非个人,整个交易所的交易记录 268 | func (btch *BTCChina) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 269 | panic("unimplement") 270 | } 271 | 272 | func (btch *BTCChina) GetExchangeName() string { 273 | return "btcchina.com" 274 | } 275 | 276 | func (btch *BTCChina) GetBasicAuth(sign string) string { 277 | authStr := btch.accessKey + ":" + sign 278 | basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(authStr)) 279 | return basicAuth 280 | } 281 | 282 | func (btch *BTCChina) sendAuthorizationRequst(method string, params []interface{}) (map[string]interface{}, error) { 283 | reqParams := btch.buildPostForm(method, params) 284 | reqJsonParams, _ := json.Marshal(reqParams) 285 | println(string(reqJsonParams)) 286 | 287 | resp, err := HttpPostForm3(btch.httpClient, 288 | _TRADE_API_V1_URL, 289 | string(reqJsonParams), 290 | map[string]string{"Json-Rpc-Tonce": reqParams.Tonce, 291 | "Authorization": btch.GetBasicAuth(reqParams.Sign)}) 292 | 293 | if err != nil { 294 | return nil, err 295 | } 296 | 297 | var respmap map[string]interface{} 298 | err = json.Unmarshal(resp, &respmap) 299 | if err != nil { 300 | return nil, err 301 | } 302 | 303 | if respmap["error"] != nil { 304 | return nil, errors.New(string(resp)) 305 | } 306 | 307 | return respmap, nil 308 | } 309 | 310 | func (btch *BTCChina) buildPostForm(method string, paramAr []interface{}) ReqBody { 311 | /* 312 | tonce=1377743828095093 313 | &accesskey=1d87effa-e84d-48c1-a172-0232b86305dd 314 | &requestmethod=post 315 | &id=1 316 | &method=getAccountInfo 317 | ¶ms= 318 | */ 319 | 320 | paramStr := "" 321 | 322 | if paramAr != nil && len(paramAr) > 0 { 323 | for _, p := range paramAr { 324 | switch p.(type) { 325 | case float64: 326 | paramStr = fmt.Sprintf("%s,%f", paramStr, p) 327 | case string: 328 | paramStr = fmt.Sprintf("%s,%s", paramStr, p) 329 | case int: 330 | paramStr = fmt.Sprintf("%s,%d", paramStr, p) 331 | case bool: 332 | paramStr = fmt.Sprintf("%s,%t", paramStr, p) 333 | } 334 | } 335 | paramStr = paramStr[1:] 336 | //println(paramStr) 337 | } 338 | 339 | tonce := strconv.FormatInt(time.Now().UnixNano(), 10)[0:16] 340 | signPyload := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s¶ms=%s", 341 | tonce, btch.accessKey, method, paramStr) 342 | signStr, _ := GetParamHmacSHA1Sign(btch.secretKey, signPyload) 343 | 344 | reqbody := ReqBody{} 345 | reqbody.Id = 1 346 | reqbody.Method = method 347 | reqbody.Params = paramAr 348 | reqbody.Tonce = tonce 349 | reqbody.Sign = signStr 350 | 351 | return reqbody 352 | } 353 | -------------------------------------------------------------------------------- /btcc/BTCChina_test.go: -------------------------------------------------------------------------------- 1 | package btcc 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | var btch = NewBTCChina(http.DefaultClient, "", "") 11 | 12 | func TestBTCChina_GetTicker(t *testing.T) { 13 | ticker, err := btch.GetTicker(goex.BTC_CNY) 14 | assert.Nil(t, err) 15 | t.Log(ticker) 16 | } 17 | 18 | func TestBTCChina_GetDepth(t *testing.T) { 19 | dep, err := btch.GetDepth(2, goex.BTC_CNY) 20 | assert.Nil(t, err) 21 | t.Log(dep) 22 | } 23 | 24 | func TestBTCChina_GetAccount(t *testing.T) { 25 | acc, err := btch.GetAccount() 26 | assert.Nil(t, err) 27 | t.Log(acc) 28 | } 29 | 30 | func TestBTCChina_LimitBuy(t *testing.T) { 31 | ord, err := btch.LimitBuy("0.001", "200", goex.LTC_CNY) 32 | assert.Nil(t, err) 33 | t.Log(ord) 34 | } 35 | 36 | func TestBTCChina_CancelOrder(t *testing.T) { 37 | r, err := btch.CancelOrder("24956079", goex.LTC_CNY) 38 | assert.Nil(t, err) 39 | t.Log(r) 40 | } 41 | 42 | func TestBTCChina_GetOneOrder(t *testing.T) { 43 | order, err := btch.GetOneOrder("24956079", goex.LTC_CNY) 44 | assert.Nil(t, err) 45 | t.Log(order) 46 | } 47 | 48 | func TestBTCChina_GetUnfinishOrders(t *testing.T) { 49 | ords, err := btch.GetUnfinishOrders(goex.LTC_CNY) 50 | assert.Nil(t, err) 51 | t.Log(ords) 52 | } 53 | -------------------------------------------------------------------------------- /btcmarkets/Btcmarkets.go: -------------------------------------------------------------------------------- 1 | package btcmarkets 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | . "github.com/wudian/GoEx" 7 | "net/http" 8 | "time" 9 | ) 10 | 11 | const ( 12 | EXCHANGE_NAME = "btcmarkets.net" 13 | 14 | API_BASE_URL = "https://api.btcmarkets.net/" 15 | 16 | TICKER_URI = "market/%s/%s/tick" 17 | ) 18 | 19 | type Btcmarkets struct { 20 | accessKey, 21 | secretKey string 22 | httpClient *http.Client 23 | } 24 | 25 | func New(client *http.Client, accessKey, secretKey string) *Btcmarkets { 26 | return &Btcmarkets{accessKey, secretKey, client} 27 | } 28 | 29 | func (btcm *Btcmarkets) GetExchangeName() string { 30 | return EXCHANGE_NAME 31 | } 32 | 33 | func (btcm *Btcmarkets) GetTicker(currency CurrencyPair) (*Ticker, error) { 34 | tickerUri := fmt.Sprintf(API_BASE_URL+TICKER_URI, currency.CurrencyA.String(), currency.CurrencyB.String()) 35 | //log.Println("tickerUrl:", tickerUri) 36 | bodyDataMap, err := HttpGet(btcm.httpClient, tickerUri) 37 | //log.Println("Btcmarkets bodyDataMap:", tickerUri, bodyDataMap) 38 | 39 | timestamp := time.Now().Unix() 40 | if err != nil { 41 | //log.Println(err) 42 | return nil, err 43 | } 44 | 45 | if result, isok := bodyDataMap["success"].(bool); isok == true && result != true { 46 | //log.Println("bodyDataMap[\"success\"]", isok, result) 47 | return nil, errors.New("err") 48 | } 49 | 50 | var tickerMap map[string]interface{} = bodyDataMap 51 | var ticker Ticker 52 | 53 | //fmt.Println(bodyDataMap) 54 | ticker.Date = uint64(timestamp) 55 | ticker.Last, _ = tickerMap["lastPrice"].(float64) 56 | 57 | ticker.Buy, _ = tickerMap["bestBid"].(float64) 58 | ticker.Sell, _ = tickerMap["bestAsk"].(float64) 59 | ticker.Vol, _ = tickerMap["volume24h"].(float64) 60 | //log.Println("Btcmarkets", currency, "ticker:", ticker) 61 | return &ticker, nil 62 | } 63 | func (btcm *Btcmarkets) GetTickers(currency CurrencyPair) (*Ticker, error) { 64 | return btcm.GetTicker(currency) 65 | } 66 | 67 | func (btcm *Btcmarkets) GetTickerInBuf(currency CurrencyPair) (*Ticker, error) { 68 | return btcm.GetTicker(currency) 69 | } 70 | 71 | func (btcm *Btcmarkets) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 72 | panic("not implement") 73 | } 74 | 75 | func (btcm *Btcmarkets) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 76 | panic("not implements") 77 | } 78 | 79 | func (btcm *Btcmarkets) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 80 | panic("not implements") 81 | } 82 | 83 | func (btcm *Btcmarkets) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 84 | panic("not implements") 85 | } 86 | 87 | func (btcm *Btcmarkets) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 88 | panic("not implements") 89 | } 90 | 91 | func (btcm *Btcmarkets) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 92 | panic("not implements") 93 | } 94 | 95 | func (btcm *Btcmarkets) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 96 | panic("not implements") 97 | } 98 | func (btcm *Btcmarkets) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 99 | panic("not implements") 100 | } 101 | 102 | func (btcm *Btcmarkets) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 103 | panic("not implements") 104 | } 105 | 106 | func (btcm *Btcmarkets) GetAccount() (*Account, error) { 107 | panic("not implements") 108 | } 109 | 110 | func (btcm *Btcmarkets) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 111 | panic("not implements") 112 | } 113 | 114 | //非个人,整个交易所的交易记录 115 | func (btcm *Btcmarkets) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 116 | panic("not implements") 117 | } 118 | -------------------------------------------------------------------------------- /btcmarkets/Btcmarkets_test.go: -------------------------------------------------------------------------------- 1 | package btcmarkets 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var btcm = New(http.DefaultClient, "", "") 10 | 11 | func TestBtcm_GetTicker(t *testing.T) { 12 | AUD := goex.NewCurrency("AUD", "") 13 | BTC_AUD := goex.NewCurrencyPair(goex.BTC, AUD) 14 | ticker, err := btcm.GetTicker(BTC_AUD) 15 | t.Log("err=>", err) 16 | t.Log("ticker=>", ticker) 17 | } 18 | -------------------------------------------------------------------------------- /builder/APIBuilder.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | . "github.com/wudian/GoEx" 7 | "github.com/wudian/GoEx/bigone" 8 | "github.com/wudian/GoEx/binance" 9 | "github.com/wudian/GoEx/bitfinex" 10 | "github.com/wudian/GoEx/bithumb" 11 | "github.com/wudian/GoEx/bitstamp" 12 | "github.com/wudian/GoEx/bittrex" 13 | "github.com/wudian/GoEx/coin58" 14 | "github.com/wudian/GoEx/coinex" 15 | "github.com/wudian/GoEx/fcoin" 16 | "github.com/wudian/GoEx/gateio" 17 | "github.com/wudian/GoEx/gdax" 18 | "github.com/wudian/GoEx/hitbtc" 19 | "github.com/wudian/GoEx/huobi" 20 | "github.com/wudian/GoEx/kraken" 21 | "github.com/wudian/GoEx/okcoin" 22 | "github.com/wudian/GoEx/okex" 23 | "github.com/wudian/GoEx/poloniex" 24 | "github.com/wudian/GoEx/zb" 25 | "net" 26 | "net/http" 27 | "net/url" 28 | "time" 29 | ) 30 | 31 | type APIBuilder struct { 32 | client *http.Client 33 | httpTimeout time.Duration 34 | apiKey string 35 | secretkey string 36 | clientId string 37 | apiPassphrase string 38 | } 39 | 40 | func NewAPIBuilder() (builder *APIBuilder) { 41 | _client := http.DefaultClient 42 | transport := &http.Transport{ 43 | MaxIdleConns: 10, 44 | IdleConnTimeout: 4 * time.Second, 45 | } 46 | _client.Transport = transport 47 | return &APIBuilder{client: _client} 48 | } 49 | 50 | func NewCustomAPIBuilder(client *http.Client) (builder *APIBuilder) { 51 | return &APIBuilder{client: client} 52 | } 53 | 54 | func (builder *APIBuilder) APIKey(key string) (_builder *APIBuilder) { 55 | builder.apiKey = key 56 | return builder 57 | } 58 | 59 | func (builder *APIBuilder) APISecretkey(key string) (_builder *APIBuilder) { 60 | builder.secretkey = key 61 | return builder 62 | } 63 | 64 | func (builder *APIBuilder) HttpProxy(proxyUrl string) (_builder *APIBuilder) { 65 | proxy, err := url.Parse(proxyUrl) 66 | if err != nil { 67 | return 68 | } 69 | transport := builder.client.Transport.(*http.Transport) 70 | transport.Proxy = http.ProxyURL(proxy) 71 | return builder 72 | } 73 | 74 | func (builder *APIBuilder) ClientID(id string) (_builder *APIBuilder) { 75 | builder.clientId = id 76 | return builder 77 | } 78 | 79 | func (builder *APIBuilder) HttpTimeout(timeout time.Duration) (_builder *APIBuilder) { 80 | builder.httpTimeout = timeout 81 | builder.client.Timeout = timeout 82 | transport := builder.client.Transport.(*http.Transport) 83 | if transport != nil { 84 | transport.ResponseHeaderTimeout = timeout 85 | transport.TLSHandshakeTimeout = timeout 86 | transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { 87 | return net.DialTimeout(network, addr, timeout) 88 | } 89 | } 90 | return builder 91 | } 92 | 93 | func (builder *APIBuilder) ApiPassphrase(apiPassphrase string) (_builder *APIBuilder) { 94 | builder.apiPassphrase = apiPassphrase 95 | return builder 96 | } 97 | 98 | func (builder *APIBuilder) Build(exName string) (api API) { 99 | var _api API 100 | switch exName { 101 | //case OKCOIN_CN: 102 | // _api = okcoin.New(builder.client, builder.apiKey, builder.secretkey) 103 | case POLONIEX: 104 | _api = poloniex.New(builder.client, builder.apiKey, builder.secretkey) 105 | case OKCOIN_COM: 106 | _api = okcoin.NewCOM(builder.client, builder.apiKey, builder.secretkey) 107 | case BITSTAMP: 108 | _api = bitstamp.NewBitstamp(builder.client, builder.apiKey, builder.secretkey, builder.clientId) 109 | case HUOBI_PRO: 110 | _api = huobi.NewHuoBiProSpot(builder.client, builder.apiKey, builder.secretkey) 111 | case OKEX: 112 | _api = okcoin.NewOKExSpot(builder.client, builder.apiKey, builder.secretkey) 113 | case BITFINEX: 114 | _api = bitfinex.New(builder.client, builder.apiKey, builder.secretkey) 115 | case KRAKEN: 116 | _api = kraken.New(builder.client, builder.apiKey, builder.secretkey) 117 | case BINANCE: 118 | _api = binance.New(builder.client, builder.apiKey, builder.secretkey) 119 | case BITTREX: 120 | _api = bittrex.New(builder.client, builder.apiKey, builder.secretkey) 121 | case BITHUMB: 122 | _api = bithumb.New(builder.client, builder.apiKey, builder.secretkey) 123 | case GDAX: 124 | _api = gdax.New(builder.client, builder.apiKey, builder.secretkey) 125 | case GATEIO: 126 | _api = gateio.New(builder.client, builder.apiKey, builder.secretkey) 127 | case ZB: 128 | _api = zb.New(builder.client, builder.apiKey, builder.secretkey) 129 | case COINEX: 130 | _api = coinex.New(builder.client, builder.apiKey, builder.secretkey) 131 | case FCOIN: 132 | _api = fcoin.NewFCoin(builder.client, builder.apiKey, builder.secretkey) 133 | case COIN58: 134 | _api = coin58.New58Coin(builder.client, builder.apiKey, builder.secretkey) 135 | case BIGONE: 136 | _api = bigone.New(builder.client, builder.apiKey, builder.secretkey) 137 | case HITBTC: 138 | _api = hitbtc.New(builder.client, builder.apiKey, builder.secretkey) 139 | default: 140 | panic("exchange name error [" + exName + "].") 141 | 142 | } 143 | return _api 144 | } 145 | 146 | func (builder *APIBuilder) BuildFuture(exName string) (api FutureRestAPI) { 147 | switch exName { 148 | case OKEX_FUTURE: 149 | return okcoin.NewOKEx(builder.client, builder.apiKey, builder.secretkey) 150 | case HBDM: 151 | return huobi.NewHbdm(&APIConfig{HttpClient: builder.client, ApiKey: builder.apiKey, ApiSecretKey: builder.secretkey}) 152 | case OKEX_SWAP: 153 | return okex.NewOKExSwap(&APIConfig{HttpClient: builder.client, Endpoint: "https://www.okex.com", ApiKey: builder.apiKey, ApiSecretKey: builder.secretkey, ApiPassphrase: builder.apiPassphrase}) 154 | default: 155 | panic(fmt.Sprintf("%s not support", exName)) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /builder/APIBuilder_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var builder = NewAPIBuilder() 10 | 11 | func TestAPIBuilder_Build(t *testing.T) { 12 | assert.Equal(t, builder.APIKey("").APISecretkey("").Build(goex.OKCOIN_COM).GetExchangeName(), goex.OKCOIN_COM) 13 | assert.Equal(t, builder.APIKey("").APISecretkey("").Build(goex.HUOBI_PRO).GetExchangeName(), goex.HUOBI_PRO) 14 | assert.Equal(t, builder.APIKey("").APISecretkey("").Build(goex.ZB).GetExchangeName(), goex.ZB) 15 | assert.Equal(t, builder.APIKey("").APISecretkey("").Build(goex.BIGONE).GetExchangeName(), goex.BIGONE) 16 | assert.Equal(t, builder.APIKey("").APISecretkey("").Build(goex.OKEX).GetExchangeName(), goex.OKEX) 17 | assert.Equal(t, builder.APIKey("").APISecretkey("").Build(goex.POLONIEX).GetExchangeName(), goex.POLONIEX) 18 | assert.Equal(t, builder.APIKey("").APISecretkey("").Build(goex.KRAKEN).GetExchangeName(), goex.KRAKEN) 19 | assert.Equal(t, builder.APIKey("").APISecretkey("").BuildFuture(goex.HBDM).GetExchangeName(), goex.HBDM) 20 | } 21 | -------------------------------------------------------------------------------- /c-cex/C-cex.go: -------------------------------------------------------------------------------- 1 | package c_cex 2 | 3 | import ( 4 | . "github.com/wudian/GoEx" 5 | //"log" 6 | "net/http" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | const ( 12 | EXCHANGE_NAME = "c-cex.com" 13 | 14 | API_BASE_URL = "https://c-cex.com/" 15 | 16 | TICKER_URI = "t/" 17 | ) 18 | 19 | type C_cex struct { 20 | accessKey, 21 | secretKey string 22 | httpClient *http.Client 23 | } 24 | 25 | func New(client *http.Client, accessKey, secretKey string) *C_cex { 26 | return &C_cex{accessKey, secretKey, client} 27 | } 28 | 29 | func (ccex *C_cex) GetExchangeName() string { 30 | return EXCHANGE_NAME 31 | } 32 | 33 | func (ccex *C_cex) GetTicker(currency CurrencyPair) (*Ticker, error) { 34 | currency = ccex.adaptCurrencyPair(currency) 35 | 36 | tickerUri := API_BASE_URL + TICKER_URI + strings.ToLower(currency.ToSymbol("-")) + ".json" 37 | //log.Println("tickerUrl:", tickerUri) 38 | bodyDataMap, err := HttpGet(ccex.httpClient, tickerUri) 39 | //log.Println("C_cex bodyDataMap:", tickerUri, bodyDataMap) 40 | 41 | timestamp := time.Now().Unix() 42 | if err != nil { 43 | //log.Println(err) 44 | return nil, err 45 | } 46 | 47 | tickerMap := bodyDataMap["ticker"].(map[string]interface{}) 48 | var ticker Ticker 49 | 50 | //fmt.Println(bodyDataMap) 51 | ticker.Date = uint64(timestamp) 52 | ticker.Last, _ = tickerMap["lastprice"].(float64) 53 | ticker.Buy, _ = tickerMap["buy"].(float64) 54 | ticker.Sell, _ = tickerMap["sell"].(float64) 55 | //ticker.Vol, _ = tickerMap["Volume"].(float64) 56 | //log.Println("C_cex", currency, "ticker:", ticker) 57 | return &ticker, nil 58 | } 59 | func (ccex *C_cex) GetTickers(currency CurrencyPair) (*Ticker, error) { 60 | return ccex.GetTicker(currency) 61 | } 62 | 63 | func (ccex *C_cex) GetTickerInBuf(currency CurrencyPair) (*Ticker, error) { 64 | return ccex.GetTicker(currency) 65 | } 66 | 67 | func (ccex *C_cex) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 68 | panic("not implement") 69 | } 70 | func (ccex *C_cex) adaptCurrencyPair(pair CurrencyPair) CurrencyPair { 71 | var currencyA Currency 72 | var currencyB Currency 73 | 74 | //if pair.QuoteCurrency == BCC { 75 | // currencyA = BCH 76 | //} else { 77 | // currencyA = pair.QuoteCurrency 78 | //} 79 | currencyA = pair.CurrencyA 80 | if pair.CurrencyA == USDT { 81 | currencyB = USD 82 | } else { 83 | currencyB = pair.CurrencyA 84 | } 85 | 86 | return NewCurrencyPair(currencyA, currencyB) 87 | } 88 | 89 | func (ccex *C_cex) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 90 | panic("not implements") 91 | } 92 | 93 | func (ccex *C_cex) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 94 | panic("not implements") 95 | } 96 | 97 | func (ccex *C_cex) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 98 | panic("not implements") 99 | } 100 | 101 | func (ccex *C_cex) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 102 | panic("not implements") 103 | } 104 | 105 | func (ccex *C_cex) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 106 | panic("not implements") 107 | } 108 | 109 | func (ccex *C_cex) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 110 | panic("not implements") 111 | } 112 | func (ccex *C_cex) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 113 | panic("not implements") 114 | } 115 | 116 | func (ccex *C_cex) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 117 | panic("not implements") 118 | } 119 | 120 | func (ccex *C_cex) GetAccount() (*Account, error) { 121 | panic("not implements") 122 | } 123 | 124 | func (ccex *C_cex) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 125 | panic("not implements") 126 | } 127 | 128 | //非个人,整个交易所的交易记录 129 | func (ccex *C_cex) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 130 | panic("not implements") 131 | } 132 | -------------------------------------------------------------------------------- /c-cex/C-cex_test.go: -------------------------------------------------------------------------------- 1 | package c_cex 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var ccex = New(http.DefaultClient, "", "") 10 | 11 | func TestCcex_GetTicker(t *testing.T) { 12 | ticker, err := ccex.GetTicker(goex.BTC_USD) 13 | t.Log("err=>", err) 14 | t.Log("ticker=>", ticker) 15 | } 16 | -------------------------------------------------------------------------------- /coin58/coin58.go: -------------------------------------------------------------------------------- 1 | package coin58 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "fmt" 7 | . "github.com/wudian/GoEx" 8 | "net/http" 9 | "net/url" 10 | "sort" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | type Coin58 struct { 16 | client *http.Client 17 | apikey string 18 | apisecretkey string 19 | apiurl string 20 | } 21 | 22 | //58coin.com closed the trade api 23 | func New58Coin(client *http.Client, apikey string, apisecretkey string) *Coin58 { 24 | return &Coin58{client: client, apikey: apikey, apisecretkey: apisecretkey, apiurl: "https://api.58coin.com/v1/"} 25 | } 26 | 27 | func (coin58 *Coin58) placeOrder(t, side, amount, price string, currency CurrencyPair) (*Order, error) { 28 | var params = url.Values{} 29 | params.Set("symbol", currency.AdaptUsdToUsdt().ToSymbol("_")) 30 | params.Set("type", t) 31 | params.Set("side", side) 32 | params.Set("amount", amount) 33 | //params.Set("client_oid" , "1") 34 | if t == "limit" { 35 | params.Set("price", price) 36 | } 37 | 38 | r, err := coin58.doAuthenticatedRequest("spot/my/order/place", params) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | o := r.(map[string]interface{}) 44 | 45 | //log.Println(r) 46 | 47 | var tradeSide TradeSide = SELL 48 | 49 | switch side { 50 | case "sell": 51 | tradeSide = SELL 52 | case "buy": 53 | tradeSide = BUY 54 | } 55 | 56 | return &Order{ 57 | OrderID2: o["order_id"].(string), 58 | Currency: currency, 59 | Amount: ToFloat64(amount), 60 | Price: ToFloat64(price), 61 | Status: ORDER_UNFINISH, 62 | Side: tradeSide, 63 | OrderTime: ToInt(o["created_time"])}, nil 64 | } 65 | 66 | func (coin58 *Coin58) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 67 | return coin58.placeOrder("limit", "buy", amount, price, currency) 68 | } 69 | 70 | func (coin58 *Coin58) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 71 | return coin58.placeOrder("limit", "sell", amount, price, currency) 72 | } 73 | 74 | func (coin58 *Coin58) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 75 | return coin58.placeOrder("market", "buy", amount, price, currency) 76 | } 77 | 78 | func (coin58 *Coin58) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 79 | return coin58.placeOrder("market", "sell", amount, price, currency) 80 | } 81 | 82 | func (coin58 *Coin58) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 83 | params := url.Values{} 84 | params.Set("symbol", currency.AdaptUsdToUsdt().ToSymbol("_")) 85 | params.Set("order_id", orderId) 86 | 87 | _, err := coin58.doAuthenticatedRequest("spot/my/order/cancel", params) 88 | if err != nil { 89 | return false, err 90 | } 91 | 92 | return true, nil 93 | } 94 | 95 | func (coin58 *Coin58) toOrder(o map[string]interface{}, pair CurrencyPair) *Order { 96 | var side TradeSide = SELL 97 | if o["side"].(string) == "buy" { 98 | side = BUY 99 | } 100 | 101 | var oStatus TradeStatus = ORDER_UNFINISH 102 | 103 | switch o["status"].(string) { 104 | case "Finished", "finished": 105 | oStatus = ORDER_FINISH 106 | case "Cancelled", "cancelled": 107 | oStatus = ORDER_CANCEL 108 | case "Cancelling", "cancelling": 109 | oStatus = ORDER_CANCEL_ING 110 | //case "Active", "active": 111 | // oStatus = ORDER_PART_FINISH 112 | } 113 | 114 | avg := 0.0 115 | if ToFloat64(o["quote_filled"]) > 0 && ToFloat64(o["base_filled"]) > 0 { 116 | avg = ToFloat64(o["quote_filled"]) / ToFloat64(o["base_filled"]) 117 | } 118 | 119 | return &Order{ 120 | Currency: pair, 121 | OrderID2: fmt.Sprint(o["order_id"]), 122 | Price: ToFloat64(o["price"]), 123 | Amount: ToFloat64(o["amount"]) / ToFloat64(o["price"]), 124 | AvgPrice: avg, 125 | DealAmount: ToFloat64(o["base_filled"]), 126 | Side: side, 127 | Status: oStatus, 128 | OrderTime: ToInt(o["created_time"])} 129 | } 130 | 131 | func (coin58 *Coin58) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 132 | params := url.Values{} 133 | params.Set("symbol", currency.AdaptUsdToUsdt().ToSymbol("_")) 134 | params.Set("order_id", orderId) 135 | 136 | r, err := coin58.doAuthenticatedRequest("spot/my/order", params) 137 | if err != nil { 138 | return nil, err 139 | } 140 | 141 | o := r.(map[string]interface{}) 142 | // log.Println(o) 143 | return coin58.toOrder(o, currency), nil 144 | } 145 | 146 | func (coin58 *Coin58) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 147 | params := url.Values{} 148 | params.Set("symbol", currency.AdaptUsdToUsdt().ToSymbol("_")) 149 | 150 | r, err := coin58.doAuthenticatedRequest("spot/my/orders", params) 151 | if err != nil { 152 | return nil, err 153 | } 154 | 155 | omaps, isok := r.([]interface{}) 156 | if !isok { 157 | return []Order{}, nil 158 | } 159 | //log.Println(omaps) 160 | var orders []Order 161 | for _, o := range omaps { 162 | orders = append(orders, *coin58.toOrder(o.(map[string]interface{}), currency)) 163 | } 164 | 165 | return orders, nil 166 | } 167 | 168 | func (coin58 *Coin58) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 169 | panic("not implement") 170 | } 171 | 172 | func (coin58 *Coin58) GetAccount() (*Account, error) { 173 | r, err := coin58.doAuthenticatedRequest("spot/my/accounts", url.Values{}) 174 | if err != nil { 175 | return nil, err 176 | } 177 | 178 | acc := new(Account) 179 | acc.Exchange = COIN58 180 | acc.SubAccounts = make(map[Currency]SubAccount) 181 | 182 | for _, c := range r.([]interface{}) { 183 | c2 := c.(map[string]interface{}) 184 | balance := ToFloat64(c2["balance"]) 185 | available := ToFloat64(c2["available"]) 186 | currency := NewCurrency(c2["currency"].(string), "") 187 | acc.SubAccounts[currency] = SubAccount{ 188 | Currency: currency, 189 | Amount: available, 190 | ForzenAmount: balance - available, 191 | LoanAmount: 0} 192 | } 193 | 194 | return acc, err 195 | } 196 | 197 | func (coin58 *Coin58) GetTicker(currency CurrencyPair) (*Ticker, error) { 198 | tickerUrl := coin58.apiurl + "spot/ticker?symbol=" + currency.AdaptUsdToUsdt().ToSymbol("_") 199 | m, err := HttpGet(coin58.client, tickerUrl) 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | error := m["error"] 205 | if error != nil { 206 | return nil, coin58.adaptError(error.(map[string]interface{})) 207 | } 208 | 209 | r, isok := m["result"].([]interface{}) 210 | if !isok || len(r) == 0 { 211 | return nil, API_ERR 212 | } 213 | 214 | t := r[0].(map[string]interface{}) 215 | 216 | return &Ticker{Pair: currency, 217 | Last: ToFloat64(t["last"]), 218 | Low: ToFloat64(t["low"]), 219 | High: ToFloat64(t["high"]), 220 | Sell: ToFloat64(t["ask"]), 221 | Buy: ToFloat64(t["bid"]), 222 | Vol: ToFloat64(t["volume"]), 223 | Date: ToUint64(t["time"]) / 1000}, nil 224 | } 225 | 226 | func (coin58 *Coin58) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 227 | depurl := coin58.apiurl + "spot/order_book?symbol=" + currency.AdaptUsdToUsdt().ToSymbol("_") + "&limit=" + fmt.Sprint(size) 228 | m, err := HttpGet(coin58.client, depurl) 229 | if err != nil { 230 | return nil, HTTP_ERR_CODE.OriginErr(err.Error()) 231 | } 232 | 233 | error := m["error"] 234 | if error != nil { 235 | return nil, coin58.adaptError(error.(map[string]interface{})) 236 | } 237 | // log.Println(m) 238 | r := m["result"].(map[string]interface{}) 239 | asks := r["asks"].([]interface{}) 240 | bids := r["bids"].([]interface{}) 241 | 242 | dep := new(Depth) 243 | dep.Pair = currency 244 | 245 | for _, ask := range asks { 246 | ask2 := ask.([]interface{}) 247 | dep.AskList = append(dep.AskList, DepthRecord{ToFloat64(ask2[0]), ToFloat64(ask2[1])}) 248 | } 249 | 250 | for _, bid := range bids { 251 | bid2 := bid.([]interface{}) 252 | dep.BidList = append(dep.BidList, DepthRecord{ToFloat64(bid2[0]), ToFloat64(bid2[1])}) 253 | } 254 | 255 | sort.Sort(sort.Reverse(dep.AskList)) 256 | sort.Sort(sort.Reverse(dep.BidList)) 257 | 258 | return dep, nil 259 | } 260 | 261 | func (coin58 *Coin58) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 262 | panic("not implement") 263 | } 264 | 265 | //非个人,整个交易所的交易记录 266 | func (coin58 *Coin58) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 267 | panic("not implement") 268 | } 269 | 270 | func (coin58 *Coin58) GetExchangeName() string { 271 | return COIN58 272 | } 273 | 274 | func (coin58 *Coin58) doAuthenticatedRequest(api string, params url.Values) (interface{}, error) { 275 | url := coin58.apiurl + api 276 | 277 | var paramsmap = map[string]string{} 278 | for k, v := range params { 279 | paramsmap[k] = v[0] 280 | } 281 | 282 | timestamp := fmt.Sprint(time.Now().Unix() * 1000) 283 | 284 | params.Set("api_key", coin58.apikey) 285 | 286 | signParamString := params.Encode() + "&api_secret=" + coin58.apisecretkey + "×tamp=" + timestamp 287 | 288 | sign, _ := GetParamHmacSHA256Sign(coin58.apisecretkey, signParamString) 289 | 290 | header := map[string]string{ 291 | "X-58COIN-APIKEY": coin58.apikey, 292 | "X-58COIN-SIGNATURE": base64.URLEncoding.EncodeToString([]byte(strings.ToUpper(sign))), 293 | "X-58COIN-TIMESTAMP": timestamp} 294 | 295 | params.Del("api_key") 296 | 297 | body, err := HttpPostForm2(coin58.client, url, params, header) 298 | 299 | if err != nil { 300 | return nil, HTTP_ERR_CODE 301 | } 302 | 303 | var m map[string]interface{} 304 | json.Unmarshal(body, &m) 305 | 306 | errm := m["error"] 307 | if errm != nil { 308 | return nil, coin58.adaptError(errm.(map[string]interface{})) 309 | } 310 | 311 | return m["result"], nil 312 | } 313 | 314 | func (coin58 *Coin58) adaptError(errmap map[string]interface{}) ApiError { 315 | code := ToInt(errmap["code"]) 316 | switch code { 317 | case 10006: 318 | return EX_ERR_NOT_FIND_APIKEY 319 | case 10007: 320 | return EX_ERR_SIGN 321 | case 20000: 322 | return EX_ERR_SYMBOL_ERR 323 | case 20015: 324 | return EX_ERR_NOT_FIND_ORDER 325 | case 20007: 326 | return EX_ERR_INSUFFICIENT_BALANCE 327 | } 328 | //log.Println(errmap) 329 | return API_ERR 330 | } 331 | -------------------------------------------------------------------------------- /coin58/coin58_test.go: -------------------------------------------------------------------------------- 1 | package coin58 2 | 3 | import ( 4 | "testing" 5 | "net/http" 6 | "github.com/wudian/GoEx" 7 | "time" 8 | ) 9 | 10 | var coin58 = New58Coin(http.DefaultClient, "", "") 11 | 12 | func TestCoin58_GetTicker(t *testing.T) { 13 | t.Log(coin58.GetTicker(goex.NewCurrencyPair2("58b_btc"))) 14 | } 15 | 16 | func TestCoin58_GetDepth(t *testing.T) { 17 | b := time.Now() 18 | dep , _ := coin58.GetDepth(1 , goex.NewCurrencyPair2("58b_ucc")) 19 | //dep, _ := coin58.GetDepth(2, goex.BTC_USD) 20 | t.Log(time.Now().Sub(b)) 21 | t.Log(dep.BidList) 22 | t.Log(dep.AskList) 23 | } 24 | 25 | func TestCoin58_GetAccount(t *testing.T) { 26 | acc , err := coin58.GetAccount() 27 | t.Log(err) 28 | t.Log(acc) 29 | } 30 | 31 | func TestCoin58_LimitSell(t *testing.T) { 32 | ord , err := coin58.LimitSell("11.2" , "0.17" , goex.NewCurrencyPair2("58b_ucc")) 33 | t.Log(err) 34 | t.Log(ord) 35 | } 36 | 37 | func TestCoin58_GetOneOrder(t *testing.T) { 38 | ord , _ := coin58.GetOneOrder("23613" , goex.NewCurrencyPair2("58b_ucc")) //23478 39 | t.Logf("%+v" , ord) 40 | } 41 | 42 | func TestCoin58_GetUnfinishOrders(t *testing.T) { 43 | t.Log(coin58.GetUnfinishOrders(goex.NewCurrencyPair2("58b_usdt"))) 44 | } 45 | 46 | func TestCoin58_CancelOrder(t *testing.T) { 47 | t.Log(coin58.CancelOrder("72" , goex.NewCurrencyPair2("58b_usdt"))) 48 | } 49 | 50 | -------------------------------------------------------------------------------- /coinbig/coinbig_test.go: -------------------------------------------------------------------------------- 1 | package coinbig 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var cb = New(http.DefaultClient, "", "") 10 | 11 | 12 | func TestCoinBig_BuildSigned(t *testing.T) { 13 | return 14 | //params := url.Values{} 15 | // 16 | //params.Set("apikey", cb.accessKey) 17 | //params.Set("symbol", "btc_usdt") 18 | //params.Set("size", "20") 19 | //params.Set("type", "1") 20 | //t.Log(params) 21 | //t.Log(cb.Encode(params)) 22 | //cb.buildSigned(¶ms) 23 | //t.Log(cb.Encode(params)) 24 | //0272D6FAF94868C0E84B9FC86C1266AA 25 | //E94B142B9A11DE390F20D8AB193D7FE1 26 | param := "apikey=7E7D227B7CA207088BF275BACB3015B2&size=20&symbol=btc_usdt&type=1&secret_key=4AC5140A7CDDC7F9610A3C0E3284231C" 27 | 28 | //w := md5.New() 29 | //io.WriteString(w,param) 30 | //md5String := fmt.Sprintf("%x",w.Sum(nil)) 31 | //t.Log(strings.ToUpper(md5String)) 32 | s, _ := goex.GetParamMD5Sign("", param) 33 | t.Log(s) 34 | } 35 | 36 | func TestCoinBig_GetAccount(t *testing.T) { 37 | //return 38 | t.Log(cb.GetAccount()) 39 | } 40 | func TestCoinBig_GetUnfinishOrders(t *testing.T) { 41 | //return 42 | t.Log(cb.GetUnfinishOrders(goex.BTC_USDT)) 43 | } 44 | func TestCoinBig_GetOneOrder(t *testing.T) { 45 | return 46 | t.Log(cb.GetOneOrder("1111", goex.BTC_USDT)) 47 | } 48 | 49 | func TestCoinBig_CancelOrder(t *testing.T) { 50 | return 51 | t.Log(cb.CancelOrder("1111", goex.BTC_USDT)) 52 | 53 | } 54 | func TestCoinBig_GetTicker(t *testing.T) { 55 | return 56 | t.Log(cb.GetTicker(goex.BTC_USDT)) 57 | } 58 | 59 | func TestCoinBig_GetDepth(t *testing.T) { 60 | return 61 | t.Log(cb.GetDepth(3, goex.BTC_USDT)) 62 | } 63 | 64 | func TestCoinBig_LimitBuy(t *testing.T) { 65 | return 66 | t.Log(cb.LimitBuy("1", "1", goex.BTC_USDT)) 67 | } 68 | -------------------------------------------------------------------------------- /coincheck/Coincheck.go: -------------------------------------------------------------------------------- 1 | package coincheck 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/wudian/GoEx" 6 | "log" 7 | "net/http" 8 | //"strconv" 9 | "sort" 10 | "strconv" 11 | ) 12 | 13 | type Coincheck struct { 14 | client *http.Client 15 | baseUrl, 16 | accessKey, 17 | secretKey string 18 | } 19 | 20 | func New(httpClient *http.Client, accessKey, secretKey string) (coinCheck *Coincheck) { 21 | cc := new(Coincheck) 22 | cc.client = httpClient 23 | cc.accessKey = accessKey 24 | cc.secretKey = secretKey 25 | cc.baseUrl = "https://coincheck.com/" 26 | return cc 27 | } 28 | 29 | func (cc *Coincheck) GetExchangeName() string { 30 | return "coincheck.com" 31 | } 32 | 33 | func (cc *Coincheck) GetTicker(currency CurrencyPair) (*Ticker, error) { 34 | tickerUrl := fmt.Sprintf(cc.baseUrl + "api/ticker") 35 | 36 | //println(tickerUrl) 37 | resp, err := HttpGet(cc.client, tickerUrl) 38 | if err != nil { 39 | log.Print(err) 40 | return nil, err 41 | } 42 | //log.Println(resp) 43 | ticker := new(Ticker) 44 | ticker.Buy = resp["bid"].(float64) 45 | ticker.Sell = resp["ask"].(float64) 46 | ticker.Last = resp["last"].(float64) 47 | ticker.High = resp["high"].(float64) 48 | ticker.Low = resp["low"].(float64) 49 | ticker.Date = uint64(resp["timestamp"].(float64)) 50 | ticker.Vol = resp["volume"].(float64) 51 | return ticker, nil 52 | } 53 | 54 | func (cc *Coincheck) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 55 | depthUrl := cc.baseUrl + "api/order_books" 56 | resp, err := HttpGet(cc.client, depthUrl) 57 | if err != nil { 58 | log.Println(err) 59 | return nil, err 60 | } 61 | //log.Println(resp) 62 | var depth Depth 63 | 64 | //asks, isOK := resp["asks"].([]interface{}) 65 | //if !isOK { 66 | // return nil, errors.New("asks assert error") 67 | //} 68 | _sz := size 69 | for _, v := range resp["asks"].([]interface{}) { 70 | var dr DepthRecord 71 | for i, vv := range v.([]interface{}) { 72 | switch i { 73 | case 0: 74 | dr.Price, _ = strconv.ParseFloat(vv.(string), 64) 75 | case 1: 76 | dr.Amount, _ = strconv.ParseFloat(vv.(string), 64) 77 | } 78 | } 79 | depth.AskList = append(depth.AskList, dr) 80 | _sz-- 81 | if _sz == 0 { 82 | break 83 | } 84 | } 85 | 86 | sort.Sort(sort.Reverse(depth.AskList)) 87 | 88 | _sz = size 89 | for _, v := range resp["bids"].([]interface{}) { 90 | var dr DepthRecord 91 | for i, vv := range v.([]interface{}) { 92 | switch i { 93 | case 0: 94 | dr.Price, _ = strconv.ParseFloat(vv.(string), 64) 95 | case 1: 96 | dr.Amount, _ = strconv.ParseFloat(vv.(string), 64) 97 | } 98 | } 99 | depth.BidList = append(depth.BidList, dr) 100 | 101 | _sz-- 102 | if _sz == 0 { 103 | break 104 | } 105 | } 106 | 107 | return &depth, nil 108 | } 109 | 110 | 111 | func (cc *Coincheck) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 112 | return nil, nil 113 | } 114 | 115 | func (cc *Coincheck) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 116 | return nil, nil 117 | } 118 | 119 | func (cc *Coincheck) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 120 | return nil, nil 121 | } 122 | 123 | func (cc *Coincheck) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 124 | return nil, nil 125 | } 126 | 127 | func (cc *Coincheck) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 128 | return false, nil 129 | } 130 | 131 | func (cc *Coincheck) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 132 | return nil, nil 133 | } 134 | 135 | func (cc *Coincheck) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 136 | return nil, nil 137 | } 138 | 139 | func (cc *Coincheck) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 140 | return nil, nil 141 | } 142 | 143 | func (cc *Coincheck) GetAccount() (*Account, error) { 144 | return nil, nil 145 | } 146 | 147 | func (cc *Coincheck) GetKlineRecords(currency CurrencyPair, period , size, since int) ([]Kline, error) { 148 | return nil, nil 149 | } 150 | 151 | //非个人,整个交易所的交易记录 152 | func (cc *Coincheck) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 153 | return nil, nil 154 | } -------------------------------------------------------------------------------- /coincheck/Coincheck_test.go: -------------------------------------------------------------------------------- 1 | package coincheck 2 | 3 | import ( 4 | . "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | var api = New(http.DefaultClient, "", "") 11 | 12 | func TestCoincheck_GetTicker(t *testing.T) { 13 | ticker, err := api.GetTicker(CurrencyPair{BTC, Currency{"JPY", ""}}) 14 | assert.NoError(t, err) 15 | t.Log(ticker) 16 | } 17 | 18 | func TestCoincheck_GetDepth(t *testing.T) { 19 | depth, err := api.GetDepth(3, CurrencyPair{BTC, NewCurrency("JPY", "")}) 20 | assert.NoError(t, err) 21 | t.Log(depth) 22 | } 23 | -------------------------------------------------------------------------------- /coinex/coinex.go: -------------------------------------------------------------------------------- 1 | package coinex 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "net/url" 10 | "sort" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | . "github.com/wudian/GoEx" 16 | ) 17 | 18 | type CoinEx struct { 19 | httpClient *http.Client 20 | accessKey, 21 | secretKey string 22 | } 23 | 24 | var ( 25 | baseurl = "https://api.coinex.com/v1/" 26 | ) 27 | 28 | func New(client *http.Client, accessKey, secretKey string) *CoinEx { 29 | return &CoinEx{client, accessKey, secretKey} 30 | } 31 | 32 | func (coinex *CoinEx) GetExchangeName() string { 33 | return COINEX 34 | } 35 | 36 | func (coinex *CoinEx) GetTicker(currency CurrencyPair) (*Ticker, error) { 37 | params := url.Values{} 38 | params.Set("market", currency.ToSymbol("")) 39 | datamap, err := coinex.doRequest("GET", "market/ticker", ¶ms) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | tickermap := datamap["ticker"].(map[string]interface{}) 45 | 46 | return &Ticker{ 47 | Date: ToUint64(datamap["date"]) / 1000, 48 | Last: ToFloat64(tickermap["last"]), 49 | Buy: ToFloat64(tickermap["buy"]), 50 | Sell: ToFloat64(tickermap["sell"]), 51 | High: ToFloat64(tickermap["high"]), 52 | Low: ToFloat64(tickermap["low"]), 53 | Vol: ToFloat64(tickermap["vol"])}, nil 54 | } 55 | 56 | func (coinex *CoinEx) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 57 | params := url.Values{} 58 | params.Set("market", currency.ToSymbol("")) 59 | params.Set("merge", "0.00000001") 60 | params.Set("limit", fmt.Sprint(size)) 61 | 62 | datamap, err := coinex.doRequest("GET", "market/depth", ¶ms) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | dep := Depth{} 68 | dep.AskList = make([]DepthRecord, 0, size) 69 | dep.BidList = make([]DepthRecord, 0, size) 70 | 71 | asks := datamap["asks"].([]interface{}) 72 | bids := datamap["bids"].([]interface{}) 73 | 74 | for _, v := range asks { 75 | r := v.([]interface{}) 76 | dep.AskList = append(dep.AskList, DepthRecord{ToFloat64(r[0]), ToFloat64(r[1])}) 77 | } 78 | 79 | for _, v := range bids { 80 | r := v.([]interface{}) 81 | dep.BidList = append(dep.BidList, DepthRecord{ToFloat64(r[0]), ToFloat64(r[1])}) 82 | } 83 | 84 | sort.Sort(sort.Reverse(dep.AskList)) 85 | 86 | return &dep, nil 87 | } 88 | 89 | func (coinex *CoinEx) placeLimitOrder(side, amount, price string, pair CurrencyPair) (*Order, error) { 90 | params := url.Values{} 91 | params.Set("market", pair.ToSymbol("")) 92 | params.Set("type", side) 93 | params.Set("amount", amount) 94 | params.Set("price", price) 95 | 96 | retmap, err := coinex.doRequest("POST", "order/limit", ¶ms) 97 | if err != nil { 98 | return nil, err 99 | } 100 | 101 | order := coinex.adaptOrder(retmap, pair) 102 | 103 | return &order, nil 104 | } 105 | 106 | func (coinex *CoinEx) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 107 | return coinex.placeLimitOrder("buy", amount, price, currency) 108 | } 109 | 110 | func (coinex *CoinEx) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 111 | return coinex.placeLimitOrder("sell", amount, price, currency) 112 | } 113 | 114 | func (coinex *CoinEx) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 115 | panic("not implement") 116 | } 117 | func (coinex *CoinEx) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 118 | panic("not implement") 119 | } 120 | 121 | func (coinex *CoinEx) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 122 | params := url.Values{} 123 | params.Set("id", orderId) 124 | params.Set("market", currency.ToSymbol("")) 125 | _, err := coinex.doRequest("DELETE", "order/pending", ¶ms) 126 | if err != nil { 127 | return false, err 128 | } 129 | // log.Println(retmap) 130 | return true, nil 131 | } 132 | 133 | func (coinex *CoinEx) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 134 | params := url.Values{} 135 | params.Set("id", orderId) 136 | params.Set("market", currency.ToSymbol("")) 137 | retmap, err := coinex.doRequest("GET", "order", ¶ms) 138 | if err != nil { 139 | if "Order not found" == err.Error() { 140 | return nil, EX_ERR_NOT_FIND_ORDER 141 | } 142 | return nil, err 143 | } 144 | order := coinex.adaptOrder(retmap, currency) 145 | return &order, nil 146 | } 147 | 148 | func (coinex *CoinEx) GetPendingOrders(page, limit int, pair CurrencyPair) ([]Order, error) { 149 | params := url.Values{} 150 | params.Set("page", fmt.Sprint(page)) 151 | params.Set("limit", fmt.Sprint(limit)) 152 | params.Set("market", pair.ToSymbol("")) 153 | 154 | retmap, err := coinex.doRequest("GET", "order/pending", ¶ms) 155 | if err != nil { 156 | return nil, err 157 | } 158 | 159 | //log.Println(retmap) 160 | 161 | datamap, isok := retmap["data"].([]interface{}) 162 | if !isok { 163 | log.Println(datamap) 164 | return nil, errors.New("response format error") 165 | } 166 | 167 | var orders []Order 168 | for _, v := range datamap { 169 | vv := v.(map[string]interface{}) 170 | orders = append(orders, coinex.adaptOrder(vv, pair)) 171 | } 172 | 173 | return orders, nil 174 | 175 | } 176 | 177 | func (coinex *CoinEx) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 178 | return coinex.GetPendingOrders(1, 100, currency) 179 | } 180 | 181 | func (coinex *CoinEx) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 182 | panic("not implement") 183 | } 184 | 185 | type coinexDifficulty struct { 186 | Code int `json:"code"` 187 | Data struct { 188 | Difficulty string `json:"difficulty"` 189 | Prediction string `json:"prediction"` 190 | UpdateTime int `json:"update_time"` 191 | } `json:"data"` 192 | Message string `json:"message"` 193 | } 194 | 195 | func (coinex *CoinEx) GetDifficulty() (limit, cur float64, err error) { 196 | buf, err := coinex.doRequestInner("GET", "order/mining/difficulty", &url.Values{}) 197 | if nil != err { 198 | log.Printf("GetDifficulty - http.NewRequest failed : %v", err) 199 | return 0.0, 0.0, err 200 | } 201 | 202 | var diff coinexDifficulty 203 | if err = json.Unmarshal(buf, &diff); nil != err { 204 | log.Printf("GetDifficulty - json.Unmarshal failed : %v", err) 205 | return 0.0, 0.0, err 206 | } 207 | limit, err = strconv.ParseFloat(diff.Data.Difficulty, 64) 208 | if nil != err { 209 | log.Printf("GetDifficulty - strconv.ParseFloat failed : %v", err) 210 | return 0.0, 0.0, err 211 | } 212 | cur, err = strconv.ParseFloat(diff.Data.Prediction, 64) 213 | if nil != err { 214 | log.Printf("GetDifficulty - strconv.ParseFloat failed : %v", err) 215 | return 0.0, 0.0, err 216 | } 217 | 218 | return limit, cur, nil 219 | } 220 | 221 | func (coinex *CoinEx) GetAccount() (*Account, error) { 222 | datamap, err := coinex.doRequest("GET", "balance", &url.Values{}) 223 | if err != nil { 224 | return nil, err 225 | } 226 | //log.Println(datamap) 227 | acc := new(Account) 228 | acc.SubAccounts = make(map[Currency]SubAccount, 2) 229 | acc.Exchange = coinex.GetExchangeName() 230 | for c, v := range datamap { 231 | vv := v.(map[string]interface{}) 232 | currency := NewCurrency(c, "") 233 | acc.SubAccounts[currency] = SubAccount{ 234 | Currency: currency, 235 | Amount: ToFloat64(vv["available"]), 236 | ForzenAmount: ToFloat64(vv["frozen"])} 237 | } 238 | return acc, nil 239 | } 240 | 241 | func (coinex *CoinEx) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 242 | panic("not implement") 243 | } 244 | 245 | //非个人,整个交易所的交易记录 246 | func (coinex *CoinEx) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 247 | panic("not implement") 248 | } 249 | 250 | func (coinex *CoinEx) doRequestInner(method, uri string, params *url.Values) (buf []byte, err error) { 251 | reqUrl := baseurl + uri 252 | 253 | headermap := map[string]string{ 254 | "Content-Type": "application/json; charset=utf-8"} 255 | 256 | if !strings.HasPrefix(uri, "market") { 257 | params.Set("access_id", coinex.accessKey) 258 | params.Set("tonce", fmt.Sprint(time.Now().UnixNano()/int64(time.Millisecond))) 259 | // println(params.Encode() + "&secret_key=" + coinex.secretKey) 260 | sign, _ := GetParamMD5Sign("", params.Encode()+"&secret_key="+coinex.secretKey) 261 | headermap["authorization"] = strings.ToUpper(sign) 262 | } 263 | 264 | if ("GET" == method || "DELETE" == method) && len(params.Encode()) > 0 { 265 | reqUrl += "?" + params.Encode() 266 | } 267 | 268 | var paramStr string = "" 269 | if "POST" == method { 270 | //to json 271 | paramStr = params.Encode() 272 | var parammap map[string]string = make(map[string]string, 2) 273 | for _, v := range strings.Split(paramStr, "&") { 274 | vv := strings.Split(v, "=") 275 | parammap[vv[0]] = vv[1] 276 | } 277 | jsonData, _ := json.Marshal(parammap) 278 | paramStr = string(jsonData) 279 | } 280 | 281 | return NewHttpRequest(coinex.httpClient, method, reqUrl, paramStr, headermap) 282 | } 283 | 284 | func (coinex *CoinEx) doRequest(method, uri string, params *url.Values) (map[string]interface{}, error) { 285 | resp, err := coinex.doRequestInner(method, uri, params) 286 | 287 | if err != nil { 288 | return nil, err 289 | } 290 | 291 | retmap := make(map[string]interface{}, 1) 292 | err = json.Unmarshal(resp, &retmap) 293 | if err != nil { 294 | return nil, err 295 | } 296 | 297 | if ToInt(retmap["code"]) != 0 { 298 | return nil, errors.New(retmap["message"].(string)) 299 | } 300 | 301 | // log.Println(retmap) 302 | datamap := retmap["data"].(map[string]interface{}) 303 | 304 | return datamap, nil 305 | } 306 | 307 | func (coinex *CoinEx) adaptTradeSide(side string) TradeSide { 308 | switch side { 309 | case "sell": 310 | return SELL 311 | case "buy": 312 | return BUY 313 | } 314 | return BUY 315 | } 316 | 317 | func (coinex *CoinEx) adaptTradeStatus(status string) TradeStatus { 318 | var tradeStatus TradeStatus = ORDER_UNFINISH 319 | switch status { 320 | case "not_deal": 321 | tradeStatus = ORDER_UNFINISH 322 | case "done": 323 | tradeStatus = ORDER_FINISH 324 | case "partly": 325 | tradeStatus = ORDER_PART_FINISH 326 | case "cancel": 327 | tradeStatus = ORDER_CANCEL 328 | } 329 | return tradeStatus 330 | } 331 | 332 | func (coinex *CoinEx) adaptOrder(ordermap map[string]interface{}, pair CurrencyPair) Order { 333 | return Order{ 334 | Currency: pair, 335 | OrderID: ToInt(ordermap["id"]), 336 | OrderID2: fmt.Sprint(ToInt(ordermap["id"])), 337 | Amount: ToFloat64(ordermap["amount"]), 338 | Price: ToFloat64(ordermap["price"]), 339 | DealAmount: ToFloat64(ordermap["deal_amount"]), 340 | AvgPrice: ToFloat64(ordermap["avg_price"]), 341 | Status: coinex.adaptTradeStatus(ordermap["status"].(string)), 342 | Side: coinex.adaptTradeSide(ordermap["type"].(string)), 343 | Fee: ToFloat64(ordermap["deal_fee"]), 344 | OrderTime: ToInt(ordermap["create_time"])} 345 | } 346 | -------------------------------------------------------------------------------- /coinex/coinex_test.go: -------------------------------------------------------------------------------- 1 | package coinex 2 | 3 | import ( 4 | "fmt" 5 | "github.com/wudian/GoEx" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | var coinex = New(http.DefaultClient, "", "") 11 | 12 | func TestCoinEx_GetTicker(t *testing.T) { 13 | ticker, err := coinex.GetTicker(goex.LTC_BTC) 14 | t.Log(err) 15 | t.Log(ticker) 16 | } 17 | 18 | func TestCoinEx_GetDepth(t *testing.T) { 19 | dep, err := coinex.GetDepth(5, goex.LTC_BTC) 20 | t.Log(err) 21 | t.Log(dep.AskList) 22 | t.Log(dep.BidList) 23 | } 24 | 25 | func TestCoinEx_GetAccount(t *testing.T) { 26 | //os.Setenv("https_proxy", "http://120.27.230.57:30000") 27 | acc, err := coinex.GetAccount() 28 | t.Log(err) 29 | t.Log(acc) 30 | } 31 | 32 | func TestCoinEx_LimitBuy(t *testing.T) { 33 | 34 | } 35 | 36 | func TestCoinEx_LimitSell(t *testing.T) { 37 | ord, err := coinex.LimitSell("100", "0.0000601", goex.NewCurrencyPair2("CET_BCH")) 38 | t.Log(err) 39 | t.Log(ord) 40 | } 41 | 42 | func TestCoinEx_GetUnfinishOrders(t *testing.T) { 43 | ords, err := coinex.GetUnfinishOrders(goex.NewCurrencyPair2("CET_BCH")) 44 | t.Log(err) 45 | t.Log(fmt.Sprint(ords[0].OrderID)) 46 | } 47 | 48 | func TestCoinEx_CancelOrder(t *testing.T) { 49 | r, err := coinex.CancelOrder("37504128", goex.NewCurrencyPair2("CET_BCH")) 50 | t.Log(r, err) 51 | } 52 | 53 | func TestCoinEx_GetOneOrder(t *testing.T) { 54 | ord, err := coinex.GetOneOrder("37504128", goex.NewCurrencyPair2("CET_BCH")) 55 | t.Log(err) 56 | t.Log(ord) 57 | } 58 | -------------------------------------------------------------------------------- /coinpark/coinpark_test.go: -------------------------------------------------------------------------------- 1 | package coinpark 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var cpk = New(http.DefaultClient, "", "") 10 | 11 | func TestCpk_buildSigned(t *testing.T) { 12 | //[{"cmd":"user/userInfo","body":{}}] 13 | return 14 | cmds := "[{\"cmd\":\"user/userInfo\",\"body\":{}}]" 15 | t.Log(cpk.buildSigned(cmds)) 16 | } 17 | func TestCpk_GetAccount(t *testing.T) { 18 | //return 19 | t.Log(cpk.GetAccount()) 20 | } 21 | 22 | func TestCpk_LimitBuy(t *testing.T) { 23 | return 24 | t.Log(cpk.LimitBuy("1", "1", goex.BTC_USDT)) 25 | } 26 | func TestCpk_LimitSell(t *testing.T) { 27 | return 28 | t.Log(cpk.LimitSell("1", "999999", goex.BTC_USDT)) 29 | } 30 | 31 | func TestCpk_CancelOrder(t *testing.T) { 32 | return 33 | t.Log(cpk.CancelOrder("123", goex.BTC_USDT)) 34 | } 35 | 36 | func TestCpk_GetPairList(t *testing.T) { 37 | return 38 | t.Log(cpk.GetPairList()) 39 | } 40 | -------------------------------------------------------------------------------- /cryptopia/Cryptopia_test.go: -------------------------------------------------------------------------------- 1 | package cryptopia 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var ctp = New(http.DefaultClient, "", "") 10 | 11 | func TestCryptopia_GetTicker(t *testing.T) { 12 | ticker, err := ctp.GetTicker(goex.BTC_USDT) 13 | t.Log("err=>", err) 14 | t.Log("ticker=>", ticker) 15 | } 16 | 17 | func TestCryptopia_GetDepth(t *testing.T) { 18 | depCtp, err := ctp.GetDepth(goex.BTC_USDT) 19 | t.Log(err) 20 | t.Log("AskList=>", depCtp.AskList) 21 | t.Log("BidList=>", depCtp.BidList) 22 | } 23 | -------------------------------------------------------------------------------- /cryptopia/README.md: -------------------------------------------------------------------------------- 1 | # Reference to Cryptopia API documentation: 2 | https://support.cryptopia.co.nz/csm?id=kb_article&sys_id=a75703dcdbb9130084ed147a3a9619bc -------------------------------------------------------------------------------- /exx/exx_test.go: -------------------------------------------------------------------------------- 1 | package exx 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "net/url" 7 | "testing" 8 | ) 9 | 10 | 11 | var ( 12 | api_key = "yourAccessKey" 13 | api_secretkey = "yourSecretKey" 14 | exx = New(http.DefaultClient, api_key, api_secretkey) 15 | ) 16 | 17 | func TestExx_Signed(t *testing.T) { 18 | return 19 | params := url.Values{} 20 | exx.accessKey = "yourAccessKey" 21 | exx.secretKey = "yourSecretKey" 22 | exx.buildPostForm(¶ms) 23 | t.Log(params) 24 | } 25 | 26 | func TestExx_GetAccount(t *testing.T) { 27 | //return 28 | acc, err := exx.GetAccount() 29 | t.Log(acc, err) 30 | //t.Log(acc.SubAccounts[goex.BTC]) 31 | } 32 | 33 | func TestExx_GetTicker(t *testing.T) { 34 | return 35 | ticker, err := exx.GetTicker(goex.BTC_USD) 36 | t.Log(ticker, err) 37 | } 38 | 39 | func TestExx_GetDepth(t *testing.T) { 40 | return 41 | dep, _ := exx.GetDepth(2, goex.BTC_USDT) 42 | t.Log(dep) 43 | t.Log(dep.AskList[0]) 44 | t.Log(dep.BidList[0]) 45 | } 46 | 47 | func TestExx_LimitSell(t *testing.T) { 48 | return 49 | ord, err := exx.LimitSell("0.001", "75000", goex.NewCurrencyPair2("BTC_QC")) 50 | t.Log(err) 51 | t.Log(ord) 52 | } 53 | 54 | func TestExx_LimitBuy(t *testing.T) { 55 | return 56 | ord, err := exx.LimitBuy("2", "4", goex.NewCurrencyPair2("1ST_QC")) 57 | t.Log(err) 58 | t.Log(ord) 59 | } 60 | 61 | func TestExx_CancelOrder(t *testing.T) { 62 | return 63 | r, err := exx.CancelOrder("201802014255365", goex.NewCurrencyPair2("BTC_QC")) 64 | t.Log(err) 65 | t.Log(r) 66 | } 67 | 68 | func TestExx_GetUnfinishOrders(t *testing.T) { 69 | return 70 | ords, err := exx.GetUnfinishOrders(goex.NewCurrencyPair2("1ST_QC")) 71 | t.Log(err) 72 | t.Log(ords) 73 | } 74 | 75 | func TestExx_GetOneOrder(t *testing.T) { 76 | return 77 | ord, err := exx.GetOneOrder("20180201341043", goex.NewCurrencyPair2("1ST_QC")) 78 | t.Log(err) 79 | t.Log(ord) 80 | } 81 | -------------------------------------------------------------------------------- /fcoin/fcoin_test.go: -------------------------------------------------------------------------------- 1 | package fcoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var ft = NewFCoin(http.DefaultClient, "", "") 10 | 11 | func TestFCoin_GetTicker(t *testing.T) { 12 | t.Log(ft.GetTicker(goex.NewCurrencyPair2("BNB_USDT"))) 13 | } 14 | 15 | func TestFCoin_GetDepth(t *testing.T) { 16 | dep, _ := ft.GetDepth(1, goex.BTC_USDT) 17 | t.Log(dep.AskList) 18 | t.Log(dep.BidList) 19 | } 20 | 21 | func TestFCoin_GetAccount(t *testing.T) { 22 | acc, _ := ft.GetAccount() 23 | t.Log(acc) 24 | } 25 | 26 | func TestFCoin_LimitBuy(t *testing.T) { 27 | t.Log(ft.LimitBuy("0.01", "100", goex.ETC_USD)) 28 | } 29 | 30 | func TestFCoin_LimitSell(t *testing.T) { 31 | t.Log(ft.LimitSell("0.01", "50", goex.ETC_USD)) 32 | } 33 | 34 | func TestFCoin_GetOneOrder(t *testing.T) { 35 | t.Log(ft.GetOneOrder("KRcowt_w79qxcBdooYb-RxtZ_67TFcme7eUXU8bMusg=", goex.ETC_USDT)) 36 | } 37 | 38 | func TestFCoin_CancelOrder(t *testing.T) { 39 | t.Log(ft.CancelOrder("-MR0CItwW-rpSFJau7bfCyUBrw9nrkLNipV9odvPlRQ=", goex.ETC_USDT)) 40 | } 41 | 42 | func TestFCoin_GetUnfinishOrders(t *testing.T) { 43 | t.Log(ft.GetUnfinishOrders(goex.ETC_USDT)) 44 | } 45 | -------------------------------------------------------------------------------- /gateio/gateio.go: -------------------------------------------------------------------------------- 1 | package gateio 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/wudian/GoEx" 6 | "net/http" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | marketBaseUrl = "http://data.gate.io/api2/1" 13 | ) 14 | 15 | type Gate struct { 16 | client *http.Client 17 | accesskey, 18 | secretkey string 19 | } 20 | 21 | func New(client *http.Client, accesskey, secretkey string) *Gate { 22 | return &Gate{client: client, accesskey: accesskey, secretkey: secretkey} 23 | } 24 | 25 | func (g *Gate) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 26 | panic("not implement") 27 | } 28 | func (g *Gate) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 29 | panic("not implement") 30 | } 31 | func (g *Gate) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 32 | panic("not implement") 33 | } 34 | func (g *Gate) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 35 | panic("not implement") 36 | } 37 | func (g *Gate) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 38 | panic("not implement") 39 | } 40 | func (g *Gate) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 41 | panic("not implement") 42 | } 43 | func (g *Gate) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 44 | panic("not implement") 45 | } 46 | func (g *Gate) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 47 | panic("not implement") 48 | } 49 | func (g *Gate) GetAccount() (*Account, error) { 50 | panic("not implement") 51 | } 52 | 53 | func (g *Gate) GetTicker(currency CurrencyPair) (*Ticker, error) { 54 | uri := fmt.Sprintf("%s/ticker/%s", marketBaseUrl, strings.ToLower(currency.ToSymbol("_"))) 55 | 56 | resp, err := HttpGet(g.client, uri) 57 | if err != nil { 58 | errCode := HTTP_ERR_CODE 59 | errCode.OriginErrMsg = err.Error() 60 | return nil, errCode 61 | } 62 | 63 | return &Ticker{ 64 | Last: ToFloat64(resp["last"]), 65 | Sell: ToFloat64(resp["lowestAsk"]), 66 | Buy: ToFloat64(resp["highestBid"]), 67 | High: ToFloat64(resp["high24hr"]), 68 | Low: ToFloat64(resp["low24hr"]), 69 | Vol: ToFloat64(resp["quoteVolume"]), 70 | }, nil 71 | } 72 | 73 | func (g *Gate) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 74 | resp, err := HttpGet(g.client, fmt.Sprintf("%s/orderBook/%s", marketBaseUrl, currency.ToSymbol("_"))) 75 | if err != nil { 76 | errCode := HTTP_ERR_CODE 77 | errCode.OriginErrMsg = err.Error() 78 | return nil, errCode 79 | } 80 | 81 | bids, _ := resp["bids"].([]interface{}) 82 | asks, _ := resp["asks"].([]interface{}) 83 | 84 | dep := new(Depth) 85 | 86 | for _, v := range bids { 87 | r := v.([]interface{}) 88 | dep.BidList = append(dep.BidList, DepthRecord{ToFloat64(r[0]), ToFloat64(r[1])}) 89 | } 90 | 91 | for _, v := range asks { 92 | r := v.([]interface{}) 93 | dep.AskList = append(dep.AskList, DepthRecord{ToFloat64(r[0]), ToFloat64(r[1])}) 94 | } 95 | 96 | sort.Sort(sort.Reverse(dep.AskList)) 97 | 98 | return dep, nil 99 | } 100 | 101 | func (g *Gate) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 102 | panic("not implement") 103 | } 104 | 105 | //非个人,整个交易所的交易记录 106 | func (g *Gate) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 107 | panic("not implement") 108 | } 109 | 110 | func (g *Gate) GetExchangeName() string { 111 | return GATEIO 112 | } 113 | -------------------------------------------------------------------------------- /gateio/gateio_test.go: -------------------------------------------------------------------------------- 1 | package gateio 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var gate = New(http.DefaultClient, "", "") 10 | 11 | func TestGate_GetTicker(t *testing.T) { 12 | ticker, err := gate.GetTicker(goex.BTC_USDT) 13 | t.Log("err=>", err) 14 | t.Log("ticker=>", ticker) 15 | } 16 | 17 | func TestGate_GetDepth(t *testing.T) { 18 | dep, err := gate.GetDepth(1, goex.BTC_USDT) 19 | 20 | t.Log("err=>", err) 21 | t.Log("asks=>", dep.AskList) 22 | t.Log("bids=>", dep.BidList) 23 | } 24 | -------------------------------------------------------------------------------- /gdax/gdax.go: -------------------------------------------------------------------------------- 1 | package gdax 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/wudian/GoEx" 6 | "net/http" 7 | "sort" 8 | ) 9 | 10 | //www.coinbase.com or www.gdax.com 11 | 12 | type Gdax struct { 13 | httpClient *http.Client 14 | baseUrl, 15 | accessKey, 16 | secretKey string 17 | } 18 | 19 | func New(client *http.Client, accesskey, secretkey string) *Gdax { 20 | return &Gdax{client, "https://api.gdax.com", accesskey, secretkey} 21 | } 22 | 23 | func (g *Gdax) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 24 | panic("not implement") 25 | } 26 | func (g *Gdax) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 27 | panic("not implement") 28 | } 29 | func (g *Gdax) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 30 | panic("not implement") 31 | } 32 | func (g *Gdax) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 33 | panic("not implement") 34 | } 35 | func (g *Gdax) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 36 | panic("not implement") 37 | } 38 | func (g *Gdax) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 39 | panic("not implement") 40 | } 41 | func (g *Gdax) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 42 | panic("not implement") 43 | } 44 | func (g *Gdax) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 45 | panic("not implement") 46 | } 47 | func (g *Gdax) GetAccount() (*Account, error) { 48 | panic("not implement") 49 | } 50 | 51 | func (g *Gdax) GetTicker(currency CurrencyPair) (*Ticker, error) { 52 | resp, err := HttpGet(g.httpClient, fmt.Sprintf("%s/products/%s/ticker", g.baseUrl, currency.ToSymbol("-"))) 53 | if err != nil { 54 | errCode := HTTP_ERR_CODE 55 | errCode.OriginErrMsg = err.Error() 56 | return nil, errCode 57 | } 58 | 59 | return &Ticker{ 60 | Last: ToFloat64(resp["price"]), 61 | Sell: ToFloat64(resp["ask"]), 62 | Buy: ToFloat64(resp["bid"]), 63 | Vol: ToFloat64(resp["volume"]), 64 | }, nil 65 | } 66 | 67 | func (g *Gdax) Get24HStats(pair CurrencyPair) (*Ticker, error) { 68 | resp, err := HttpGet(g.httpClient, fmt.Sprintf("%s/products/%s/stats", g.baseUrl, pair.ToSymbol("-"))) 69 | if err != nil { 70 | errCode := HTTP_ERR_CODE 71 | errCode.OriginErrMsg = err.Error() 72 | return nil, errCode 73 | } 74 | return &Ticker{ 75 | High: ToFloat64(resp["high"]), 76 | Low: ToFloat64(resp["low"]), 77 | Vol: ToFloat64(resp["volmue"]), 78 | Last: ToFloat64(resp["last"]), 79 | }, nil 80 | } 81 | 82 | func (g *Gdax) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 83 | var level int = 2 84 | if size == 1 { 85 | level = 1 86 | } 87 | 88 | resp, err := HttpGet(g.httpClient, fmt.Sprintf("%s/products/%s/book?level=%d", g.baseUrl, currency.ToSymbol("-"), level)) 89 | if err != nil { 90 | errCode := HTTP_ERR_CODE 91 | errCode.OriginErrMsg = err.Error() 92 | return nil, errCode 93 | } 94 | 95 | bids, _ := resp["bids"].([]interface{}) 96 | asks, _ := resp["asks"].([]interface{}) 97 | 98 | dep := new(Depth) 99 | 100 | for _, v := range bids { 101 | r := v.([]interface{}) 102 | dep.BidList = append(dep.BidList, DepthRecord{ToFloat64(r[0]), ToFloat64(r[1])}) 103 | } 104 | 105 | for _, v := range asks { 106 | r := v.([]interface{}) 107 | dep.AskList = append(dep.AskList, DepthRecord{ToFloat64(r[0]), ToFloat64(r[1])}) 108 | } 109 | 110 | sort.Sort(sort.Reverse(dep.AskList)) 111 | 112 | return dep, nil 113 | } 114 | 115 | func (g *Gdax) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 116 | panic("not implement") 117 | } 118 | 119 | //非个人,整个交易所的交易记录 120 | func (g *Gdax) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 121 | panic("not implement") 122 | } 123 | 124 | func (g *Gdax) GetExchangeName() string { 125 | return GDAX 126 | } 127 | -------------------------------------------------------------------------------- /gdax/gdax_test.go: -------------------------------------------------------------------------------- 1 | package gdax 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var gdax = New(http.DefaultClient, "", "") 10 | 11 | func TestGdax_GetTicker(t *testing.T) { 12 | ticker, err := gdax.GetTicker(goex.BTC_USD) 13 | t.Log("err=>", err) 14 | t.Log("ticker=>", ticker) 15 | } 16 | 17 | func TestGdax_Get24HStats(t *testing.T) { 18 | stats, err := gdax.Get24HStats(goex.BTC_USD) 19 | t.Log("err=>", err) 20 | t.Log("stats=>", stats) 21 | } 22 | 23 | func TestGdax_GetDepth(t *testing.T) { 24 | dep, err := gdax.GetDepth(2, goex.BTC_USD) 25 | t.Log("err=>", err) 26 | t.Log("bids=>", dep.BidList) 27 | t.Log("asks=>", dep.AskList) 28 | } 29 | -------------------------------------------------------------------------------- /hashkey/Hashkey_test.go: -------------------------------------------------------------------------------- 1 | package hashkey 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/require" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | const ( 11 | PubKey = "" 12 | SecretKey = "" 13 | apiKeyHMAC = "MTU0NDUwODQ0NjI0NTAwMDAwMDAwNTQ=" 14 | secretKey = "vprggEasLOksdmut6WcFvuv4oUuAbewdkGJY1fgAvBw=" 15 | ) 16 | 17 | var htb *Hashkey 18 | 19 | func init() { 20 | htb = New(http.DefaultClient, PubKey, SecretKey) 21 | } 22 | 23 | func TestHitbtc_GetSymbols(t *testing.T) { 24 | return 25 | t.Log(htb.GetSymbols()) 26 | } 27 | 28 | func TestHitbtc_adaptSymbolToCurrencyPair(t *testing.T) { 29 | return 30 | t.Log(htb.adaptSymbolToCurrencyPair("DOGEBTC").String() == "DOGE_BTC") 31 | t.Log(htb.adaptSymbolToCurrencyPair("BTCGUSD").String() == "BTC_GUSD") 32 | t.Log(htb.adaptSymbolToCurrencyPair("btctusd").String() == "BTC_TUSD") 33 | t.Log(htb.adaptSymbolToCurrencyPair("BTCUSDC").String() == "BTC_USDC") 34 | t.Log(htb.adaptSymbolToCurrencyPair("ETHEOS").String() == "ETH_EOS") 35 | } 36 | 37 | func TestGetTicker(t *testing.T) { 38 | res, err := htb.GetTicker(goex.BTC_USDT) 39 | require := require.New(t) 40 | require.Nil(err) 41 | t.Log(res) 42 | } 43 | 44 | func TestGetAccount(t *testing.T) { 45 | return 46 | res, err := htb.GetAccount() 47 | require := require.New(t) 48 | require.Nil(err) 49 | t.Log(res) 50 | } 51 | 52 | func TestDepth(t *testing.T) { 53 | res, err := htb.GetDepth(10, goex.BTC_USDT) 54 | require := require.New(t) 55 | require.Nil(err) 56 | t.Log(res) 57 | } 58 | 59 | func TestKline(t *testing.T) { 60 | return 61 | res, err := htb.GetKline(YCC_BTC, "1M", 10, 0) 62 | require := require.New(t) 63 | require.Nil(err) 64 | t.Log(res) 65 | } 66 | 67 | func TestTrades(t *testing.T) { 68 | return 69 | res, err := htb.GetTrades(YCC_BTC, 1519862400) 70 | require := require.New(t) 71 | require.Nil(err) 72 | t.Log(res) 73 | } 74 | 75 | func TestPlaceOrder(t *testing.T) { 76 | return 77 | res, err := htb.LimitBuy("15", "0.000008", YCC_BTC) 78 | require := require.New(t) 79 | require.Nil(err) 80 | t.Log(res) 81 | } 82 | 83 | func testCancelOrder(t *testing.T) { 84 | res, err := htb.CancelOrder("a605f2abbcc750da9138687bb27a2835", YCC_BTC) 85 | require := require.New(t) 86 | require.Nil(err) 87 | t.Log(res) 88 | } 89 | 90 | func testGetOneOrder(t *testing.T) { 91 | return 92 | res, err := htb.GetOneOrder("177836e71c8d57a14648d465e893efce", YCC_BTC) 93 | require := require.New(t) 94 | require.Nil(err) 95 | t.Log(res) 96 | } 97 | 98 | func TestGetOrders(t *testing.T) { 99 | return 100 | res, err := htb.GetOrderHistorys(YCC_BTC, 1, 10) 101 | require := require.New(t) 102 | require.Nil(err) 103 | t.Log(res) 104 | } 105 | 106 | func TestGetUnfinishOrders(t *testing.T) { 107 | return 108 | res, err := htb.GetUnfinishOrders(YCC_BTC) 109 | require := require.New(t) 110 | require.Nil(err) 111 | t.Log(res) 112 | } 113 | -------------------------------------------------------------------------------- /hitbtc/Hitbtc_test.go: -------------------------------------------------------------------------------- 1 | package hitbtc 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/require" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | const ( 11 | PubKey = "" 12 | SecretKey = "" 13 | ) 14 | 15 | var htb *Hitbtc 16 | 17 | func init() { 18 | htb = New(http.DefaultClient, PubKey, SecretKey) 19 | } 20 | 21 | func TestHitbtc_GetSymbols(t *testing.T) { 22 | return 23 | t.Log(htb.GetSymbols()) 24 | } 25 | 26 | func TestHitbtc_adaptSymbolToCurrencyPair(t *testing.T) { 27 | return 28 | t.Log(htb.adaptSymbolToCurrencyPair("DOGEBTC").String() == "DOGE_BTC") 29 | t.Log(htb.adaptSymbolToCurrencyPair("BTCGUSD").String() == "BTC_GUSD") 30 | t.Log(htb.adaptSymbolToCurrencyPair("btctusd").String() == "BTC_TUSD") 31 | t.Log(htb.adaptSymbolToCurrencyPair("BTCUSDC").String() == "BTC_USDC") 32 | t.Log(htb.adaptSymbolToCurrencyPair("ETHEOS").String() == "ETH_EOS") 33 | } 34 | 35 | func TestGetTicker(t *testing.T) { 36 | res, err := htb.GetTicker(goex.BTC_USDT) 37 | require := require.New(t) 38 | require.Nil(err) 39 | t.Log(res) 40 | } 41 | 42 | func TestGetAccount(t *testing.T) { 43 | return 44 | res, err := htb.GetAccount() 45 | require := require.New(t) 46 | require.Nil(err) 47 | t.Log(res) 48 | } 49 | 50 | func TestDepth(t *testing.T) { 51 | res, err := htb.GetDepth(10, goex.BTC_USDT) 52 | require := require.New(t) 53 | require.Nil(err) 54 | t.Log(res) 55 | } 56 | 57 | func TestKline(t *testing.T) { 58 | return 59 | res, err := htb.GetKline(YCC_BTC, "1M", 10, 0) 60 | require := require.New(t) 61 | require.Nil(err) 62 | t.Log(res) 63 | } 64 | 65 | func TestTrades(t *testing.T) { 66 | return 67 | res, err := htb.GetTrades(YCC_BTC, 1519862400) 68 | require := require.New(t) 69 | require.Nil(err) 70 | t.Log(res) 71 | } 72 | 73 | func TestPlaceOrder(t *testing.T) { 74 | return 75 | res, err := htb.LimitBuy("15", "0.000008", YCC_BTC) 76 | require := require.New(t) 77 | require.Nil(err) 78 | t.Log(res) 79 | } 80 | 81 | func testCancelOrder(t *testing.T) { 82 | res, err := htb.CancelOrder("a605f2abbcc750da9138687bb27a2835", YCC_BTC) 83 | require := require.New(t) 84 | require.Nil(err) 85 | t.Log(res) 86 | } 87 | 88 | func testGetOneOrder(t *testing.T) { 89 | return 90 | res, err := htb.GetOneOrder("177836e71c8d57a14648d465e893efce", YCC_BTC) 91 | require := require.New(t) 92 | require.Nil(err) 93 | t.Log(res) 94 | } 95 | 96 | func TestGetOrders(t *testing.T) { 97 | return 98 | res, err := htb.GetOrderHistorys(YCC_BTC, 1, 10) 99 | require := require.New(t) 100 | require.Nil(err) 101 | t.Log(res) 102 | } 103 | 104 | func TestGetUnfinishOrders(t *testing.T) { 105 | return 106 | res, err := htb.GetUnfinishOrders(YCC_BTC) 107 | require := require.New(t) 108 | require.Nil(err) 109 | t.Log(res) 110 | } 111 | -------------------------------------------------------------------------------- /huobi/Hbdm_test.go: -------------------------------------------------------------------------------- 1 | package huobi 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | var dm = NewHbdm(&goex.APIConfig{ 10 | Endpoint: "https://api.hbdm.com", 11 | HttpClient: httpProxyClient, 12 | ApiKey: "", 13 | ApiSecretKey: ""}) 14 | 15 | func TestHbdm_GetFutureUserinfo(t *testing.T) { 16 | t.Log(dm.GetFutureUserinfo()) 17 | } 18 | 19 | func TestHbdm_GetFuturePosition(t *testing.T) { 20 | t.Log(dm.GetFuturePosition(goex.BTC_USD, goex.QUARTER_CONTRACT)) 21 | } 22 | 23 | func TestHbdm_PlaceFutureOrder(t *testing.T) { 24 | t.Log(dm.PlaceFutureOrder(goex.BTC_USD, goex.QUARTER_CONTRACT, "3800", "1", goex.OPEN_BUY, 0, 20)) 25 | } 26 | 27 | func TestHbdm_FutureCancelOrder(t *testing.T) { 28 | t.Log(dm.FutureCancelOrder(goex.BTC_USD, goex.QUARTER_CONTRACT, "6")) 29 | } 30 | 31 | func TestHbdm_GetUnfinishFutureOrders(t *testing.T) { 32 | t.Log(dm.GetUnfinishFutureOrders(goex.BTC_USD, goex.QUARTER_CONTRACT)) 33 | } 34 | 35 | func TestHbdm_GetFutureOrders(t *testing.T) { 36 | t.Log(dm.GetFutureOrders([]string{"6", "5"}, goex.BTC_USD, goex.QUARTER_CONTRACT)) 37 | } 38 | 39 | func TestHbdm_GetFutureOrder(t *testing.T) { 40 | t.Log(dm.GetFutureOrder("6", goex.BTC_USD, goex.QUARTER_CONTRACT)) 41 | } 42 | 43 | func TestHbdm_GetFutureTicker(t *testing.T) { 44 | t.Log(dm.GetFutureTicker(goex.EOS_USD, goex.QUARTER_CONTRACT)) 45 | } 46 | 47 | func TestHbdm_GetFutureDepth(t *testing.T) { 48 | dep, err := dm.GetFutureDepth(goex.BTC_USD, goex.QUARTER_CONTRACT, 0) 49 | t.Log(err) 50 | t.Logf("%+v\n%+v", dep.AskList, dep.BidList) 51 | } 52 | func TestHbdm_GetFutureIndex(t *testing.T) { 53 | t.Log(dm.GetFutureIndex(goex.BTC_USD)) 54 | } 55 | 56 | func TestHbdm_GetFutureEstimatedPrice(t *testing.T) { 57 | t.Log(dm.GetFutureEstimatedPrice(goex.BTC_USD)) 58 | } 59 | 60 | func TestHbdm_GetKlineRecords(t *testing.T) { 61 | klines, _ := dm.GetKlineRecords(goex.QUARTER_CONTRACT, goex.EOS_USD, goex.KLINE_PERIOD_1MIN, 20, 0) 62 | for _, k := range klines { 63 | tt := time.Unix(k.Timestamp, 0) 64 | t.Log(k.Pair, tt, k.Open, k.Close, k.High, k.Low, k.Vol, k.Vol2) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /huobi/HuobiPro_test.go: -------------------------------------------------------------------------------- 1 | package huobi 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net" 7 | "net/http" 8 | "net/url" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | var httpProxyClient = &http.Client{ 14 | Transport: &http.Transport{ 15 | Proxy: func(req *http.Request) (*url.URL, error) { 16 | return &url.URL{ 17 | Scheme: "socks5", 18 | Host: "172.16.226.24:8998"}, nil 19 | }, 20 | Dial: (&net.Dialer{ 21 | Timeout: 10 * time.Second, 22 | }).Dial, 23 | }, 24 | Timeout: 10 * time.Second, 25 | } 26 | 27 | var ( 28 | apikey = "478c732a-4f0cd03e-e779076d-0a87b" 29 | secretkey = "e1b9b5c7-5a9bff84-e72dc5eb-f5dd9" 30 | ) 31 | 32 | // 33 | var hbpro = NewHuoBiProSpot(http.DefaultClient, apikey, secretkey) 34 | 35 | func TestHuobiPro_GetTicker(t *testing.T) { 36 | //return 37 | ticker, err := hbpro.GetTicker(goex.BTC_USDT) 38 | assert.Nil(t, err) 39 | t.Log(ticker) 40 | } 41 | 42 | func TestHuobiPro_GetDepth(t *testing.T) { 43 | dep, err := hbpro.GetDepth(10, goex.ETH_USDT) 44 | assert.Nil(t, err) 45 | t.Log(dep) 46 | //t.Log(dep.BidList) 47 | } 48 | 49 | func TestHuobiPro_GetAccountInfo(t *testing.T) { 50 | return 51 | info, err := hbpro.GetAccountInfo("point") 52 | assert.Nil(t, err) 53 | t.Log(info) 54 | } 55 | 56 | //获取点卡剩余 57 | func TestHuoBiPro_GetPoint(t *testing.T) { 58 | return 59 | point := NewHuoBiProPoint(httpProxyClient, apikey, secretkey) 60 | acc, _ := point.GetAccount() 61 | t.Log(acc.SubAccounts[HBPOINT]) 62 | } 63 | 64 | //获取现货资产信息 65 | func TestHuobiPro_GetAccount(t *testing.T) { 66 | return 67 | acc, err := hbpro.GetAccount() 68 | assert.Nil(t, err) 69 | t.Log(acc.SubAccounts) 70 | } 71 | 72 | func TestHuobiPro_LimitBuy(t *testing.T) { 73 | return 74 | ord, err := hbpro.LimitBuy("", "0.09122", goex.BCC_BTC) 75 | assert.Nil(t, err) 76 | t.Log(ord) 77 | } 78 | 79 | func TestHuobiPro_LimitSell(t *testing.T) { 80 | return 81 | ord, err := hbpro.LimitSell("1", "0.212", goex.BCC_BTC) 82 | assert.Nil(t, err) 83 | t.Log(ord) 84 | } 85 | 86 | func TestHuobiPro_MarketSell(t *testing.T) { 87 | return 88 | ord, err := hbpro.MarketSell("0.1738", "0.212", goex.BCC_BTC) 89 | assert.Nil(t, err) 90 | t.Log(ord) 91 | } 92 | 93 | func TestHuobiPro_MarketBuy(t *testing.T) { 94 | return 95 | ord, err := hbpro.MarketBuy("0.02", "", goex.BCC_BTC) 96 | assert.Nil(t, err) 97 | t.Log(ord) 98 | } 99 | 100 | func TestHuobiPro_GetUnfinishOrders(t *testing.T) { 101 | return 102 | ords, err := hbpro.GetUnfinishOrders(goex.ETC_USDT) 103 | assert.Nil(t, err) 104 | t.Log(ords) 105 | } 106 | 107 | func TestHuobiPro_CancelOrder(t *testing.T) { 108 | return 109 | r, err := hbpro.CancelOrder("600329873", goex.ETH_USDT) 110 | assert.Nil(t, err) 111 | t.Log(r) 112 | t.Log(err) 113 | } 114 | 115 | func TestHuobiPro_GetOneOrder(t *testing.T) { 116 | return 117 | ord, err := hbpro.GetOneOrder("1116237737", goex.LTC_BTC) 118 | assert.Nil(t, err) 119 | t.Log(ord) 120 | } 121 | 122 | func TestHuobiPro_GetOrderHistorys(t *testing.T) { 123 | return 124 | ords, err := hbpro.GetOrderHistorys(goex.NewCurrencyPair2("HT_USDT"), 1, 3) 125 | t.Log(err) 126 | t.Log(ords) 127 | } 128 | 129 | //func TestHuobiPro_GetDepthWithWs(t *testing.T) { 130 | // return 131 | // hbpro.GetDepthWithWs(goex.BTC_USDT, func(dep *goex.Depth) { 132 | // log.Println("%+v", *dep) 133 | // }) 134 | // time.Sleep(time.Minute) 135 | //} 136 | // 137 | //func TestHuobiPro_GetTickerWithWs(t *testing.T) { 138 | // return 139 | // hbpro.GetTickerWithWs(goex.BTC_USDT, func(ticker *goex.Ticker) { 140 | // log.Println("%+v", *ticker) 141 | // }) 142 | // time.Sleep(time.Minute) 143 | //} 144 | // 145 | //func TestHuobiPro_GetKLineWithWs(t *testing.T) { 146 | // return 147 | // hbpro.GetKLineWithWs(goex.BTC_USDT, goex.KLINE_PERIOD_60MIN, func(kline *goex.Kline) { 148 | // log.Println("%+v", *kline) 149 | // }) 150 | // time.Sleep(time.Minute) 151 | //} 152 | 153 | func TestHuobiPro_GetCurrenciesList(t *testing.T) { 154 | return 155 | hbpro.GetCurrenciesList() 156 | } 157 | 158 | func TestHuobiPro_GetCurrenciesPrecision(t *testing.T) { 159 | return 160 | t.Log(hbpro.GetCurrenciesPrecision()) 161 | } 162 | -------------------------------------------------------------------------------- /kraken/Kraken_test.go: -------------------------------------------------------------------------------- 1 | package kraken 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | var k = New(http.DefaultClient, "", "") 11 | var BCH_XBT = goex.NewCurrencyPair(goex.BCH, goex.XBT) 12 | 13 | func TestKraken_GetDepth(t *testing.T) { 14 | dep, err := k.GetDepth(2, goex.BTC_USD) 15 | assert.Nil(t, err) 16 | t.Log(dep) 17 | } 18 | 19 | func TestKraken_GetTicker(t *testing.T) { 20 | ticker, err := k.GetTicker(goex.ETC_BTC) 21 | assert.Nil(t, err) 22 | t.Log(ticker) 23 | } 24 | 25 | func TestKraken_GetAccount(t *testing.T) { 26 | acc, err := k.GetAccount() 27 | assert.Nil(t, err) 28 | t.Log(acc) 29 | } 30 | 31 | func TestKraken_LimitSell(t *testing.T) { 32 | ord, err := k.LimitSell("0.01", "6900", goex.BTC_USD) 33 | assert.Nil(t, err) 34 | t.Log(ord) 35 | } 36 | 37 | func TestKraken_LimitBuy(t *testing.T) { 38 | ord, err := k.LimitBuy("0.01", "6100", goex.NewCurrencyPair(goex.XBT, goex.USD)) 39 | assert.Nil(t, err) 40 | t.Log(ord) 41 | } 42 | 43 | func TestKraken_GetUnfinishOrders(t *testing.T) { 44 | ords, err := k.GetUnfinishOrders(goex.NewCurrencyPair(goex.XBT, goex.USD)) 45 | assert.Nil(t, err) 46 | t.Log(ords) 47 | } 48 | 49 | func TestKraken_CancelOrder(t *testing.T) { 50 | r, err := k.CancelOrder("O6EAJC-YAC3C-XDEEXQ", goex.NewCurrencyPair(goex.XBT, goex.USD)) 51 | assert.Nil(t, err) 52 | t.Log(r) 53 | } 54 | 55 | func TestKraken_GetTradeBalance(t *testing.T) { 56 | // k.GetTradeBalance() 57 | } 58 | 59 | func TestKraken_GetOneOrder(t *testing.T) { 60 | ord, err := k.GetOneOrder("ODCRMQ-RDEID-CY334C", goex.BTC_USD) 61 | assert.Nil(t, err) 62 | t.Log(ord) 63 | } 64 | -------------------------------------------------------------------------------- /liqui/Liqui.go: -------------------------------------------------------------------------------- 1 | package liqui 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | 9 | . "github.com/wudian/GoEx" 10 | "strings" 11 | ) 12 | 13 | const ( 14 | EXCHANGE_NAME = "liqui.io" 15 | 16 | API_BASE_URL = "https://api.liqui.io/" 17 | API_V1 = API_BASE_URL + "api/3/" 18 | TICKER_URI = "ticker/%s" 19 | ) 20 | 21 | type Liqui struct { 22 | accessKey, 23 | secretKey string 24 | httpClient *http.Client 25 | } 26 | 27 | func New(client *http.Client, accessKey, secretKey string) *Liqui { 28 | return &Liqui{accessKey, secretKey, client} 29 | } 30 | 31 | func (liqui *Liqui) GetExchangeName() string { 32 | return EXCHANGE_NAME 33 | } 34 | 35 | func (liqui *Liqui) GetTicker(currency CurrencyPair) (*Ticker, error) { 36 | cur := strings.ToLower(currency.ToSymbol("_")) 37 | if cur == "nil" { 38 | log.Println("Unsupport The CurrencyPair") 39 | return nil, errors.New("Unsupport The CurrencyPair") 40 | } 41 | tickerUri := API_V1 + fmt.Sprintf(TICKER_URI, cur) 42 | bodyDataMap, err := HttpGet(liqui.httpClient, tickerUri) 43 | //fmt.Println("tickerUri:", tickerUri) 44 | //fmt.Println("Liqui bodyDataMap:", bodyDataMap) 45 | if err != nil { 46 | log.Println(err) 47 | return nil, err 48 | } 49 | 50 | var tickerMap map[string]interface{} 51 | var ticker Ticker 52 | 53 | switch bodyDataMap[cur].(type) { 54 | case map[string]interface{}: 55 | tickerMap = bodyDataMap[cur].(map[string]interface{}) 56 | default: 57 | return nil, errors.New(fmt.Sprintf("Type Convert Error ? \n %s", bodyDataMap)) 58 | } 59 | 60 | // fmt.Println(cur, " tickerMap:", tickerMap) 61 | date := tickerMap["updated"].(float64) 62 | ticker.Date = uint64(date) 63 | ticker.Last = tickerMap["last"].(float64) 64 | ticker.Buy = tickerMap["buy"].(float64) 65 | ticker.Sell = tickerMap["sell"].(float64) 66 | ticker.Low = tickerMap["low"].(float64) 67 | ticker.High = tickerMap["high"].(float64) 68 | ticker.Vol = tickerMap["vol"].(float64) 69 | 70 | return &ticker, nil 71 | } 72 | 73 | func (liqui *Liqui) GetDepth() string { 74 | panic("not implements") 75 | } 76 | func (liqui *Liqui) GetAccount() string { 77 | panic("not implements") 78 | } 79 | func (liqui *Liqui) LimitBuy() string { 80 | panic("not implements") 81 | } 82 | func (liqui *Liqui) LimitSell() string { 83 | panic("not implements") 84 | } 85 | func (liqui *Liqui) MarketBuy() string { 86 | panic("not implements") 87 | } 88 | func (liqui *Liqui) MarketSell() string { 89 | panic("not implements") 90 | } 91 | func (liqui *Liqui) CancelOrder() string { 92 | panic("not implements") 93 | } 94 | func (liqui *Liqui) GetOneOrder() string { 95 | panic("not implements") 96 | } 97 | func (liqui *Liqui) GetUnfinishOrders() string { 98 | panic("not implements") 99 | } 100 | 101 | func (liqui *Liqui) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 102 | panic("not implements") 103 | } 104 | 105 | //非个人,整个交易所的交易记录 106 | func (liqui *Liqui) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 107 | panic("not implements") 108 | } 109 | 110 | func (liqui *Liqui) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 111 | panic("not implements") 112 | } 113 | -------------------------------------------------------------------------------- /ocx/ocx.go: -------------------------------------------------------------------------------- 1 | package ocx 2 | 3 | import ( 4 | "crypto" 5 | "crypto/hmac" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | . "github.com/wudian/GoEx" 10 | "log" 11 | "net/http" 12 | "net/url" 13 | "strconv" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | var TimeOffset int64 = 0 19 | 20 | const ( 21 | API_BASE_URL = "https://openapi.ocx.com" 22 | V2 = "/api/v2/" 23 | //API_URL = API_BASE_URL + V2 24 | 25 | TICKER_API = "market/ticker/%s" 26 | TICKERS_API = "market/tickers" 27 | DEPTH_API = "depth?market_code=%s" 28 | SERVER_TIME = "timestamp" 29 | 30 | TRADE_URL = "orders" 31 | GET_ACCOUNT_API = "accounts" 32 | GET_ORDER_API = "orders/%s" 33 | 34 | CANCEL_ORDER_API = "orders/:%s/cancel" 35 | CANCEL_ALL_ORDER_API = "orders/clear" 36 | 37 | PLACE_ORDER_API = "orders" 38 | ) 39 | 40 | type Ocx struct { 41 | httpClient *http.Client 42 | accessKey, 43 | secretKey string 44 | } 45 | 46 | func New(client *http.Client, apikey, secretkey string) *Ocx { 47 | return &Ocx{accessKey: apikey, secretKey: secretkey, httpClient: client} 48 | } 49 | 50 | func (o *Ocx) GetExchangeName() string { 51 | return "ocx.com" 52 | } 53 | 54 | func (o *Ocx) GetServerTime() int64 { 55 | url := API_BASE_URL + V2 + SERVER_TIME 56 | respmap, err := HttpGet(o.httpClient, url) 57 | if err != nil { 58 | return 0 59 | } 60 | data := respmap["data"].(interface{}) 61 | d := data.(map[string]interface{}) 62 | ts := d["timestamp"].(float64) 63 | servertime := int64(ts) 64 | now := time.Now().Unix() 65 | TimeOffset = servertime - now 66 | return servertime 67 | } 68 | 69 | func (o *Ocx) GetTicker(currencyPair CurrencyPair) (*Ticker, error) { 70 | url := API_BASE_URL + V2 + fmt.Sprintf(TICKER_API, strings.ToLower(currencyPair.ToSymbol(""))) 71 | respmap, err := HttpGet(o.httpClient, url) 72 | if err != nil { 73 | return nil, err 74 | } 75 | //log.Println("ticker respmap:", respmap) 76 | 77 | tickmap, ok := respmap["data"].(map[string]interface{}) 78 | if !ok { 79 | return nil, errors.New("tick assert error") 80 | } 81 | ticker := new(Ticker) 82 | ticker.Pair = currencyPair 83 | ticker.Date = ToUint64(tickmap["timestamp"]) 84 | ticker.Last = ToFloat64(tickmap["last"]) 85 | ticker.Vol = ToFloat64(tickmap["volume"]) 86 | ticker.Low = ToFloat64(tickmap["low"]) 87 | ticker.High = ToFloat64(tickmap["high"]) 88 | ticker.Buy = ToFloat64(tickmap["buy"]) 89 | ticker.Sell = ToFloat64(tickmap["sell"]) 90 | 91 | return ticker, nil 92 | 93 | } 94 | 95 | func (o *Ocx) GetDepth(size int, currency CurrencyPair) (*Depth, error) { 96 | url := API_BASE_URL + V2 + fmt.Sprintf(DEPTH_API, strings.ToLower(currency.ToSymbol(""))) 97 | resp, err := HttpGet(o.httpClient, url) 98 | if err != nil { 99 | return nil, err 100 | } 101 | respmap, _ := resp["status"].(map[string]interface{}) 102 | bids, ok1 := respmap["bids"].([]interface{}) 103 | asks, ok2 := respmap["asks"].([]interface{}) 104 | 105 | if !ok1 || !ok2 { 106 | return nil, errors.New("tick assert error") 107 | } 108 | 109 | depth := new(Depth) 110 | 111 | for _, r := range asks { 112 | var dr DepthRecord 113 | rr := r.([]interface{}) 114 | dr.Price = ToFloat64(rr[0]) 115 | dr.Amount = ToFloat64(rr[1]) 116 | depth.AskList = append(depth.AskList, dr) 117 | } 118 | 119 | for _, r := range bids { 120 | var dr DepthRecord 121 | rr := r.([]interface{}) 122 | dr.Price = ToFloat64(rr[0]) 123 | dr.Amount = ToFloat64(rr[1]) 124 | depth.BidList = append(depth.BidList, dr) 125 | } 126 | 127 | return depth, nil 128 | } 129 | 130 | func (o *Ocx) buildSigned(method, apiurl string, para *url.Values) string { 131 | param := "" 132 | if para != nil { 133 | param = para.Encode() 134 | } 135 | 136 | log.Println("param", param) 137 | 138 | sig_str := "" 139 | if para != nil { 140 | sig_str = method + "|" + apiurl + "|" + param 141 | } else { 142 | sig_str = method + "|" + apiurl 143 | } 144 | 145 | log.Println("sig_str", sig_str) 146 | 147 | mac := hmac.New(crypto.SHA256.New, []byte("abc")) 148 | 149 | mac.Write([]byte(sig_str)) 150 | 151 | sum := mac.Sum(nil) 152 | 153 | return fmt.Sprintf("%x", sum) 154 | } 155 | 156 | func (o *Ocx) placeOrder(orderType, orderSide, amount, price string, pair CurrencyPair) (*Order, error) { 157 | uri := API_BASE_URL + V2 + TRADE_URL 158 | method := "POST" 159 | path := V2 + TRADE_URL 160 | params := url.Values{} 161 | tonce := strconv.Itoa(int(time.Now().UnixNano() / 1000000)) 162 | params.Set("access_key", o.accessKey) 163 | params.Set("tonce", tonce) 164 | //params.Set("foo", "bar") 165 | 166 | signed := o.buildSigned(method, path, ¶ms) 167 | 168 | f := fmt.Sprintf("access_key=%s&tonce=%s&signature=%s&market_code=%s&price=%s&side=%s&volume=%s", 169 | o.accessKey, tonce, signed, strings.ToLower(pair.ToSymbol("")), price, orderSide, amount) 170 | resp, err := HttpPostForm3(o.httpClient, uri, f, nil) 171 | //resp, err := HttpPostForm3(o.httpClient, uri, form.Encode(), nil) 172 | log.Println("resp:", string(resp), "err:", err) 173 | if err != nil { 174 | return nil, err 175 | } 176 | 177 | respmap := make(map[string]interface{}) 178 | err = json.Unmarshal(resp, &respmap) 179 | if err != nil { 180 | log.Println(string(resp)) 181 | return nil, err 182 | } 183 | 184 | orderId := ToInt(respmap["orderId"]) 185 | if orderId <= 0 { 186 | return nil, errors.New(string(resp)) 187 | } 188 | 189 | side := BUY 190 | if orderSide == "SELL" { 191 | side = SELL 192 | } 193 | 194 | return &Order{ 195 | Currency: pair, 196 | OrderID: orderId, 197 | OrderID2: fmt.Sprint(orderId), 198 | Price: ToFloat64(price), 199 | Amount: ToFloat64(amount), 200 | DealAmount: 0, 201 | AvgPrice: 0, 202 | Side: TradeSide(side), 203 | Status: ORDER_UNFINISH, 204 | OrderTime: int(time.Now().Unix())}, nil 205 | 206 | panic("1") 207 | } 208 | 209 | func (o *Ocx) LimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 210 | return o.placeOrder("limit", "buy", amount, price, currency) 211 | } 212 | 213 | func (o *Ocx) LimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 214 | return o.placeOrder("limit", "sell", amount, price, currency) 215 | } 216 | 217 | func (o *Ocx) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { 218 | panic("not implement") 219 | } 220 | func (o *Ocx) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { 221 | panic("not implement") 222 | } 223 | 224 | func (o *Ocx) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { 225 | path := API_BASE_URL + V2 + fmt.Sprintf(CANCEL_ORDER_API, strings.ToLower(currency.ToSymbol(""))) 226 | params := url.Values{} 227 | 228 | params.Set("order_id", orderId) 229 | 230 | sign := o.buildSigned("POST", path, ¶ms) 231 | log.Println("path", path, "params", params.Encode(), "sign", sign) 232 | 233 | resp, err := HttpPostForm2(o.httpClient, path, params, nil) 234 | log.Println("resp:", string(resp), "err:", err) 235 | return true, nil 236 | } 237 | 238 | func (o *Ocx) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { 239 | path := API_BASE_URL + V2 + fmt.Sprintf(GET_ORDER_API, orderId) 240 | para := url.Values{} 241 | para.Set("order_id", orderId) 242 | 243 | //sign := o.buildSigned("GET", path, ¶) 244 | 245 | respmap, err := HttpGet2(o.httpClient, path, nil) 246 | 247 | if err != nil { 248 | return nil, err 249 | } 250 | 251 | log.Println(respmap) 252 | 253 | return nil, nil 254 | 255 | } 256 | 257 | func (o *Ocx) GetOrdersList() { 258 | panic("not implement") 259 | 260 | } 261 | 262 | func (o *Ocx) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { 263 | panic("not implement") 264 | } 265 | 266 | func (o *Ocx) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { 267 | panic("not implement") 268 | } 269 | 270 | func (o *Ocx) GetAccount() (*Account, error) { 271 | url := API_BASE_URL + V2 + GET_ACCOUNT_API 272 | //timestamp := strconv.FormatInt((time.Now().UnixNano() / 1000000), 10) 273 | 274 | //sign := o.buildSigned("GET", url, nil) 275 | 276 | respmap, err := HttpGet2(o.httpClient, url, nil) 277 | 278 | if err != nil { 279 | return nil, err 280 | } 281 | 282 | log.Println(respmap) 283 | 284 | if respmap["status"].(float64) != 0 { 285 | return nil, errors.New(respmap["msg"].(string)) 286 | } 287 | 288 | acc := new(Account) 289 | acc.SubAccounts = make(map[Currency]SubAccount) 290 | acc.Exchange = o.GetExchangeName() 291 | 292 | balances := respmap["data"].([]interface{}) 293 | for _, v := range balances { 294 | //log.Println(v) 295 | vv := v.(map[string]interface{}) 296 | currency := NewCurrency(vv["currency"].(string), "") 297 | acc.SubAccounts[currency] = SubAccount{ 298 | Currency: currency, 299 | Amount: ToFloat64(vv["available"]), 300 | ForzenAmount: ToFloat64(vv["frozen"]), 301 | } 302 | } 303 | 304 | return acc, nil 305 | 306 | } 307 | 308 | func (o *Ocx) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { 309 | panic("not implement") 310 | } 311 | 312 | //非个人,整个交易所的交易记录 313 | func (o *Ocx) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { 314 | panic("not implement") 315 | } 316 | -------------------------------------------------------------------------------- /ocx/ocx_test.go: -------------------------------------------------------------------------------- 1 | package ocx 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "net/url" 7 | "testing" 8 | ) 9 | 10 | var o = New(&http.Client{}, "", "") 11 | 12 | func TestOcx_GetServerTime(t *testing.T) { 13 | return 14 | t.Log(o.GetServerTime()) 15 | } 16 | 17 | func TestOcx_buildSigned(t *testing.T) { 18 | return 19 | method := "GET" 20 | path := "/api/v2/markets" 21 | para := url.Values{} 22 | para.Set("access_key", "xxx") 23 | para.Set("tonce", "123456789") 24 | para.Set("foo", "bar") 25 | t.Log(o.buildSigned(method, path, ¶)) 26 | 27 | } 28 | 29 | func TestOcx_LimitSell(t *testing.T) { 30 | //return 31 | 32 | o.LimitBuy("1", "1", goex.BTC_USDT) 33 | } 34 | -------------------------------------------------------------------------------- /okcoin/OKCoin_CN_test.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var okcn = NewOKCoinCn(http.DefaultClient, "", "") 10 | 11 | func TestOKCoinCN_API_GetTicker(t *testing.T) { 12 | ticker, _ := okcn.GetTicker(goex.BTC_USDT) 13 | t.Log(ticker) 14 | } 15 | 16 | func TestOKCoinCN_API_GetDepth(t *testing.T) { 17 | dep, _ := okcn.GetDepth(10, goex.BTC_USDT) 18 | t.Log(dep) 19 | } 20 | 21 | func testOKCoinCN_API_GetKlineRecords(t *testing.T) { 22 | klines, _ := okcn.GetKlineRecords(goex.BTC_USDT, goex.KLINE_PERIOD_1MIN, 1000, -1) 23 | t.Log(klines) 24 | } 25 | -------------------------------------------------------------------------------- /okcoin/OKCoin_COM.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | . "github.com/wudian/GoEx" 7 | "net/http" 8 | "net/url" 9 | "strconv" 10 | ) 11 | 12 | const ( 13 | EXCHANGE_NAME_COM = "okcoin.com" 14 | ) 15 | 16 | type OKCoinCOM_API struct { 17 | OKCoinCN_API 18 | } 19 | 20 | func NewCOM(client *http.Client, api_key, secret_key string) *OKCoinCOM_API { 21 | return &OKCoinCOM_API{OKCoinCN_API{client, api_key, secret_key, "https://www.okcoin.com/api/v1/"}} 22 | } 23 | 24 | func (ctx *OKCoinCOM_API) GetAccount() (*Account, error) { 25 | postData := url.Values{} 26 | err := ctx.buildPostForm(&postData) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | body, err := HttpPostForm(ctx.client, ctx.api_base_url+url_userinfo, postData) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | // println(string(body)) 37 | 38 | var respMap map[string]interface{} 39 | 40 | err = json.Unmarshal(body, &respMap) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | if !respMap["result"].(bool) { 46 | errcode := strconv.FormatFloat(respMap["error_code"].(float64), 'f', 0, 64) 47 | return nil, errors.New(errcode) 48 | } 49 | 50 | info := respMap["info"].(map[string]interface{}) 51 | funds := info["funds"].(map[string]interface{}) 52 | asset := funds["asset"].(map[string]interface{}) 53 | free := funds["free"].(map[string]interface{}) 54 | freezed := funds["freezed"].(map[string]interface{}) 55 | 56 | account := new(Account) 57 | account.Exchange = ctx.GetExchangeName() 58 | account.Asset, _ = strconv.ParseFloat(asset["total"].(string), 64) 59 | account.NetAsset, _ = strconv.ParseFloat(asset["net"].(string), 64) 60 | 61 | var btcSubAccount SubAccount 62 | var ltcSubAccount SubAccount 63 | var cnySubAccount SubAccount 64 | 65 | btcSubAccount.Currency = BTC 66 | btcSubAccount.Amount, _ = strconv.ParseFloat(free["btc"].(string), 64) 67 | btcSubAccount.LoanAmount = 0 68 | btcSubAccount.ForzenAmount, _ = strconv.ParseFloat(freezed["btc"].(string), 64) 69 | 70 | ltcSubAccount.Currency = LTC 71 | ltcSubAccount.Amount, _ = strconv.ParseFloat(free["ltc"].(string), 64) 72 | ltcSubAccount.LoanAmount = 0 73 | ltcSubAccount.ForzenAmount, _ = strconv.ParseFloat(freezed["ltc"].(string), 64) 74 | 75 | cnySubAccount.Currency = CNY 76 | cnySubAccount.Amount, _ = strconv.ParseFloat(free["usd"].(string), 64) 77 | cnySubAccount.LoanAmount = 0 78 | cnySubAccount.ForzenAmount, _ = strconv.ParseFloat(freezed["usd"].(string), 64) 79 | 80 | account.SubAccounts = make(map[Currency]SubAccount, 3) 81 | account.SubAccounts[BTC] = btcSubAccount 82 | account.SubAccounts[LTC] = ltcSubAccount 83 | account.SubAccounts[USD] = cnySubAccount 84 | 85 | return account, nil 86 | } 87 | 88 | func (ctx *OKCoinCOM_API) GetExchangeName() string { 89 | return OKCOIN_COM 90 | } 91 | -------------------------------------------------------------------------------- /okcoin/OKExSpot.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | . "github.com/wudian/GoEx" 7 | "net/http" 8 | "net/url" 9 | "strconv" 10 | ) 11 | 12 | type OKExSpot struct { 13 | OKCoinCN_API 14 | } 15 | 16 | func NewOKExSpot(client *http.Client, accesskey, secretkey string) *OKExSpot { 17 | return &OKExSpot{ 18 | OKCoinCN_API: OKCoinCN_API{client, accesskey, secretkey, "https://www.okex.com/api/v1/"}} 19 | } 20 | 21 | func (ctx *OKExSpot) GetExchangeName() string { 22 | return OKEX 23 | } 24 | 25 | func (ctx *OKExSpot) GetAccount() (*Account, error) { 26 | postData := url.Values{} 27 | err := ctx.buildPostForm(&postData) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | body, err := HttpPostForm(ctx.client, ctx.api_base_url+url_userinfo, postData) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | var respMap map[string]interface{} 38 | 39 | err = json.Unmarshal(body, &respMap) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | if errcode, isok := respMap["error_code"].(float64); isok { 45 | errcodeStr := strconv.FormatFloat(errcode, 'f', 0, 64) 46 | return nil, errors.New(errcodeStr) 47 | } 48 | //log.Println(respMap) 49 | info, ok := respMap["info"].(map[string]interface{}) 50 | if !ok { 51 | return nil, errors.New(string(body)) 52 | } 53 | 54 | funds := info["funds"].(map[string]interface{}) 55 | free := funds["free"].(map[string]interface{}) 56 | freezed := funds["freezed"].(map[string]interface{}) 57 | 58 | account := new(Account) 59 | account.Exchange = ctx.GetExchangeName() 60 | 61 | account.SubAccounts = make(map[Currency]SubAccount, 6) 62 | for k, v := range free { 63 | currencyKey := NewCurrency(k, "") 64 | subAcc := SubAccount{ 65 | Currency: currencyKey, 66 | Amount: ToFloat64(v), 67 | ForzenAmount: ToFloat64(freezed[k])} 68 | account.SubAccounts[currencyKey] = subAcc 69 | } 70 | 71 | return account, nil 72 | } 73 | -------------------------------------------------------------------------------- /okcoin/OKExSpot_test.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | var okexSpot = NewOKExSpot(http.DefaultClient, "06aa16ef-ec6e-425b-bbdb-145bee989842", "EF674EDE16BE81D7F2A2F83330BEDE36") 11 | 12 | func TestOKExSpot_GetTicker(t *testing.T) { 13 | ticker, err := okexSpot.GetTicker(goex.ETC_BTC) 14 | assert.Nil(t, err) 15 | t.Log(ticker) 16 | } 17 | 18 | func TestOKExSpot_GetDepth(t *testing.T) { 19 | dep, err := okexSpot.GetDepth(2, goex.ETC_BTC) 20 | assert.Nil(t, err) 21 | t.Log(dep) 22 | } 23 | 24 | func TestOKExSpot_GetKlineRecords(t *testing.T) { 25 | klines, err := okexSpot.GetKlineRecords(goex.LTC_BTC, goex.KLINE_PERIOD_1MIN, 1000, -1) 26 | t.Log(err, klines) 27 | } 28 | -------------------------------------------------------------------------------- /okcoin/OKEx_Future_Ws.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | . "github.com/wudian/GoEx" 8 | "log" 9 | "strings" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | type OKExFutureWs struct { 15 | *WsBuilder 16 | sync.Once 17 | wsConn *WsConn 18 | 19 | tickerCallback func(*FutureTicker) 20 | depthCallback func(*Depth) 21 | tradeCallback func(*Trade, string) 22 | } 23 | 24 | func NewOKExFutureWs() *OKExFutureWs { 25 | okWs := &OKExFutureWs{WsBuilder: NewWsBuilder()} 26 | okWs.WsBuilder = okWs.WsBuilder. 27 | WsUrl("wss://real.okex.com:10440/ws/v1"). 28 | Heartbeat([]byte("{\"event\": \"ping\"} "), 30*time.Second). 29 | ReconnectIntervalTime(24 * time.Hour). 30 | UnCompressFunc(FlateUnCompress). 31 | ProtoHandleFunc(okWs.handle) 32 | return okWs 33 | } 34 | 35 | func (okWs *OKExFutureWs) SetCallbacks(tickerCallback func(*FutureTicker), 36 | depthCallback func(*Depth), 37 | tradeCallback func(*Trade, string)) { 38 | okWs.tickerCallback = tickerCallback 39 | okWs.depthCallback = depthCallback 40 | okWs.tradeCallback = tradeCallback 41 | } 42 | 43 | func (okWs *OKExFutureWs) SubscribeTicker(pair CurrencyPair, contract string) error { 44 | if okWs.tickerCallback == nil { 45 | return errors.New("please set ticker callback func") 46 | } 47 | return okWs.subscribe(map[string]interface{}{ 48 | "event": "addChannel", 49 | "channel": fmt.Sprintf("ok_sub_futureusd_%s_ticker_%s", strings.ToLower(pair.CurrencyA.Symbol), contract)}) 50 | } 51 | 52 | func (okWs *OKExFutureWs) SubscribeDepth(pair CurrencyPair, contract string, size int) error { 53 | if okWs.depthCallback == nil { 54 | return errors.New("please set depth callback func") 55 | } 56 | return okWs.subscribe(map[string]interface{}{ 57 | "event": "addChannel", 58 | "channel": fmt.Sprintf("ok_sub_futureusd_%s_depth_%s_%d", strings.ToLower(pair.CurrencyA.Symbol), contract, size)}) 59 | } 60 | 61 | func (okWs *OKExFutureWs) SubscribeTrade(pair CurrencyPair, contract string) error { 62 | if okWs.tradeCallback == nil { 63 | return errors.New("please set trade callback func") 64 | } 65 | return okWs.subscribe(map[string]interface{}{ 66 | "event": "addChannel", 67 | "channel": fmt.Sprintf("ok_sub_futureusd_%s_trade_%s", strings.ToLower(pair.CurrencyA.Symbol), contract)}) 68 | } 69 | 70 | func (okWs *OKExFutureWs) subscribe(sub map[string]interface{}) error { 71 | okWs.connectWs() 72 | return okWs.wsConn.Subscribe(sub) 73 | } 74 | 75 | func (okWs *OKExFutureWs) connectWs() { 76 | okWs.Do(func() { 77 | okWs.wsConn = okWs.WsBuilder.Build() 78 | okWs.wsConn.ReceiveMessage() 79 | }) 80 | } 81 | 82 | func (okWs *OKExFutureWs) handle(msg []byte) error { 83 | //log.Println(string(msg)) 84 | if string(msg) == "{\"event\":\"pong\"}" { 85 | // log.Println(string(msg)) 86 | okWs.wsConn.UpdateActiveTime() 87 | return nil 88 | } 89 | 90 | var resp []WsBaseResp 91 | err := json.Unmarshal(msg, &resp) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | if len(resp) < 0 { 97 | return nil 98 | } 99 | 100 | if resp[0].Channel == "addChannel" { 101 | log.Println("subscribe:", string(resp[0].Data)) 102 | return nil 103 | } 104 | 105 | pair, contract, ch := okWs.parseChannel(resp[0].Channel) 106 | 107 | if ch == "ticker" { 108 | var t FutureTicker 109 | err := json.Unmarshal(resp[0].Data, &t) 110 | if err != nil { 111 | return err 112 | } 113 | t.ContractType = contract 114 | t.Pair = pair 115 | okWs.tickerCallback(&t) 116 | return nil 117 | } 118 | 119 | if ch == "depth" { 120 | var ( 121 | d Depth 122 | data struct { 123 | Asks [][]float64 `json:"asks"` 124 | Bids [][]float64 `json:"bids"` 125 | Timestamp int64 `json:"timestamp"` 126 | } 127 | ) 128 | 129 | err := json.Unmarshal(resp[0].Data, &data) 130 | if err != nil { 131 | return err 132 | } 133 | 134 | for _, a := range data.Asks { 135 | d.AskList = append(d.AskList, DepthRecord{a[0], a[1]}) 136 | } 137 | 138 | for _, b := range data.Bids { 139 | d.BidList = append(d.BidList, DepthRecord{b[0], b[1]}) 140 | } 141 | 142 | d.Pair = pair 143 | d.ContractType = contract 144 | //d.UTime = time.Unix(data.Timestamp/1000, 0) 145 | okWs.depthCallback(&d) 146 | 147 | return nil 148 | } 149 | 150 | if ch == "trade" { 151 | var data TradeData 152 | err := json.Unmarshal(resp[0].Data, &data) 153 | if err != nil { 154 | return err 155 | } 156 | 157 | for _, td := range data { 158 | side := TradeSide(SELL) 159 | if td[4] == "bid" { 160 | side = BUY 161 | } 162 | okWs.tradeCallback(&Trade{Pair: pair, Tid: ToInt64(td[0]), Price: ToFloat64(td[1]), 163 | Amount: ToFloat64(td[2]), Type: side, Date: okWs.adaptTime(td[3])}, contract) 164 | } 165 | 166 | return nil 167 | } 168 | 169 | return errors.New("unknown channel for " + resp[0].Channel) 170 | } 171 | 172 | func (okWs *OKExFutureWs) parseChannel(channel string) (pair CurrencyPair, contract string, ch string) { 173 | metas := strings.Split(channel, "_") 174 | pair = NewCurrencyPair2(strings.ToUpper(metas[3] + "_USD")) 175 | contract = metas[5] 176 | ch = metas[4] 177 | return pair, contract, ch 178 | } 179 | 180 | func (okWs *OKExFutureWs) adaptTime(tm string) int64 { 181 | format := "2006-01-02 15:04:05" 182 | day := time.Now().Format("2006-01-02") 183 | local, _ := time.LoadLocation("Asia/Chongqing") 184 | t, _ := time.ParseInLocation(format, day+" "+tm, local) 185 | return t.UnixNano() / 1e6 186 | 187 | } 188 | -------------------------------------------------------------------------------- /okcoin/OKEx_Future_Ws_test.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestNewOKExFutureWs(t *testing.T) { 10 | okWs := NewOKExFutureWs() 11 | okWs.ErrorHandleFunc(func(err error) { 12 | t.Log(err) 13 | }) 14 | okWs.SetCallbacks(func(ticker *goex.FutureTicker) { 15 | t.Log(ticker, ticker.Ticker) 16 | }, func(depth *goex.Depth) { 17 | t.Log(depth.ContractType, depth.Pair, depth.AskList, depth.BidList) 18 | }, func(trade *goex.Trade, contract string) { 19 | t.Log(contract, trade) 20 | }) 21 | okWs.SubscribeTicker(goex.LTC_USD, goex.QUARTER_CONTRACT) 22 | okWs.SubscribeDepth(goex.LTC_USD, goex.QUARTER_CONTRACT, 5) 23 | okWs.SubscribeTrade(goex.LTC_USD, goex.QUARTER_CONTRACT) 24 | time.Sleep(3 * time.Minute) 25 | } 26 | -------------------------------------------------------------------------------- /okcoin/OKEx_Spot_Ws.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | . "github.com/wudian/GoEx" 8 | "log" 9 | "strings" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | type WsBaseResp struct { 15 | Channel string 16 | Data json.RawMessage 17 | } 18 | 19 | type AddChannelData struct { 20 | Result bool 21 | Channel string 22 | } 23 | 24 | type TickerData struct { 25 | Last float64 `json:"last,string"` 26 | Sell float64 `json:"sell,string"` 27 | Buy float64 `json:"buy,string"` 28 | High float64 `json:"high,string"` 29 | Low float64 `json:"low,string"` 30 | Vol float64 `json:"vol,string"` 31 | Timestamp int64 32 | } 33 | 34 | type DepthData struct { 35 | Asks [][]string `json:"asks""` 36 | Bids [][]string `json:"bids"` 37 | Timestamp int64 38 | } 39 | 40 | type TradeData [][]string 41 | 42 | type KlineData [][]string 43 | 44 | type OKExSpotWs struct { 45 | *WsBuilder 46 | sync.Once 47 | wsConn *WsConn 48 | 49 | tickerCallback func(*Ticker) 50 | depthCallback func(*Depth) 51 | tradeCallback func(*Trade) 52 | klineCallback func(*Kline, int) 53 | } 54 | 55 | func NewOKExSpotWs() *OKExSpotWs { 56 | okWs := &OKExSpotWs{} 57 | 58 | okWs.WsBuilder = NewWsBuilder(). 59 | WsUrl("wss://real.okex.com:10440/ws/v1"). 60 | Heartbeat([]byte("{\"event\": \"ping\"} "), 30*time.Second). 61 | ReconnectIntervalTime(24 * time.Hour). 62 | UnCompressFunc(FlateUnCompress). 63 | ProtoHandleFunc(okWs.handle) 64 | 65 | return okWs 66 | } 67 | 68 | func (okWs *OKExSpotWs) SetCallbacks(tickerCallback func(*Ticker), 69 | depthCallback func(*Depth), 70 | tradeCallback func(*Trade), 71 | klineCallback func(*Kline, int)) { 72 | okWs.tickerCallback = tickerCallback 73 | okWs.depthCallback = depthCallback 74 | okWs.tradeCallback = tradeCallback 75 | okWs.klineCallback = klineCallback 76 | } 77 | 78 | func (okWs *OKExSpotWs) subscribe(sub map[string]interface{}) error { 79 | okWs.connectWs() 80 | return okWs.wsConn.Subscribe(sub) 81 | } 82 | 83 | func (okWs *OKExSpotWs) SubscribeDepth(pair CurrencyPair, size int) error { 84 | if okWs.depthCallback == nil { 85 | return errors.New("please set depth callback func") 86 | } 87 | 88 | return okWs.subscribe(map[string]interface{}{ 89 | "event": "addChannel", 90 | "channel": fmt.Sprintf("ok_sub_spot_%s_depth_%d", strings.ToLower(pair.ToSymbol("_")), size)}) 91 | } 92 | 93 | func (okWs *OKExSpotWs) SubscribeTicker(pair CurrencyPair) error { 94 | if okWs.tickerCallback == nil { 95 | return errors.New("please set ticker callback func") 96 | } 97 | 98 | return okWs.subscribe(map[string]interface{}{ 99 | "event": "addChannel", 100 | "channel": fmt.Sprintf("ok_sub_spot_%s_ticker", strings.ToLower(pair.ToSymbol("_")))}) 101 | } 102 | 103 | func (okWs *OKExSpotWs) SubscribeTrade(pair CurrencyPair) error { 104 | if okWs.tradeCallback == nil { 105 | return errors.New("please set trade callback func") 106 | } 107 | 108 | return okWs.subscribe(map[string]interface{}{ 109 | "event": "addChannel", 110 | "channel": fmt.Sprintf("ok_sub_spot_%s_deals", strings.ToLower(pair.ToSymbol("_")))}) 111 | } 112 | 113 | func (okWs *OKExSpotWs) SubscribeKline(pair CurrencyPair, period int) error { 114 | if okWs.klineCallback == nil { 115 | return errors.New("place set kline callback func") 116 | } 117 | 118 | return okWs.subscribe(map[string]interface{}{ 119 | "event": "addChannel", 120 | "channel": fmt.Sprintf("ok_sub_spot_%s_kline_%s", 121 | strings.ToLower(pair.ToSymbol("_")), AdaptKlinePeriodForOKEx(period))}) 122 | } 123 | 124 | func (okWs *OKExSpotWs) connectWs() { 125 | okWs.Do(func() { 126 | okWs.wsConn = okWs.WsBuilder.Build() 127 | okWs.wsConn.ReceiveMessage() 128 | }) 129 | } 130 | 131 | func (okWs *OKExSpotWs) handle(msg []byte) error { 132 | if string(msg) == "{\"event\":\"pong\"}" { 133 | okWs.wsConn.UpdateActiveTime() 134 | return nil 135 | } 136 | 137 | var resp []WsBaseResp 138 | err := json.Unmarshal(msg, &resp) 139 | if err != nil { 140 | return err 141 | } 142 | 143 | if len(resp) < 0 { 144 | return nil 145 | } 146 | 147 | if resp[0].Channel == "addChannel" { 148 | log.Println("subscribe:", string(resp[0].Data)) 149 | return nil 150 | } 151 | 152 | pair, err := okWs.getPairFormChannel(resp[0].Channel) 153 | if err != nil { 154 | log.Println(err, string(msg)) 155 | return nil 156 | } 157 | 158 | if strings.Contains(resp[0].Channel, "depth") { 159 | var ( 160 | depthData DepthData 161 | dep Depth 162 | ) 163 | 164 | err := json.Unmarshal(resp[0].Data, &depthData) 165 | if err != nil { 166 | return err 167 | } 168 | 169 | for _, ask := range depthData.Asks { 170 | dep.AskList = append(dep.AskList, DepthRecord{ToFloat64(ask[0]), ToFloat64(ask[1])}) 171 | } 172 | 173 | for _, bid := range depthData.Bids { 174 | dep.BidList = append(dep.BidList, DepthRecord{ToFloat64(bid[0]), ToFloat64(bid[1])}) 175 | } 176 | 177 | dep.Pair = pair 178 | //dep.UTime = time.Unix(depthData.Timestamp/1000, 0) 179 | 180 | okWs.depthCallback(&dep) 181 | return nil 182 | } 183 | 184 | if strings.Contains(resp[0].Channel, "ticker") { 185 | var tickerData TickerData 186 | err := json.Unmarshal(resp[0].Data, &tickerData) 187 | if err != nil { 188 | return err 189 | } 190 | okWs.tickerCallback(&Ticker{ 191 | Pair: pair, 192 | Last: tickerData.Last, 193 | Low: tickerData.Low, 194 | High: tickerData.High, 195 | Sell: tickerData.Sell, 196 | Buy: tickerData.Buy, 197 | Vol: tickerData.Vol, 198 | Date: int64(tickerData.Timestamp)}) 199 | return nil 200 | } 201 | 202 | if strings.Contains(resp[0].Channel, "deals") { 203 | var ( 204 | tradeData TradeData 205 | ) 206 | 207 | err := json.Unmarshal(resp[0].Data, &tradeData) 208 | if err != nil { 209 | return err 210 | } 211 | 212 | for _, td := range tradeData { 213 | side := TradeSide(SELL) 214 | if td[4] == "bid" { 215 | side = BUY 216 | } 217 | okWs.tradeCallback(&Trade{Pair: pair, Tid: ToInt64(td[0]), 218 | Price: ToFloat64(td[1]), Amount: ToFloat64(td[2]), Type: side, Date: okWs.adaptTime(td[3])}) 219 | } 220 | 221 | return nil 222 | } 223 | 224 | if strings.Contains(resp[0].Channel, "kline") { 225 | var k KlineData 226 | period := okWs.getKlinePeriodFormChannel(resp[0].Channel) 227 | err := json.Unmarshal(resp[0].Data, &k) 228 | if err != nil { 229 | return err 230 | } 231 | okWs.klineCallback(&Kline{ 232 | Pair: pair, 233 | Timestamp: ToInt64(k[0][0]), 234 | Open: ToFloat64(k[0][1]), 235 | Close: ToFloat64(k[0][4]), 236 | High: ToFloat64(k[0][2]), 237 | Low: ToFloat64(k[0][3]), 238 | Vol: ToFloat64(k[0][5])}, period) 239 | return nil 240 | } 241 | 242 | return errors.New("unknown message " + resp[0].Channel) 243 | } 244 | 245 | func (okWs *OKExSpotWs) getPairFormChannel(channel string) (CurrencyPair, error) { 246 | metas := strings.Split(channel, "_") 247 | if len(metas) < 5 { 248 | return UNKNOWN_PAIR, errors.New("channel format error") 249 | } 250 | return NewCurrencyPair2(metas[3] + "_" + metas[4]), nil 251 | } 252 | func (okWs *OKExSpotWs) getKlinePeriodFormChannel(channel string) int { 253 | metas := strings.Split(channel, "_") 254 | if len(metas) < 7 { 255 | return 0 256 | } 257 | 258 | switch metas[6] { 259 | case "1hour": 260 | return KLINE_PERIOD_1H 261 | case "2hour": 262 | return KLINE_PERIOD_2H 263 | case "4hour": 264 | return KLINE_PERIOD_4H 265 | case "1min": 266 | return KLINE_PERIOD_1MIN 267 | case "5min": 268 | return KLINE_PERIOD_5MIN 269 | case "15min": 270 | return KLINE_PERIOD_15MIN 271 | case "30min": 272 | return KLINE_PERIOD_30MIN 273 | case "day": 274 | return KLINE_PERIOD_1DAY 275 | case "week": 276 | return KLINE_PERIOD_1WEEK 277 | default: 278 | return 0 279 | } 280 | } 281 | 282 | func (okWs *OKExSpotWs) adaptTime(tm string) int64 { 283 | format := "2006-01-02 15:04:05" 284 | day := time.Now().Format("2006-01-02") 285 | local, _ := time.LoadLocation("Asia/Chongqing") 286 | t, _ := time.ParseInLocation(format, day+" "+tm, local) 287 | return t.UnixNano() / 1e6 288 | 289 | } 290 | -------------------------------------------------------------------------------- /okcoin/OKEx_Spot_Ws_test.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestNewOKExSpotWs(t *testing.T) { 10 | okSpotWs := NewOKExSpotWs() 11 | // okSpotWs.ProxyUrl("socks5://127.0.0.1:1080") 12 | 13 | okSpotWs.SetCallbacks(func(ticker *goex.Ticker) { 14 | t.Log(ticker) 15 | }, func(depth *goex.Depth) { 16 | t.Log(depth) 17 | }, func(trade *goex.Trade) { 18 | t.Log(trade) 19 | }, func(kline *goex.Kline, i int) { 20 | t.Log(i, kline) 21 | }) 22 | 23 | okSpotWs.ErrorHandleFunc(func(err error) { 24 | t.Log(err) 25 | }) 26 | // t.Log(okSpotWs.SubscribeTicker(goex.BTC_USDT)) 27 | // t.Log(okSpotWs.SubscribeTicker(goex.BCH_USDT)) 28 | //okSpotWs.SubscribeDepth(goex.BTC_USDT, 5) 29 | //okSpotWs.SubscribeTrade(goex.BTC_USDT) 30 | t.Log(okSpotWs.SubscribeKline(goex.BTC_USDT, goex.KLINE_PERIOD_1H)) 31 | time.Sleep(10 * time.Second) 32 | } 33 | -------------------------------------------------------------------------------- /okcoin/OKEx_test.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | . "github.com/wudian/GoEx" 5 | "github.com/stretchr/testify/assert" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | okex = NewOKEx(http.DefaultClient, "", "") 12 | ) 13 | 14 | func TestOKEx_GetFutureDepth(t *testing.T) { 15 | dep, err := okex.GetFutureDepth(BTC_USD, QUARTER_CONTRACT, 1) 16 | assert.Nil(t, err) 17 | t.Log(dep) 18 | } 19 | -------------------------------------------------------------------------------- /okcoin/OKcoin_COM_test.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var okcom = NewCOM(http.DefaultClient, "", "") 10 | 11 | func TestOKCoinCOM_API_GetTicker(t *testing.T) { 12 | ticker, _ := okcom.GetTicker(goex.BTC_CNY) 13 | t.Log(ticker) 14 | } 15 | -------------------------------------------------------------------------------- /okex/OKExSwap_test.go: -------------------------------------------------------------------------------- 1 | package okex 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var config = &goex.APIConfig{ 10 | HttpClient: http.DefaultClient, 11 | ApiPassphrase: "", 12 | ApiSecretKey: "", 13 | ApiKey: "", 14 | } 15 | 16 | var okExSwap goex.FutureRestAPI = NewOKExSwap(config) 17 | 18 | func TestOKExSwap_GetFutureUserinfo(t *testing.T) { 19 | t.Log(okExSwap.GetFutureUserinfo()) 20 | } 21 | 22 | func TestOKExSwap_PlaceFutureOrder(t *testing.T) { 23 | t.Log(okExSwap.PlaceFutureOrder(goex.BTC_USD, BTC_USD_SWAP, "3675.6", "10", goex.CLOSE_BUY, 0, 0)) 24 | } 25 | 26 | func TestOKExSwap_FutureCancelOrder(t *testing.T) { 27 | t.Log(okExSwap.FutureCancelOrder(goex.BTC_USD, BTC_USD_SWAP, "64-a-3e3c3c359-0")) 28 | } 29 | 30 | func TestOKExSwap_GetFutureOrder(t *testing.T) { 31 | t.Log(okExSwap.GetFutureOrder("65-4-3e62a331c-0", goex.LTC_USD, LTC_USD_SWAP)) 32 | } 33 | 34 | func TestOKExSwap_GetFuturePosition(t *testing.T) { 35 | t.Log(okExSwap.GetFuturePosition(goex.BTC_USD, BTC_USD_SWAP)) 36 | } 37 | 38 | func TestOKExSwap_GetFutureDepth(t *testing.T) { 39 | t.Log(okExSwap.GetFutureDepth(goex.LTC_USD, LTC_USD_SWAP, 10)) 40 | } 41 | 42 | func TestOKExSwap_GetFutureTicker(t *testing.T) { 43 | t.Log(okExSwap.GetFutureTicker(goex.BTC_USD, BTC_USD_SWAP)) 44 | } 45 | 46 | func TestOKExSwap_GetUnfinishFutureOrders(t *testing.T) { 47 | t.Log(okExSwap.GetUnfinishFutureOrders(goex.BTC_USD, BTC_USD_SWAP)) 48 | } 49 | -------------------------------------------------------------------------------- /okex/ParamSignUtil.go: -------------------------------------------------------------------------------- 1 | package okex 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/wudian/GoEx" 9 | "log" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | /* 15 | Get a iso time 16 | eg: 2018-03-16T18:02:48.284Z 17 | */ 18 | func IsoTime() string { 19 | utcTime := time.Now().UTC() 20 | iso := utcTime.String() 21 | isoBytes := []byte(iso) 22 | iso = string(isoBytes[:10]) + "T" + string(isoBytes[11:23]) + "Z" 23 | return iso 24 | } 25 | 26 | /* 27 | Get a http request body is a json string and a byte array. 28 | */ 29 | func BuildRequestBody(params interface{}) (string, *bytes.Reader, error) { 30 | if params == nil { 31 | return "", nil, errors.New("illegal parameter") 32 | } 33 | data, err := json.Marshal(params) 34 | if err != nil { 35 | log.Println(err) 36 | return "", nil, errors.New("json convert string error") 37 | } 38 | 39 | jsonBody := string(data) 40 | binBody := bytes.NewReader(data) 41 | 42 | return jsonBody, binBody, nil 43 | } 44 | 45 | func doParamSign(httpMethod, apiSecret, uri, requestBody string) (string, string) { 46 | timestamp := IsoTime() 47 | preText := fmt.Sprintf("%s%s%s%s", timestamp, strings.ToUpper(httpMethod), uri, requestBody) 48 | log.Println("preHash", preText) 49 | sign, _ := goex.GetParamHmacSHA256Base64Sign(apiSecret, preText) 50 | return sign, timestamp 51 | } 52 | -------------------------------------------------------------------------------- /okex/SwapResponse.go: -------------------------------------------------------------------------------- 1 | package okex 2 | 3 | /* 4 | OKEX api result definition 5 | @author Lingting Fu 6 | @date 2018-12-27 7 | @version 1.0.0 8 | */ 9 | 10 | type SwapPositionHolding struct { 11 | LiquidationPrice float64 `json:"liquidation_price , string"` 12 | Position float64 `json:"position,string"` 13 | AvailPosition float64 `json:"avail_position,string"` 14 | AvgCost float64 `json:"avg_cost , string"` 15 | SettlementPrice float64 `json:"settlement_price,string"` 16 | InstrumentId string `json:"instrument_id"` 17 | Leverage string `json:"leverage"` 18 | RealizedPnl float64 `json:"realized_pnl,string"` 19 | Side string `json:"side"` 20 | Timestamp string `json:"timestamp"` 21 | Margin string `json:"margin";default:""` 22 | } 23 | 24 | type BizWarmTips struct { 25 | Code int `json:"code"` 26 | Message string `json:"message"` 27 | } 28 | 29 | type SwapPosition struct { 30 | BizWarmTips 31 | MarginMode string `json:"margin_mode"` 32 | Holding []SwapPositionHolding `json:"holding"` 33 | } 34 | 35 | type SwapAccountInfo struct { 36 | InstrumentId string `json:"instrument_id"` 37 | Timestamp string `json:"timestamp"` 38 | MarginFrozen float64 `json:"margin_frozen,string"` 39 | TotalAvailBalance float64 `json:"total_avail_balance,string"` 40 | MarginRatio float64 `json:"margin_ratio,string"` 41 | RealizedPnl float64 `json:"realized_pnl,string"` 42 | UnrealizedPnl float64 `json:"unrealized_pnl,string"` 43 | FixedBalance float64 `json:"fixed_balance,string"` 44 | Equity float64 `json:"equity,string"` 45 | Margin float64 `json:"margin,string"` 46 | MarginMode string `json:"margin_mode"` 47 | } 48 | 49 | type SwapAccounts struct { 50 | BizWarmTips 51 | Info []SwapAccountInfo `json:"info"` 52 | } 53 | 54 | type BaseSwapOrderResult struct { 55 | OrderId string `json:"order_id"` 56 | ClientOid string `json:"client_oid"` 57 | ErrorMessage string `json:"error_message"` 58 | ErrorCode string `json:"error_code"` 59 | Result string `json:"result"` 60 | } 61 | 62 | type SwapOrderResult struct { 63 | BaseSwapOrderResult 64 | BizWarmTips 65 | } 66 | 67 | type SwapOrdersResult struct { 68 | BizWarmTips 69 | OrderInfo []BaseSwapOrderResult `json:"order_info"` 70 | } 71 | 72 | type SwapCancelOrderResult struct { 73 | ErrorMessage string `json:"error_message"` 74 | ErrorCode string `json:"error_code"` 75 | OrderId string `json:"order_id"` 76 | Result bool `json:"result,string"` 77 | } 78 | 79 | type SwapBatchCancelOrderResult struct { 80 | BizWarmTips 81 | InstrumentId string `json:"instrument_id"` 82 | Ids []string `json:"ids"` 83 | Result string `json:"result"` 84 | } 85 | 86 | type BaseOrderInfo struct { 87 | InstrumentId string `json:"instrument_id"` 88 | Status int `json:"status,string"` 89 | OrderId string `json:"order_id"` 90 | Timestamp string `json:"timestamp"` 91 | Price float64 `json:"price,string"` 92 | PriceAvg float64 `json:"price_avg,string"` 93 | Size float64 `json:"size,string"` 94 | Fee float64 `json:"fee,string"` 95 | FilledQty float64 `json:"filled_qty,string"` 96 | ContractVal string `json:"contract_val"` 97 | Type int `json:"type,string"` 98 | } 99 | 100 | type SwapOrdersInfo struct { 101 | BizWarmTips 102 | OrderInfo []BaseOrderInfo `json:"order_info"` 103 | } 104 | 105 | type BaseFillInfo struct { 106 | InstrumentId string `json:"instrument_id"` 107 | OrderQty string `json:"order_qty"` 108 | TradeId string `json:"trade_id"` 109 | Fee string `json:"fee"` 110 | OrderId string `json:"order_id"` 111 | Timestamp string `json:"timestamp"` 112 | Price string `json:"price"` 113 | Side string `json:"side"` 114 | ExecType string `json:"exec_type"` 115 | } 116 | 117 | type SwapFillsInfo struct { 118 | BizWarmTips 119 | FillInfo []*BaseFillInfo `json:"fill_info"` 120 | } 121 | 122 | type SwapAccountsSetting struct { 123 | BizWarmTips 124 | InstrumentId string `json:"instrument_id"` 125 | LongLeverage string `json:"long_leverage"` 126 | ShortLeverage string `json:"short_leverage"` 127 | MarginMode string `json:"margin_mode"` 128 | } 129 | 130 | type BaseLedgerInfo struct { 131 | InstrumentId string `json:"instrument_id"` 132 | Fee string `json:"fee"` 133 | Timestamp string `json:"timestamp"` 134 | Amount string `json:"amount"` 135 | LedgerId string `json:"ledger_id"` 136 | Type string `json:"type"` 137 | } 138 | 139 | type SwapAccountsLedgerList []BaseLedgerInfo 140 | 141 | type BaseInstrumentInfo struct { 142 | InstrumentId string `json:"instrument_id"` 143 | QuoteCurrency string `json:"quote_currency"` 144 | TickSize string `json:"tick_size"` 145 | ContractVal string `json:"contract_val"` 146 | Listing string `json:"listing"` 147 | UnderlyingIndex string `json:"underlying_index"` 148 | Delivery string `json:"delivery"` 149 | Coin string `json:"coin"` 150 | SizeIncrement string `json:"size_increment"` 151 | } 152 | 153 | type SwapInstrumentList []BaseInstrumentInfo 154 | 155 | type BaesDepthInfo []interface{} 156 | type SwapInstrumentDepth struct { 157 | BizWarmTips 158 | Timestamp string `json:"time"` 159 | Bids []BaesDepthInfo `json:"bids"` 160 | Asks []BaesDepthInfo `json:"asks"` 161 | } 162 | 163 | type BaseTickerInfo struct { 164 | InstrumentId string `json:"instrument_id"` 165 | Last float64 `json:"last,string"` 166 | Timestamp string `json:"timestamp"` 167 | High24h float64 `json:"high_24h,string"` 168 | Volume24h float64 `json:"volume_24h,string"` 169 | Low24h float64 `json:"low_24h,string"` 170 | } 171 | 172 | type SwapTickerList []BaseTickerInfo 173 | 174 | type BaseTradeInfo struct { 175 | Timestamp string `json:"timestamp"` 176 | TradeId string `json:"trade_id"` 177 | Side string `json:"side"` 178 | Price string `json:"price"` 179 | Size string `json:"size"` 180 | } 181 | 182 | type SwapTradeList []BaseTradeInfo 183 | 184 | type BaseCandleInfo []interface{} 185 | type SwapCandleList []BaseCandleInfo 186 | 187 | type SwapIndexInfo struct { 188 | BizWarmTips 189 | InstrumentId string `json:"instrument_id"` 190 | Index string `json:"index"` 191 | Timestamp string `json:"timestamp"` 192 | } 193 | 194 | type SwapRate struct { 195 | InstrumentId string `json:"instrument_id"` 196 | Timestamp string `json:"timestamp"` 197 | Rate string `json:"rate"` 198 | } 199 | 200 | type BaseInstrumentAmount struct { 201 | BizWarmTips 202 | InstrumentId string `json:"instrument_id"` 203 | Timestamp string `json:"timestamp"` 204 | Amount string `json:"amount"` 205 | } 206 | 207 | type SwapOpenInterest BaseInstrumentAmount 208 | 209 | type SwapPriceLimit struct { 210 | BizWarmTips 211 | InstrumentId string `json:"instrument_id"` 212 | Lowest string `json:"lowest"` 213 | Highest string `json:"highest"` 214 | Timestamp string `json:"timestamp"` 215 | } 216 | 217 | type BaseLiquidationInfo struct { 218 | InstrumentId string `json:"instrument_id"` 219 | Loss string `json:"loss"` 220 | CreatedAt string `json:"created_at"` 221 | Type string `json:"type"` 222 | Price string `json:"price"` 223 | Size string `json:"size"` 224 | } 225 | 226 | type SwapLiquidationList []BaseLiquidationInfo 227 | 228 | type SwapAccountHolds BaseInstrumentAmount 229 | 230 | type SwapFundingTime struct { 231 | BizWarmTips 232 | InstrumentId string `json:"instrument_id"` 233 | FundingTime string `json:"funding_time"` 234 | } 235 | 236 | type SwapMarkPrice struct { 237 | BizWarmTips 238 | InstrumentId string `json:"instrument_id"` 239 | MarkPrice string `json:"mark_price"` 240 | Timestamp string `json:"timestamp"` 241 | } 242 | 243 | type BaseHistoricalFundingRate struct { 244 | InstrumentId string `json:"instrument_id"` 245 | InterestRate string `json:"interest_rate"` 246 | FundingRate string `json:"funding_rate"` 247 | FundingTime string `json:"funding_time"` 248 | RealizedRate string `json:"realized_rate"` 249 | } 250 | 251 | type SwapHistoricalFundingRateList []BaseHistoricalFundingRate 252 | -------------------------------------------------------------------------------- /poloniex/PoloniexMarginTrading.go: -------------------------------------------------------------------------------- 1 | package poloniex 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | . "github.com/wudian/GoEx" 7 | "log" 8 | "net/url" 9 | ) 10 | 11 | type PoloniexGenericResponse struct { 12 | Success int `json:"success"` 13 | Error string `json:"error"` 14 | } 15 | 16 | type PoloniexMarginPosition struct { 17 | Amount float64 `json:"amount,string"` 18 | Total float64 `json:"total,string"` 19 | BasePrice float64 `json:"basePrice,string"` 20 | LiquidiationPrice float64 `json:"liquidiationPrice"` 21 | ProfitLoss float64 `json:"pl,string"` 22 | LendingFees float64 `json:"lendingFees,string"` 23 | Type string `json:"type"` 24 | } 25 | 26 | func (poloniex *Poloniex) MarginLimitBuy(amount, price string, currency CurrencyPair) (*Order, error) { 27 | return poloniex.placeLimitOrder("marginBuy", amount, price, currency) 28 | } 29 | 30 | func (poloniex *Poloniex) MarginLimitSell(amount, price string, currency CurrencyPair) (*Order, error) { 31 | return poloniex.placeLimitOrder("marginSell", amount, price, currency) 32 | } 33 | 34 | func (poloniex *Poloniex) GetMarginPosition(currency CurrencyPair) (*PoloniexMarginPosition, error) { 35 | values := url.Values{} 36 | values.Set("command", "getMarginPosition") 37 | values.Set("currencyPair", currency.AdaptUsdToUsdt().Reverse().ToSymbol("_")) 38 | result := PoloniexMarginPosition{} 39 | err := poloniex.sendAuthenticatedRequest(values, &result) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return &result, nil 44 | } 45 | 46 | func (poloniex *Poloniex) CloseMarginPosition(currency CurrencyPair) (bool, error) { 47 | values := url.Values{} 48 | values.Set("command", "closeMarginPosition") 49 | values.Set("currencyPair", currency.AdaptUsdToUsdt().Reverse().ToSymbol("_")) 50 | result := PoloniexGenericResponse{} 51 | err := poloniex.sendAuthenticatedRequest(values, &result) 52 | if err != nil { 53 | return false, err 54 | } 55 | if result.Success == 0 { 56 | return false, errors.New(result.Error) 57 | } 58 | return true, nil 59 | } 60 | 61 | func (poloniex *Poloniex) sendAuthenticatedRequest(values url.Values, result interface{}) error { 62 | sign, _ := poloniex.buildPostForm(&values) 63 | 64 | headers := map[string]string{ 65 | "Key": poloniex.accessKey, 66 | "Sign": sign} 67 | 68 | resp, err := HttpPostForm2(poloniex.client, TRADE_API, values, headers) 69 | if err != nil { 70 | log.Println(err) 71 | return err 72 | } 73 | 74 | err = json.Unmarshal(resp, &result) 75 | if err != nil { 76 | return errors.New("Unable to JSON Unmarshal response.") 77 | } 78 | 79 | return nil 80 | } 81 | -------------------------------------------------------------------------------- /websocket.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gorilla/websocket" 6 | "log" 7 | "net/http" 8 | "net/http/httputil" 9 | "net/url" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | type WsConfig struct { 15 | WsUrl string 16 | ProxyUrl string 17 | ReqHeaders map[string][]string //连接的时候加入的头部信息 18 | HeartbeatIntervalTime time.Duration // 19 | HeartbeatData []byte //心跳数据 20 | ReconnectIntervalTime time.Duration //定时重连时间间隔 21 | ProtoHandleFunc func([]byte) error //协议处理函数 22 | UnCompressFunc func([]byte) ([]byte, error) //解压函数 23 | ErrorHandleFunc func(err error) 24 | IsDump bool 25 | } 26 | 27 | type WsConn struct { 28 | *websocket.Conn 29 | sync.Mutex 30 | WsConfig 31 | 32 | activeTime time.Time 33 | activeTimeL sync.Mutex 34 | 35 | mu chan struct{} // lock write data 36 | closeHeartbeat chan struct{} 37 | closeReconnect chan struct{} 38 | closeRecv chan struct{} 39 | closeCheck chan struct{} 40 | subs []interface{} 41 | } 42 | 43 | type WsBuilder struct { 44 | wsConfig *WsConfig 45 | } 46 | 47 | func NewWsBuilder() *WsBuilder { 48 | return &WsBuilder{&WsConfig{}} 49 | } 50 | 51 | func (b *WsBuilder) WsUrl(wsUrl string) *WsBuilder { 52 | b.wsConfig.WsUrl = wsUrl 53 | return b 54 | } 55 | 56 | func (b *WsBuilder) ProxyUrl(proxyUrl string) *WsBuilder { 57 | b.wsConfig.ProxyUrl = proxyUrl 58 | return b 59 | } 60 | 61 | func (b *WsBuilder) ReqHeader(key, value string) *WsBuilder { 62 | b.wsConfig.ReqHeaders[key] = append(b.wsConfig.ReqHeaders[key], value) 63 | return b 64 | } 65 | 66 | func (b *WsBuilder) Dump() *WsBuilder { 67 | b.wsConfig.IsDump = true 68 | return b 69 | } 70 | 71 | func (b *WsBuilder) Heartbeat(data []byte, t time.Duration) *WsBuilder { 72 | b.wsConfig.HeartbeatIntervalTime = t 73 | b.wsConfig.HeartbeatData = data 74 | return b 75 | } 76 | 77 | func (b *WsBuilder) ReconnectIntervalTime(t time.Duration) *WsBuilder { 78 | b.wsConfig.ReconnectIntervalTime = t 79 | return b 80 | } 81 | 82 | func (b *WsBuilder) ProtoHandleFunc(f func([]byte) error) *WsBuilder { 83 | b.wsConfig.ProtoHandleFunc = f 84 | return b 85 | } 86 | 87 | func (b *WsBuilder) UnCompressFunc(f func([]byte) ([]byte, error)) *WsBuilder { 88 | b.wsConfig.UnCompressFunc = f 89 | return b 90 | } 91 | 92 | func (b *WsBuilder) ErrorHandleFunc(f func(err error)) *WsBuilder { 93 | b.wsConfig.ErrorHandleFunc = f 94 | return b 95 | } 96 | 97 | func (b *WsBuilder) Build() *WsConn { 98 | if b.wsConfig.ErrorHandleFunc == nil { 99 | b.wsConfig.ErrorHandleFunc = func(err error) { 100 | log.Println(err) 101 | } 102 | } 103 | wsConn := &WsConn{WsConfig: *b.wsConfig} 104 | return wsConn.NewWs() 105 | } 106 | 107 | func (ws *WsConn) NewWs() *WsConn { 108 | ws.Lock() 109 | defer ws.Unlock() 110 | 111 | ws.connect() 112 | 113 | ws.mu = make(chan struct{}, 1) 114 | ws.closeHeartbeat = make(chan struct{}, 1) 115 | ws.closeReconnect = make(chan struct{}, 1) 116 | ws.closeRecv = make(chan struct{}, 1) 117 | ws.closeCheck = make(chan struct{}, 1) 118 | 119 | ws.HeartbeatTimer() 120 | ws.ReConnectTimer() 121 | ws.checkStatusTimer() 122 | 123 | return ws 124 | } 125 | 126 | func (ws *WsConn) connect() { 127 | dialer := websocket.DefaultDialer 128 | 129 | if ws.ProxyUrl != "" { 130 | proxy, err := url.Parse(ws.ProxyUrl) 131 | if err == nil { 132 | log.Println("proxy url :", proxy) 133 | dialer.Proxy = http.ProxyURL(proxy) 134 | } else { 135 | log.Println("proxy url error ? ", err) 136 | } 137 | } 138 | 139 | wsConn, resp, err := dialer.Dial(ws.WsUrl, http.Header(ws.ReqHeaders)) 140 | if err != nil { 141 | panic(err) 142 | } 143 | 144 | ws.Conn = wsConn 145 | 146 | if ws.IsDump { 147 | dumpData, _ := httputil.DumpResponse(resp, true) 148 | log.Println(string(dumpData)) 149 | } 150 | 151 | ws.UpdateActiveTime() 152 | } 153 | 154 | func (ws *WsConn) SendJsonMessage(v interface{}) error { 155 | ws.mu <- struct{}{} 156 | defer func() { 157 | <-ws.mu 158 | }() 159 | return ws.WriteJSON(v) 160 | } 161 | 162 | func (ws *WsConn) SendTextMessage(data []byte) error { 163 | ws.mu <- struct{}{} 164 | defer func() { 165 | <-ws.mu 166 | }() 167 | return ws.WriteMessage(websocket.TextMessage, data) 168 | } 169 | 170 | func (ws *WsConn) ReConnect() { 171 | ws.Lock() 172 | defer ws.Unlock() 173 | 174 | log.Println("close ws error :", ws.Close()) 175 | time.Sleep(time.Second) 176 | 177 | ws.connect() 178 | 179 | //re subscribe 180 | for _, sub := range ws.subs { 181 | log.Println("subscribe:", sub) 182 | ws.SendJsonMessage(sub) 183 | } 184 | } 185 | 186 | func (ws *WsConn) ReConnectTimer() { 187 | if ws.ReconnectIntervalTime == 0 { 188 | return 189 | } 190 | 191 | timer := time.NewTimer(ws.ReconnectIntervalTime) 192 | 193 | go func() { 194 | ws.clearChannel(ws.closeReconnect) 195 | 196 | for { 197 | select { 198 | case <-timer.C: 199 | log.Println("reconnect websocket") 200 | ws.ReConnect() 201 | timer.Reset(ws.ReconnectIntervalTime) 202 | case <-ws.closeReconnect: 203 | timer.Stop() 204 | log.Println("close websocket connect , exiting reconnect timer goroutine.") 205 | return 206 | } 207 | } 208 | }() 209 | } 210 | 211 | func (ws *WsConn) checkStatusTimer() { 212 | if ws.HeartbeatIntervalTime == 0 { 213 | return 214 | } 215 | 216 | timer := time.NewTimer(ws.HeartbeatIntervalTime) 217 | 218 | go func() { 219 | ws.clearChannel(ws.closeCheck) 220 | 221 | for { 222 | select { 223 | case <-timer.C: 224 | now := time.Now() 225 | if now.Sub(ws.activeTime) >= 2*ws.HeartbeatIntervalTime { 226 | log.Println("active time [ ", ws.activeTime, " ] has expired , begin reconnect ws.") 227 | ws.ReConnect() 228 | } 229 | timer.Reset(ws.HeartbeatIntervalTime) 230 | case <-ws.closeCheck: 231 | log.Println("check status timer exiting") 232 | return 233 | } 234 | } 235 | }() 236 | } 237 | 238 | func (ws *WsConn) HeartbeatTimer() { 239 | log.Println("heartbeat interval time = ", ws.HeartbeatIntervalTime) 240 | if ws.HeartbeatIntervalTime == 0 { 241 | return 242 | } 243 | 244 | timer := time.NewTicker(ws.HeartbeatIntervalTime) 245 | go func() { 246 | ws.clearChannel(ws.closeHeartbeat) 247 | 248 | for { 249 | select { 250 | case <-timer.C: 251 | err := ws.SendTextMessage(ws.HeartbeatData) 252 | if err != nil { 253 | log.Println("heartbeat error , ", err) 254 | time.Sleep(time.Second) 255 | } 256 | case <-ws.closeHeartbeat: 257 | timer.Stop() 258 | log.Println("close websocket connect , exiting heartbeat goroutine.") 259 | return 260 | } 261 | } 262 | }() 263 | } 264 | 265 | func (ws *WsConn) Subscribe(subEvent interface{}) error { 266 | err := ws.SendJsonMessage(subEvent) 267 | if err != nil { 268 | return err 269 | } 270 | ws.subs = append(ws.subs, subEvent) 271 | return nil 272 | } 273 | 274 | func (ws *WsConn) ReceiveMessage() { 275 | ws.clearChannel(ws.closeRecv) 276 | 277 | go func() { 278 | for { 279 | 280 | if len(ws.closeRecv) > 0 { 281 | <-ws.closeRecv 282 | log.Println("close websocket , exiting receive message goroutine.") 283 | return 284 | } 285 | 286 | t, msg, err := ws.ReadMessage() 287 | if err != nil { 288 | ws.ErrorHandleFunc(err) 289 | time.Sleep(time.Second) 290 | continue 291 | } 292 | 293 | switch t { 294 | case websocket.TextMessage: 295 | ws.ProtoHandleFunc(msg) 296 | case websocket.BinaryMessage: 297 | if ws.UnCompressFunc == nil { 298 | ws.ProtoHandleFunc(msg) 299 | } else { 300 | msg2, err := ws.UnCompressFunc(msg) 301 | if err != nil { 302 | ws.ErrorHandleFunc(fmt.Errorf("%s,%s", "un compress error", err.Error())) 303 | } else { 304 | err := ws.ProtoHandleFunc(msg2) 305 | if err != nil { 306 | ws.ErrorHandleFunc(err) 307 | } 308 | } 309 | } 310 | case websocket.CloseMessage: 311 | ws.CloseWs() 312 | return 313 | default: 314 | log.Println("error websocket message type , content is :\n", string(msg)) 315 | } 316 | } 317 | }() 318 | } 319 | 320 | func (ws *WsConn) UpdateActiveTime() { 321 | ws.activeTimeL.Lock() 322 | defer ws.activeTimeL.Unlock() 323 | 324 | ws.activeTime = time.Now() 325 | } 326 | 327 | func (ws *WsConn) CloseWs() { 328 | ws.clearChannel(ws.closeCheck) 329 | ws.clearChannel(ws.closeReconnect) 330 | ws.clearChannel(ws.closeHeartbeat) 331 | ws.clearChannel(ws.closeRecv) 332 | 333 | ws.closeReconnect <- struct{}{} 334 | ws.closeHeartbeat <- struct{}{} 335 | ws.closeRecv <- struct{}{} 336 | ws.closeCheck <- struct{}{} 337 | 338 | err := ws.Close() 339 | if err != nil { 340 | log.Println("close websocket error , ", err) 341 | } 342 | } 343 | 344 | func (ws *WsConn) clearChannel(c chan struct{}) { 345 | for { 346 | if len(c) > 0 { 347 | <-c 348 | } else { 349 | break 350 | } 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /websocket_test.go: -------------------------------------------------------------------------------- 1 | package goex 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func Test_time(t *testing.T) { 10 | t.Log(time.Now().Unix()) 11 | } 12 | 13 | func ProtoHandle(data []byte) error { 14 | println(string(data)) 15 | return nil 16 | } 17 | 18 | func TestNewWsConn(t *testing.T) { 19 | 20 | ws := NewWsBuilder().Dump().WsUrl("wss://real.okex.com:10442/ws/v3").ProxyUrl("socks5://127.0.0.1:55307").UnCompressFunc(FlateUnCompress). 21 | Heartbeat([]byte(fmt.Sprintf("{\"event\": \"%s\"}", "ping")), 5*time.Second).ProtoHandleFunc(ProtoHandle).Build() 22 | t.Log(ws.Subscribe(map[string]string{ 23 | "event": "addChannel", "channel": "ok_sub_spot_btc_usdt_depth_5"})) 24 | ws.ReceiveMessage() 25 | time.Sleep(time.Minute) 26 | ws.CloseWs() 27 | } 28 | -------------------------------------------------------------------------------- /wx_pay.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudian/GoEx/ca421ea49f0649ad6713e5e6c8897a5e61888e18/wx_pay.JPG -------------------------------------------------------------------------------- /zb/Zb_test.go: -------------------------------------------------------------------------------- 1 | package zb 2 | 3 | import ( 4 | "github.com/wudian/GoEx" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | api_key = "" 11 | api_secretkey = "" 12 | zb = New(http.DefaultClient, api_key, api_secretkey) 13 | ) 14 | 15 | func TestZb_GetAccount(t *testing.T) { 16 | acc, err := zb.GetAccount() 17 | t.Log(err) 18 | t.Log(acc.SubAccounts[goex.BTC]) 19 | } 20 | 21 | func TestZb_GetTicker(t *testing.T) { 22 | ticker, _ := zb.GetTicker(goex.BCH_USD) 23 | t.Log(ticker) 24 | } 25 | 26 | func TestZb_GetDepth(t *testing.T) { 27 | dep, _ := zb.GetDepth(2, goex.BCH_USDT) 28 | t.Log(dep) 29 | } 30 | 31 | func TestZb_LimitSell(t *testing.T) { 32 | ord, err := zb.LimitSell("0.001", "75000", goex.NewCurrencyPair2("BTC_QC")) 33 | t.Log(err) 34 | t.Log(ord) 35 | } 36 | 37 | func TestZb_LimitBuy(t *testing.T) { 38 | ord, err := zb.LimitBuy("2", "4", goex.NewCurrencyPair2("1ST_QC")) 39 | t.Log(err) 40 | t.Log(ord) 41 | } 42 | 43 | func TestZb_CancelOrder(t *testing.T) { 44 | r, err := zb.CancelOrder("201802014255365", goex.NewCurrencyPair2("BTC_QC")) 45 | t.Log(err) 46 | t.Log(r) 47 | } 48 | 49 | func TestZb_GetUnfinishOrders(t *testing.T) { 50 | ords, err := zb.GetUnfinishOrders(goex.NewCurrencyPair2("1ST_QC")) 51 | t.Log(err) 52 | t.Log(ords) 53 | } 54 | 55 | func TestZb_GetOneOrder(t *testing.T) { 56 | ord, err := zb.GetOneOrder("20180201341043", goex.NewCurrencyPair2("1ST_QC")) 57 | t.Log(err) 58 | t.Log(ord) 59 | } 60 | --------------------------------------------------------------------------------