package main import ( "errors" "log" "net/url" "strconv" "strings" "time" "github.com/gorilla/websocket" ) const ( OKCOIN_API_URL = "https://www.okcoin.com/api/v1/" OKCOIN_API_URL_CHINA = "https://www.okcoin.cn/api/v1/" OKCOIN_API_VERSION = "1" OKCOIN_WEBSOCKET_URL = "wss://real.okcoin.com:10440/websocket/okcoinapi" OKCOIN_WEBSOCKET_URL_CHINA = "wss://real.okcoin.cn:10440/websocket/okcoinapi" OKCOIN_TICKER = "ticker.do" OKCOIN_DEPTH = "depth.do" OKCOIN_TRADES = "trades.do" OKCOIN_KLINE = "kline.do" OKCOIN_USERINFO = "userinfo.do" OKCOIN_TRADE = "trade.do" OKCOIN_TRADE_HISTORY = "trade_history.do" OKCOIN_TRADE_BATCH = "batch_trade.do" OKCOIN_ORDER_CANCEL = "cancel_order.do" OKCOIN_ORDER_INFO = "order_info.do" OKCOIN_ORDERS_INFO = "orders_info.do" OKCOIN_ORDER_HISTORY = "order_history.do" OKCOIN_WITHDRAW = "withdraw.do" OKCOIN_WITHDRAW_CANCEL = "cancel_withdraw.do" OKCOIN_WITHDRAW_INFO = "withdraw_info.do" OKCOIN_ORDER_FEE = "order_fee.do" OKCOIN_LEND_DEPTH = "lend_depth.do" OKCOIN_BORROWS_INFO = "borrows_info.do" OKCOIN_BORROW_MONEY = "borrow_money.do" OKCOIN_BORROW_CANCEL = "cancel_borrow.do" OKCOIN_BORROW_ORDER_INFO = "borrow_order_info.do" OKCOIN_REPAYMENT = "repayment.do" OKCOIN_UNREPAYMENTS_INFO = "unrepayments_info.do" OKCOIN_ACCOUNT_RECORDS = "account_records.do" OKCOIN_FUTURES_TICKER = "future_ticker.do" OKCOIN_FUTURES_DEPTH = "future_depth.do" OKCOIN_FUTURES_TRADES = "future_trades.do" OKCOIN_FUTURES_INDEX = "future_index.do" OKCOIN_EXCHANGE_RATE = "exchange_rate.do" OKCOIN_FUTURES_ESTIMATED_PRICE = "future_estimated_price.do" OKCOIN_FUTURES_KLINE = "future_kline.do" OKCOIN_FUTURES_HOLD_AMOUNT = "future_hold_amount.do" OKCOIN_FUTURES_USERINFO = "future_userinfo.do" OKCOIN_FUTURES_POSITION = "future_position.do" OKCOIN_FUTURES_TRADE = "future_trade.do" OKCOIN_FUTURES_TRADE_HISTORY = "future_trades_history.do" OKCOIN_FUTURES_TRADE_BATCH = "future_batch_trade.do" OKCOIN_FUTURES_CANCEL = "future_cancel.do" OKCOIN_FUTURES_ORDER_INFO = "future_order_info.do" OKCOIN_FUTURES_ORDERS_INFO = "future_orders_info.do" OKCOIN_FUTURES_USERINFO_4FIX = "future_userinfo_4fix.do" OKCOIN_FUTURES_POSITION_4FIX = "future_position_4fix.do" OKCOIN_FUTURES_EXPLOSIVE = "future_explosive.do" OKCOIN_FUTURES_DEVOLVE = "future_devolve.do" ) type OKCoin struct { Name string Enabled bool Verbose bool Websocket bool WebsocketURL string RESTPollingDelay time.Duration AuthenticatedAPISupport bool APIUrl, PartnerID, SecretKey string TakerFee, MakerFee float64 RESTErrors map[string]string WebsocketErrors map[string]string BaseCurrencies []string AvailablePairs []string EnabledPairs []string FuturesValues []string WebsocketConn *websocket.Conn } type OKCoinTicker struct { Buy float64 `json:",string"` High float64 `json:",string"` Last float64 `json:",string"` Low float64 `json:",string"` Sell float64 `json:",string"` Vol float64 `json:",string"` } type OKCoinTickerResponse struct { Date string Ticker OKCoinTicker } type OKCoinFuturesTicker struct { Last float64 Buy float64 Sell float64 High float64 Low float64 Vol float64 Contract_ID int64 Unit_Amount float64 } type OKCoinOrderbook struct { Asks [][]float64 `json:"asks"` Bids [][]float64 `json:"bids"` } type OKCoinFuturesTickerResponse struct { Date string Ticker OKCoinFuturesTicker } type OKCoinBorrowInfo struct { BorrowBTC float64 `json:"borrow_btc"` BorrowLTC float64 `json:"borrow_ltc"` BorrowCNY float64 `json:"borrow_cny"` CanBorrow float64 `json:"can_borrow"` InterestBTC float64 `json:"interest_btc"` InterestLTC float64 `json:"interest_ltc"` Result bool `json:"result"` DailyInterestBTC float64 `json:"today_interest_btc"` DailyInterestLTC float64 `json:"today_interest_ltc"` DailyInterestCNY float64 `json:"today_interest_cny"` } type OKCoinBorrowOrder struct { Amount float64 `json:"amount"` BorrowDate int64 `json:"borrow_date"` BorrowID int64 `json:"borrow_id"` Days int64 `json:"days"` TradeAmount float64 `json:"deal_amount"` Rate float64 `json:"rate"` Status int64 `json:"status"` Symbol string `json:"symbol"` } type OKCoinRecord struct { Address string `json:"addr"` Account int64 `json:"account,string"` Amount float64 `json:"amount"` Bank string `json:"bank"` BenificiaryAddress string `json:"benificiary_addr"` TransactionValue float64 `json:"transaction_value"` Fee float64 `json:"fee"` Date float64 `json:"date"` } type OKCoinAccountRecords struct { Records []OKCoinRecord `json:"records"` Symbol string `json:"symbol"` } type OKCoinFuturesOrder struct { Amount float64 `json:"amount"` ContractName string `json:"contract_name"` DateCreated float64 `json:"create_date"` TradeAmount float64 `json:"deal_amount"` Fee float64 `json:"fee"` LeverageRate float64 `json:"lever_rate"` OrderID int64 `json:"order_id"` Price float64 `json:"price"` AvgPrice float64 `json:"avg_price"` Status float64 `json:"status"` Symbol string `json:"symbol"` Type int64 `json:"type"` UnitAmount int64 `json:"unit_amount"` } type OKCoinFuturesHoldAmount struct { Amount float64 `json:"amount"` ContractName string `json:"contract_name"` } type OKCoinFuturesExplosive struct { Amount float64 `json:"amount,string"` DateCreated string `json:"create_date"` Loss float64 `json:"loss,string"` Type int64 `json:"type"` } func (o *OKCoin) SetDefaults() { o.SetErrorDefaults() o.SetWebsocketErrorDefaults() if o.APIUrl == OKCOIN_API_URL { o.Name = "OKCOIN International" o.WebsocketURL = OKCOIN_WEBSOCKET_URL } else if o.APIUrl == OKCOIN_API_URL_CHINA { o.Name = "OKCOIN China" o.WebsocketURL = OKCOIN_WEBSOCKET_URL_CHINA } o.Enabled = false o.Verbose = false o.Websocket = false o.RESTPollingDelay = 10 o.FuturesValues = []string{"this_week", "next_week", "quarter"} } func (o *OKCoin) GetName() string { return o.Name } func (o *OKCoin) SetEnabled(enabled bool) { o.Enabled = enabled } func (o *OKCoin) IsEnabled() bool { return o.Enabled } func (o *OKCoin) Setup(exch Exchanges) { if !exch.Enabled { o.SetEnabled(false) } else { o.Enabled = true o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport o.SetAPIKeys(exch.APIKey, exch.APISecret) o.RESTPollingDelay = exch.RESTPollingDelay o.Verbose = exch.Verbose o.Websocket = exch.Websocket o.BaseCurrencies = SplitStrings(exch.BaseCurrencies, ",") o.AvailablePairs = SplitStrings(exch.AvailablePairs, ",") o.EnabledPairs = SplitStrings(exch.EnabledPairs, ",") } } func (k *OKCoin) GetEnabledCurrencies() []string { return k.EnabledPairs } func (o *OKCoin) Start() { go o.Run() } func (o *OKCoin) SetURL(url string) { o.APIUrl = url } func (o *OKCoin) SetAPIKeys(apiKey, apiSecret string) { o.PartnerID = apiKey o.SecretKey = apiSecret } func (o *OKCoin) GetFee(maker bool) float64 { if o.APIUrl == OKCOIN_API_URL { if maker { return o.MakerFee } else { return o.TakerFee } } // Chinese exchange does not have any trading fees return 0 } func (o *OKCoin) Run() { if o.Verbose { log.Printf("%s Websocket: %s. (url: %s).\n", o.GetName(), IsEnabled(o.Websocket), o.WebsocketURL) log.Printf("%s polling delay: %ds.\n", o.GetName(), o.RESTPollingDelay) log.Printf("%s %d currencies enabled: %s.\n", o.GetName(), len(o.EnabledPairs), o.EnabledPairs) } if o.Websocket { go o.WebsocketClient() } for o.Enabled { for _, x := range o.EnabledPairs { currency := StringToLower(x[0:3] + "_" + x[3:]) if o.APIUrl == OKCOIN_API_URL { for _, y := range o.FuturesValues { futuresValue := y go func() { ticker, err := o.GetFuturesTicker(currency, futuresValue) if err != nil { log.Println(err) return } log.Printf("OKCoin Intl Futures %s (%s): Last %f High %f Low %f Volume %f\n", currency, futuresValue, ticker.Last, ticker.High, ticker.Low, ticker.Vol) AddExchangeInfo(o.GetName(), StringToUpper(currency[0:3]), StringToUpper(currency[4:]), ticker.Last, ticker.Vol) }() } go func() { ticker, err := o.GetTicker(currency) if err != nil { log.Println(err) return } log.Printf("OKCoin Intl Spot %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Vol) AddExchangeInfo(o.GetName(), StringToUpper(currency[0:3]), StringToUpper(currency[4:]), ticker.Last, ticker.Vol) }() } else { go func() { ticker, err := o.GetTicker(currency) if err != nil { log.Println(err) return } tickerLastUSD, _ := ConvertCurrency(ticker.Last, "CNY", "USD") tickerHighUSD, _ := ConvertCurrency(ticker.High, "CNY", "USD") tickerLowUSD, _ := ConvertCurrency(ticker.Low, "CNY", "USD") log.Printf("OKCoin China %s: Last %f (%f) High %f (%f) Low %f (%f) Volume %f\n", currency, tickerLastUSD, ticker.Last, tickerHighUSD, ticker.High, tickerLowUSD, ticker.Low, ticker.Vol) AddExchangeInfo(o.GetName(), StringToUpper(currency[0:3]), StringToUpper(currency[4:]), ticker.Last, ticker.Vol) AddExchangeInfo(o.GetName(), StringToUpper(currency[0:3]), "USD", tickerLastUSD, ticker.Vol) }() } } time.Sleep(time.Second * o.RESTPollingDelay) } } func (o *OKCoin) GetTicker(symbol string) (OKCoinTicker, error) { resp := OKCoinTickerResponse{} vals := url.Values{} vals.Set("symbol", symbol) path := EncodeURLValues(o.APIUrl+OKCOIN_TICKER, vals) err := SendHTTPGetRequest(path, true, &resp) if err != nil { return OKCoinTicker{}, err } return resp.Ticker, nil } func (o *OKCoin) GetTickerPrice(currency string) TickerPrice { var tickerPrice TickerPrice ticker, err := o.GetTicker(currency) if err != nil { log.Println(err) return tickerPrice } tickerPrice.Ask = ticker.Sell tickerPrice.Bid = ticker.Buy tickerPrice.CryptoCurrency = currency tickerPrice.Low = ticker.Low tickerPrice.Last = ticker.Last tickerPrice.Volume = ticker.Vol tickerPrice.High = ticker.High return tickerPrice } func (o *OKCoin) GetOrderBook(symbol string, size int64, merge bool) (OKCoinOrderbook, error) { resp := OKCoinOrderbook{} vals := url.Values{} vals.Set("symbol", symbol) if size != 0 { vals.Set("size", strconv.FormatInt(size, 10)) } if merge { vals.Set("merge", "1") } path := EncodeURLValues(o.APIUrl+OKCOIN_DEPTH, vals) err := SendHTTPGetRequest(path, true, &resp) if err != nil { return resp, err } return resp, nil } type OKCoinTrades struct { Amount float64 `json:"amount,string"` Date int64 `json:"date` DateMS int64 `json:"date_ms"` Price float64 `json:"price,string"` TradeID int64 `json:"tid"` Type string `json:"type"` } func (o *OKCoin) GetTrades(symbol string, since int64) ([]OKCoinTrades, error) { result := []OKCoinTrades{} vals := url.Values{} vals.Set("symbol", symbol) if since != 0 { vals.Set("since", strconv.FormatInt(since, 10)) } path := EncodeURLValues(o.APIUrl+OKCOIN_TRADES, vals) err := SendHTTPGetRequest(path, true, &result) if err != nil { return nil, err } return result, nil } func (o *OKCoin) GetKline(symbol, klineType string, size, since int64) ([]interface{}, error) { resp := []interface{}{} vals := url.Values{} vals.Set("symbol", symbol) vals.Set("type", klineType) if size != 0 { vals.Set("size", strconv.FormatInt(size, 10)) } if since != 0 { vals.Set("since", strconv.FormatInt(since, 10)) } path := EncodeURLValues(o.APIUrl+OKCOIN_KLINE, vals) err := SendHTTPGetRequest(path, true, &resp) if err != nil { return nil, err } return resp, nil } func (o *OKCoin) GetFuturesTicker(symbol, contractType string) (OKCoinFuturesTicker, error) { resp := OKCoinFuturesTickerResponse{} vals := url.Values{} vals.Set("symbol", symbol) vals.Set("contract_type", contractType) path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_TICKER, vals) err := SendHTTPGetRequest(path, true, &resp) if err != nil { return OKCoinFuturesTicker{}, err } return resp.Ticker, nil } func (o *OKCoin) GetFuturesDepth(symbol, contractType string, size int64, merge bool) (OKCoinOrderbook, error) { result := OKCoinOrderbook{} vals := url.Values{} vals.Set("symbol", symbol) vals.Set("contract_type", contractType) if size != 0 { vals.Set("size", strconv.FormatInt(size, 10)) } if merge { vals.Set("merge", "1") } path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_DEPTH, vals) err := SendHTTPGetRequest(path, true, &result) if err != nil { return result, err } return result, nil } type OKCoinFuturesTrades struct { Amount float64 `json:"amount"` Date int64 `json:"date"` DateMS int64 `json:"date_ms"` Price float64 `json:"price"` TradeID int64 `json:"tid"` Type string `json:"type"` } func (o *OKCoin) GetFuturesTrades(symbol, contractType string) ([]OKCoinFuturesTrades, error) { result := []OKCoinFuturesTrades{} vals := url.Values{} vals.Set("symbol", symbol) vals.Set("contract_type", contractType) path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_TRADES, vals) err := SendHTTPGetRequest(path, true, &result) if err != nil { return nil, err } return result, nil } func (o *OKCoin) GetFuturesIndex(symbol string) (float64, error) { type Response struct { Index float64 `json:"future_index"` } result := Response{} vals := url.Values{} vals.Set("symbol", symbol) path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_INDEX, vals) err := SendHTTPGetRequest(path, true, &result) if err != nil { return 0, err } return result.Index, nil } func (o *OKCoin) GetFuturesExchangeRate() (float64, error) { type Response struct { Rate float64 `json:"rate"` } result := Response{} err := SendHTTPGetRequest(o.APIUrl+OKCOIN_EXCHANGE_RATE, true, &result) if err != nil { return result.Rate, err } return result.Rate, nil } func (o *OKCoin) GetFuturesEstimatedPrice(symbol string) (float64, error) { type Response struct { Price float64 `json:"forecast_price"` } result := Response{} vals := url.Values{} vals.Set("symbol", symbol) path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_ESTIMATED_PRICE, vals) err := SendHTTPGetRequest(path, true, &result) if err != nil { return result.Price, err } return result.Price, nil } func (o *OKCoin) GetFuturesKline(symbol, klineType, contractType string, size, since int64) ([]interface{}, error) { resp := []interface{}{} vals := url.Values{} vals.Set("symbol", symbol) vals.Set("type", klineType) vals.Set("contract_type", contractType) if size != 0 { vals.Set("size", strconv.FormatInt(size, 10)) } if since != 0 { vals.Set("since", strconv.FormatInt(since, 10)) } path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_KLINE, vals) err := SendHTTPGetRequest(path, true, &resp) if err != nil { return nil, err } return resp, nil } func (o *OKCoin) GetFuturesHoldAmount(symbol, contractType string) ([]OKCoinFuturesHoldAmount, error) { resp := []OKCoinFuturesHoldAmount{} vals := url.Values{} vals.Set("symbol", symbol) vals.Set("contract_type", contractType) path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_HOLD_AMOUNT, vals) err := SendHTTPGetRequest(path, true, &resp) if err != nil { return nil, err } return resp, nil } func (o *OKCoin) GetFuturesExplosive(symbol, contractType string, status, currentPage, pageLength int64) ([]OKCoinFuturesExplosive, error) { type Response struct { Data []OKCoinFuturesExplosive `json:"data"` } resp := Response{} vals := url.Values{} vals.Set("symbol", symbol) vals.Set("contract_type", contractType) vals.Set("status", strconv.FormatInt(status, 10)) vals.Set("current_page", strconv.FormatInt(currentPage, 10)) vals.Set("page_length", strconv.FormatInt(pageLength, 10)) path := EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_EXPLOSIVE, vals) err := SendHTTPGetRequest(path, true, &resp) if err != nil { return nil, err } return resp.Data, nil } type OKCoinUserInfo struct { Info struct { Funds struct { Asset struct { Net float64 `json:"net,string"` Total float64 `json:"total,string"` } `json:"asset"` Borrow struct { BTC float64 `json:"btc,string"` LTC float64 `json:"ltc,string"` USD float64 `json:"usd,string"` } `json:"borrow"` Free struct { BTC float64 `json:"btc,string"` LTC float64 `json:"ltc,string"` USD float64 `json:"usd,string"` } `json:"free"` Freezed struct { BTC float64 `json:"btc,string"` LTC float64 `json:"ltc,string"` USD float64 `json:"usd,string"` } `json:"freezed"` UnionFund struct { BTC float64 `json:"btc,string"` LTC float64 `json:"ltc,string"` } `json:"union_fund"` } `json:"funds"` } `json:"info"` Result bool `json:"result"` } func (o *OKCoin) GetUserInfo() (OKCoinUserInfo, error) { result := OKCoinUserInfo{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_USERINFO, url.Values{}, &result) if err != nil { return result, err } return result, nil } func (o *OKCoin) Trade(amount, price float64, symbol, orderType string) (int64, error) { type Response struct { Result bool `json:"result"` OrderID int64 `json:"order_id"` } v := url.Values{} v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) v.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) v.Set("symbol", symbol) v.Set("type", orderType) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_TRADE, v, &result) if err != nil { return 0, err } if !result.Result { return 0, errors.New("Unable to place order.") } return result.OrderID, nil } func (o *OKCoin) GetTradeHistory(symbol string, TradeID int64) ([]OKCoinTrades, error) { result := []OKCoinTrades{} v := url.Values{} v.Set("symbol", symbol) v.Set("since", strconv.FormatInt(TradeID, 10)) err := o.SendAuthenticatedHTTPRequest(OKCOIN_TRADE_HISTORY, v, &result) if err != nil { return nil, err } return result, nil } type OKCoinBatchTrade struct { OrderInfo []struct { OrderID int64 `json:"order_id"` ErrorCode int64 `json:"error_code"` } `json:"order_info"` Result bool `json:"result"` } func (o *OKCoin) BatchTrade(orderData string, symbol, orderType string) (OKCoinBatchTrade, error) { v := url.Values{} v.Set("orders_data", orderData) v.Set("symbol", symbol) v.Set("type", orderType) result := OKCoinBatchTrade{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_TRADE_BATCH, v, &result) if err != nil { return result, err } return result, nil } type OKCoinCancelOrderResponse struct { Success string Error string } func (o *OKCoin) CancelOrder(orderID []int64, symbol string) (OKCoinCancelOrderResponse, error) { v := url.Values{} orders := []string{} orderStr := "" result := OKCoinCancelOrderResponse{} if len(orderID) > 1 { for x := range orderID { orders = append(orders, strconv.FormatInt(orderID[x], 10)) } orderStr = JoinStrings(orders, ",") } else { orderStr = strconv.FormatInt(orderID[0], 10) } v.Set("order_id", orderStr) v.Set("symbol", symbol) err := o.SendAuthenticatedHTTPRequest(OKCOIN_ORDER_CANCEL, v, &result) if err != nil { return result, err } return result, nil } type OKCoinOrderInfo struct { Amount float64 `json:"amount"` AvgPrice float64 `json:"avg_price"` Created int64 `json:"create_date"` DealAmount float64 `json:"deal_amount"` OrderID int64 `json:"order_id"` OrdersID int64 `json:"orders_id"` Price float64 `json:"price"` Status int `json:"status"` Symbol string `json:"symbol"` Type string `json:"type"` } func (o *OKCoin) GetOrderInfo(orderID int64, symbol string) ([]OKCoinOrderInfo, error) { type Response struct { Result bool `json:"result"` Orders []OKCoinOrderInfo `json:"orders"` } v := url.Values{} v.Set("symbol", symbol) v.Set("order_id", strconv.FormatInt(orderID, 10)) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_ORDER_INFO, v, &result) if err != nil { return nil, err } if result.Result != true { return nil, errors.New("Unable to retrieve order info.") } return result.Orders, nil } func (o *OKCoin) GetOrderInfoBatch(orderID []int64, symbol string) ([]OKCoinOrderInfo, error) { type Response struct { Result bool `json:"result"` Orders []OKCoinOrderInfo `json:"orders"` } orders := []string{} for x := range orderID { orders = append(orders, strconv.FormatInt(orderID[x], 10)) } v := url.Values{} v.Set("symbol", symbol) v.Set("order_id", JoinStrings(orders, ",")) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_ORDER_INFO, v, &result) if err != nil { return nil, err } if result.Result != true { return nil, errors.New("Unable to retrieve order info.") } return result.Orders, nil } type OKCoinOrderHistory struct { CurrentPage int `json:"current_page"` Orders []OKCoinOrderInfo `json:"orders"` PageLength int `json:"page_length"` Result bool `json:"result"` Total int `json:"total"` } func (o *OKCoin) GetOrderHistory(pageLength, currentPage int64, status, symbol string) (OKCoinOrderHistory, error) { v := url.Values{} v.Set("symbol", symbol) v.Set("status", status) v.Set("current_page", strconv.FormatInt(currentPage, 10)) v.Set("page_length", strconv.FormatInt(pageLength, 10)) result := OKCoinOrderHistory{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_ORDER_HISTORY, v, &result) if err != nil { return result, err } return result, nil } type OKCoinWithdrawalResponse struct { WithdrawID int `json:"withdraw_id"` Result bool `json:"result"` } func (o *OKCoin) Withdrawal(symbol string, fee float64, tradePWD, address string, amount float64) (int, error) { v := url.Values{} v.Set("symbol", symbol) if fee != 0 { v.Set("chargefee", strconv.FormatFloat(fee, 'f', -1, 64)) } v.Set("trade_pwd", tradePWD) v.Set("withdraw_address", address) v.Set("withdraw_amount", strconv.FormatFloat(amount, 'f', -1, 64)) result := OKCoinWithdrawalResponse{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_WITHDRAW, v, &result) if err != nil { return 0, err } if !result.Result { return 0, errors.New("Unable to process withdrawal request.") } return result.WithdrawID, nil } func (o *OKCoin) CancelWithdrawal(symbol string, withdrawalID int64) (int, error) { v := url.Values{} v.Set("symbol", symbol) v.Set("withdrawal_id", strconv.FormatInt(withdrawalID, 10)) result := OKCoinWithdrawalResponse{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_WITHDRAW_CANCEL, v, &result) if err != nil { return 0, err } if !result.Result { return 0, errors.New("Unable to process withdrawal cancel request.") } return result.WithdrawID, nil } type OKCoinWithdrawInfo struct { Address string `json:"address"` Amount float64 `json:"amount"` Created int64 `json:"created_date"` ChargeFee float64 `json:"chargefee"` Status int `json:"status"` WithdrawID int64 `json:"withdraw_id"` } func (o *OKCoin) GetWithdrawalInfo(symbol string, withdrawalID int64) ([]OKCoinWithdrawInfo, error) { type Response struct { Result bool Withdraw []OKCoinWithdrawInfo `json:"withdraw"` } v := url.Values{} v.Set("symbol", symbol) v.Set("withdrawal_id", strconv.FormatInt(withdrawalID, 10)) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_WITHDRAW_INFO, v, &result) if err != nil { return nil, err } if !result.Result { return nil, errors.New("Unable to process withdrawal cancel request.") } return result.Withdraw, nil } type OKCoinOrderFeeInfo struct { Fee float64 `json:"fee,string"` OrderID int64 `json:"order_id"` Type string `json:"type"` } func (o *OKCoin) GetOrderFeeInfo(symbol string, orderID int64) (OKCoinOrderFeeInfo, error) { type Response struct { Data OKCoinOrderFeeInfo `json:"data"` Result bool `json:"result"` } v := url.Values{} v.Set("symbol", symbol) v.Set("order_id", strconv.FormatInt(orderID, 10)) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_ORDER_FEE, v, &result) if err != nil { return result.Data, err } if !result.Result { return result.Data, errors.New("Unable to get order fee info.") } return result.Data, nil } type OKCoinLendDepth struct { Amount float64 `json:"amount"` Days string `json:"days"` Num int64 `json:"num"` Rate float64 `json:"rate,string"` } func (o *OKCoin) GetLendDepth(symbol string) ([]OKCoinLendDepth, error) { type Response struct { LendDepth []OKCoinLendDepth `json:"lend_depth"` } v := url.Values{} v.Set("symbol", symbol) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_LEND_DEPTH, v, &result) if err != nil { return nil, err } return result.LendDepth, nil } func (o *OKCoin) GetBorrowInfo(symbol string) (OKCoinBorrowInfo, error) { v := url.Values{} v.Set("symbol", symbol) result := OKCoinBorrowInfo{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_BORROWS_INFO, v, &result) if err != nil { return result, nil } return result, nil } type OKCoinBorrowResponse struct { Result bool `json:"result"` BorrowID int `json:"borrow_id"` } func (o *OKCoin) Borrow(symbol, days string, amount, rate float64) (int, error) { v := url.Values{} v.Set("symbol", symbol) v.Set("days", days) v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) v.Set("rate", strconv.FormatFloat(rate, 'f', -1, 64)) result := OKCoinBorrowResponse{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_BORROW_MONEY, v, &result) if err != nil { return 0, err } if !result.Result { return 0, errors.New("Unable to borrow.") } return result.BorrowID, nil } func (o *OKCoin) CancelBorrow(symbol string, borrowID int64) (bool, error) { v := url.Values{} v.Set("symbol", symbol) v.Set("borrow_id", strconv.FormatInt(borrowID, 10)) result := OKCoinBorrowResponse{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_BORROW_CANCEL, v, &result) if err != nil { return false, err } if !result.Result { return false, errors.New("Unable to cancel borrow.") } return true, nil } func (o *OKCoin) GetBorrowOrderInfo(borrowID int64) (OKCoinBorrowInfo, error) { type Response struct { Result bool `json:"result"` BorrowOrder OKCoinBorrowInfo `json:"borrow_order"` } v := url.Values{} v.Set("borrow_id", strconv.FormatInt(borrowID, 10)) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_BORROW_ORDER_INFO, v, &result) if err != nil { return result.BorrowOrder, err } if !result.Result { return result.BorrowOrder, errors.New("Unable to get borrow info.") } return result.BorrowOrder, nil } func (o *OKCoin) GetRepaymentInfo(borrowID int64) (bool, error) { v := url.Values{} v.Set("borrow_id", strconv.FormatInt(borrowID, 10)) result := OKCoinBorrowResponse{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_REPAYMENT, v, &result) if err != nil { return false, err } if !result.Result { return false, errors.New("Unable to get repayment info.") } return true, nil } func (o *OKCoin) GetUnrepaymentsInfo(symbol string, currentPage, pageLength int) ([]OKCoinBorrowOrder, error) { type Response struct { Unrepayments []OKCoinBorrowOrder `json:"unrepayments"` Result bool `json:"result"` } v := url.Values{} v.Set("symbol", symbol) v.Set("current_page", strconv.Itoa(currentPage)) v.Set("page_length", strconv.Itoa(pageLength)) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_UNREPAYMENTS_INFO, v, &result) if err != nil { return nil, err } if !result.Result { return nil, errors.New("Unable to get unrepayments info.") } return result.Unrepayments, nil } func (o *OKCoin) GetAccountRecords(symbol string, recType, currentPage, pageLength int) ([]OKCoinAccountRecords, error) { type Response struct { Records []OKCoinAccountRecords `json:"records"` Symbol string `json:"symbol"` } v := url.Values{} v.Set("symbol", symbol) v.Set("type", strconv.Itoa(recType)) v.Set("current_page", strconv.Itoa(currentPage)) v.Set("page_length", strconv.Itoa(pageLength)) result := Response{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_ACCOUNT_RECORDS, v, &result) if err != nil { return nil, err } return result.Records, nil } func (o *OKCoin) GetFuturesUserInfo() { err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_USERINFO, url.Values{}, nil) if err != nil { log.Println(err) } } func (o *OKCoin) GetFuturesPosition(symbol, contractType string) { v := url.Values{} v.Set("symbol", symbol) v.Set("contract_type", contractType) err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_POSITION, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) FuturesTrade(amount, price float64, matchPrice, leverage int64, symbol, contractType, orderType string) { v := url.Values{} v.Set("symbol", symbol) v.Set("contract_type", contractType) v.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) v.Set("type", orderType) v.Set("match_price", strconv.FormatInt(matchPrice, 10)) v.Set("lever_rate", strconv.FormatInt(leverage, 10)) err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_TRADE, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) FuturesBatchTrade(orderData, symbol, contractType string, leverage int64, orderType string) { v := url.Values{} //to-do batch trade support for orders_data) v.Set("symbol", symbol) v.Set("contract_type", contractType) v.Set("orders_data", orderData) v.Set("lever_rate", strconv.FormatInt(leverage, 10)) err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_TRADE_BATCH, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) CancelFuturesOrder(orderID int64, symbol, contractType string) { v := url.Values{} v.Set("symbol", symbol) v.Set("contract_type", contractType) v.Set("order_id", strconv.FormatInt(orderID, 10)) err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_CANCEL, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) GetFuturesOrderInfo(orderID, status, currentPage, pageLength int64, symbol, contractType string) { v := url.Values{} v.Set("symbol", symbol) v.Set("contract_type", contractType) v.Set("status", strconv.FormatInt(status, 10)) v.Set("order_id", strconv.FormatInt(orderID, 10)) v.Set("current_page", strconv.FormatInt(currentPage, 10)) v.Set("page_length", strconv.FormatInt(pageLength, 10)) err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_ORDER_INFO, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) GetFutureOrdersInfo(orderID int64, contractType, symbol string) { v := url.Values{} v.Set("order_id", strconv.FormatInt(orderID, 10)) v.Set("contract_type", contractType) v.Set("symbol", symbol) err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_ORDERS_INFO, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) GetFuturesUserInfo4Fix() { v := url.Values{} err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_USERINFO_4FIX, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) GetFuturesUserPosition4Fix(symbol, contractType string) { v := url.Values{} v.Set("symbol", symbol) v.Set("contract_type", contractType) v.Set("type", strconv.FormatInt(1, 10)) err := o.SendAuthenticatedHTTPRequest(OKCOIN_FUTURES_POSITION_4FIX, v, nil) if err != nil { log.Println(err) } } func (o *OKCoin) SendAuthenticatedHTTPRequest(method string, v url.Values, result interface{}) (err error) { v.Set("api_key", o.PartnerID) hasher := GetMD5([]byte(v.Encode() + "&secret_key=" + o.SecretKey)) v.Set("sign", strings.ToUpper(HexEncodeToString(hasher))) encoded := v.Encode() path := o.APIUrl + method if o.Verbose { log.Printf("Sending POST request to %s with params %s\n", path, encoded) } headers := make(map[string]string) headers["Content-Type"] = "application/x-www-form-urlencoded" resp, err := SendHTTPRequest("POST", path, headers, strings.NewReader(encoded)) if err != nil { return err } if o.Verbose { log.Printf("Recieved raw: \n%s\n", resp) } err = JSONDecode([]byte(resp), &result) if err != nil { return errors.New("Unable to JSON Unmarshal response.") } return nil } func (o *OKCoin) SetErrorDefaults() { o.RESTErrors = map[string]string{ "10000": "Required field, can not be null", "10001": "Request frequency too high", "10002": "System error", "10003": "Not in reqest list, please try again later", "10004": "IP not allowed to access the resource", "10005": "'secretKey' does not exist", "10006": "'partner' does not exist", "10007": "Signature does not match", "10008": "Illegal parameter", "10009": "Order does not exist", "10010": "Insufficient funds", "10011": "Amount too low", "10012": "Only btc_usd/btc_cny ltc_usd,ltc_cny supported", "10013": "Only support https request", "10014": "Order price must be between 0 and 1,000,000", "10015": "Order price differs from current market price too much", "10016": "Insufficient coins balance", "10017": "API authorization error", "10018": "Borrow amount less than lower limit [usd/cny:100,btc:0.1,ltc:1]", "10019": "Loan agreement not checked", "10020": `Rate cannot exceed 1%`, "10021": `Rate cannot less than 0.01%`, "10023": "Fail to get latest ticker", "10024": "Balance not sufficient", "10025": "Quota is full, cannot borrow temporarily", "10026": "Loan (including reserved loan) and margin cannot be withdrawn", "10027": "Cannot withdraw within 24 hrs of authentication information modification", "10028": "Withdrawal amount exceeds daily limit", "10029": "Account has unpaid loan, please cancel/pay off the loan before withdraw", "10031": "Deposits can only be withdrawn after 6 confirmations", "10032": "Please enabled phone/google authenticator", "10033": "Fee higher than maximum network transaction fee", "10034": "Fee lower than minimum network transaction fee", "10035": "Insufficient BTC/LTC", "10036": "Withdrawal amount too low", "10037": "Trade password not set", "10040": "Withdrawal cancellation fails", "10041": "Withdrawal address not approved", "10042": "Admin password error", "10043": "Account equity error, withdrawal failure", "10044": "fail to cancel borrowing order", "10047": "This function is disabled for sub-account", "10100": "User account frozen", "10216": "Non-available API", "20001": "User does not exist", "20002": "Account frozen", "20003": "Account frozen due to liquidation", "20004": "Futures account frozen", "20005": "User futures account does not exist", "20006": "Required field missing", "20007": "Illegal parameter", "20008": "Futures account balance is too low", "20009": "Future contract status error", "20010": "Risk rate ratio does not exist", "20011": `Risk rate higher than 90% before opening position`, "20012": `Risk rate higher than 90% after opening position`, "20013": "Temporally no counter party price", "20014": "System error", "20015": "Order does not exist", "20016": "Close amount bigger than your open positions", "20017": "Not authorized/illegal operation", "20018": `Order price differ more than 5% from the price in the last minute`, "20019": "IP restricted from accessing the resource", "20020": "secretKey does not exist", "20021": "Index information does not exist", "20022": "Wrong API interface (Cross margin mode shall call cross margin API, fixed margin mode shall call fixed margin API)", "20023": "Account in fixed-margin mode", "20024": "Signature does not match", "20025": "Leverage rate error", "20026": "API Permission Error", "20027": "No transaction record", "20028": "No such contract", } }