Package exchanges

This commit is contained in:
Adrian Gallagher
2017-03-21 01:50:07 +11:00
parent bbe660b5f4
commit aeb327c80c
68 changed files with 3808 additions and 3621 deletions

View File

@@ -0,0 +1,391 @@
package okcoin
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"`
}
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"`
}
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"`
}
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"`
}
type OKCoinBatchTrade struct {
OrderInfo []struct {
OrderID int64 `json:"order_id"`
ErrorCode int64 `json:"error_code"`
} `json:"order_info"`
Result bool `json:"result"`
}
type OKCoinCancelOrderResponse struct {
Success string
Error string
}
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"`
}
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"`
}
type OKCoinWithdrawalResponse struct {
WithdrawID int `json:"withdraw_id"`
Result bool `json:"result"`
}
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"`
}
type OKCoinOrderFeeInfo struct {
Fee float64 `json:"fee,string"`
OrderID int64 `json:"order_id"`
Type string `json:"type"`
}
type OKCoinLendDepth struct {
Amount float64 `json:"amount"`
Days string `json:"days"`
Num int64 `json:"num"`
Rate float64 `json:"rate,string"`
}
type OKCoinBorrowResponse struct {
Result bool `json:"result"`
BorrowID int `json:"borrow_id"`
}
type OKCoinWebsocketFutureIndex struct {
FutureIndex float64 `json:"futureIndex"`
Timestamp int64 `json:"timestamp,string"`
}
type OKCoinWebsocketTicker struct {
Timestamp float64
Vol string
Buy float64
High float64
Last float64
Low float64
Sell float64
}
type OKCoinWebsocketFuturesTicker struct {
Buy float64 `json:"buy"`
ContractID string `json:"contractId"`
High float64 `json:"high"`
HoldAmount float64 `json:"hold_amount"`
Last float64 `json:"last,string"`
Low float64 `json:"low"`
Sell float64 `json:"sell"`
UnitAmount float64 `json:"unitAmount"`
Volume float64 `json:"vol,string"`
}
type OKCoinWebsocketOrderbook struct {
Asks [][]float64 `json:"asks"`
Bids [][]float64 `json:"bids"`
Timestamp int64 `json:"timestamp,string"`
}
type OKCoinWebsocketUserinfo struct {
Info struct {
Funds struct {
Asset struct {
Net float64 `json:"net,string"`
Total float64 `json:"total,string"`
} `json:"asset"`
Free struct {
BTC float64 `json:"btc,string"`
LTC float64 `json:"ltc,string"`
USD float64 `json:"usd,string"`
CNY float64 `json:"cny,string"`
} `json:"free"`
Frozen struct {
BTC float64 `json:"btc,string"`
LTC float64 `json:"ltc,string"`
USD float64 `json:"usd,string"`
CNY float64 `json:"cny,string"`
} `json:"freezed"`
} `json:"funds"`
} `json:"info"`
Result bool `json:"result"`
}
type OKCoinWebsocketFuturesContract struct {
Available float64 `json:"available"`
Balance float64 `json:"balance"`
Bond float64 `json:"bond"`
ContractID float64 `json:"contract_id"`
ContractType string `json:"contract_type"`
Frozen float64 `json:"freeze"`
Profit float64 `json:"profit"`
Loss float64 `json:"unprofit"`
}
type OKCoinWebsocketFuturesUserInfo struct {
Info struct {
BTC struct {
Balance float64 `json:"balance"`
Contracts []OKCoinWebsocketFuturesContract `json:"contracts"`
Rights float64 `json:"rights"`
} `json:"btc"`
LTC struct {
Balance float64 `json:"balance"`
Contracts []OKCoinWebsocketFuturesContract `json:"contracts"`
Rights float64 `json:"rights"`
} `json:"ltc"`
} `json:"info"`
Result bool `json:"result"`
}
type OKCoinWebsocketOrder struct {
Amount float64 `json:"amount"`
AvgPrice float64 `json:"avg_price"`
DateCreated float64 `json:"create_date"`
TradeAmount float64 `json:"deal_amount"`
OrderID float64 `json:"order_id"`
OrdersID float64 `json:"orders_id"`
Price float64 `json:"price"`
Status int64 `json:"status"`
Symbol string `json:"symbol"`
OrderType string `json:"type"`
}
type OKCoinWebsocketFuturesOrder struct {
Amount float64 `json:"amount"`
ContractName string `json:"contract_name"`
DateCreated float64 `json:"createdDate"`
TradeAmount float64 `json:"deal_amount"`
Fee float64 `json:"fee"`
LeverageAmount int `json:"lever_rate"`
OrderID float64 `json:"order_id"`
Price float64 `json:"price"`
AvgPrice float64 `json:"avg_price"`
Status int `json:"status"`
Symbol string `json:"symbol"`
TradeType int `json:"type"`
UnitAmount float64 `json:"unit_amount"`
}
type OKCoinWebsocketRealtrades struct {
AveragePrice float64 `json:"averagePrice,string"`
CompletedTradeAmount float64 `json:"completedTradeAmount,string"`
DateCreated float64 `json:"createdDate"`
ID float64 `json:"id"`
OrderID float64 `json:"orderId"`
SigTradeAmount float64 `json:"sigTradeAmount,string"`
SigTradePrice float64 `json:"sigTradePrice,string"`
Status int64 `json:"status"`
Symbol string `json:"symbol"`
TradeAmount float64 `json:"tradeAmount,string"`
TradePrice float64 `json:"buy,string"`
TradeType string `json:"tradeType"`
TradeUnitPrice float64 `json:"tradeUnitPrice,string"`
UnTrade float64 `json:"unTrade,string"`
}
type OKCoinWebsocketFuturesRealtrades struct {
Amount float64 `json:"amount,string"`
ContractID float64 `json:"contract_id,string"`
ContractName string `json:"contract_name"`
ContractType string `json:"contract_type"`
TradeAmount float64 `json:"deal_amount,string"`
Fee float64 `json:"fee,string"`
OrderID float64 `json:"orderid"`
Price float64 `json:"price,string"`
AvgPrice float64 `json:"price_avg,string"`
Status int `json:"status,string"`
TradeType int `json:"type,string"`
UnitAmount float64 `json:"unit_amount,string"`
LeverageAmount int `json:"lever_rate,string"`
}
type OKCoinWebsocketEvent struct {
Event string `json:"event"`
Channel string `json:"channel"`
}
type OKCoinWebsocketResponse struct {
Channel string `json:"channel"`
Data interface{} `json:"data"`
}
type OKCoinWebsocketEventAuth struct {
Event string `json:"event"`
Channel string `json:"channel"`
Parameters map[string]string `json:"parameters"`
}
type OKCoinWebsocketEventAuthRemove struct {
Event string `json:"event"`
Channel string `json:"channel"`
Parameters map[string]string `json:"parameters"`
}
type OKCoinWebsocketTradeOrderResponse struct {
OrderID int64 `json:"order_id,string"`
Result bool `json:"result,string"`
}

View File

@@ -0,0 +1,102 @@
package okcoin
import (
"log"
"time"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/exchanges"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
)
func (o *OKCoin) Start() {
go o.Run()
}
func (o *OKCoin) Run() {
if o.Verbose {
log.Printf("%s Websocket: %s. (url: %s).\n", o.GetName(), common.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 := common.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(), common.StringToUpper(currency[0:3]), common.StringToUpper(currency[4:]), ticker.Last, ticker.Vol)
}()
}
go func() {
ticker, err := o.GetTickerPrice(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.Volume)
//AddExchangeInfo(o.GetName(), common.StringToUpper(currency[0:3]), common.StringToUpper(currency[4:]), ticker.Last, ticker.Volume)
}()
} else {
go func() {
ticker, err := o.GetTickerPrice(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.Volume)
log.Printf("OKCoin China %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
//AddExchangeInfo(o.GetName(), common.StringToUpper(currency[0:3]), common.StringToUpper(currency[4:]), ticker.Last, ticker.Volume)
//AddExchangeInfo(o.GetName(), common.StringToUpper(currency[0:3]), "USD", tickerLastUSD, ticker.Volume)
}()
}
}
time.Sleep(time.Second * o.RESTPollingDelay)
}
}
func (o *OKCoin) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
tickerNew, err := ticker.GetTicker(o.GetName(), currency[0:3], currency[3:])
if err == nil {
return tickerNew, nil
}
var tickerPrice ticker.TickerPrice
tick, err := o.GetTicker(currency)
if err != nil {
return tickerPrice, err
}
tickerPrice.Ask = tick.Sell
tickerPrice.Bid = tick.Buy
tickerPrice.FirstCurrency = common.StringToUpper(currency[0:3])
tickerPrice.SecondCurrency = common.StringToUpper(currency[4:])
tickerPrice.Low = tick.Low
tickerPrice.Last = tick.Last
tickerPrice.Volume = tick.Vol
tickerPrice.High = tick.High
ticker.ProcessTicker(o.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
return tickerPrice, nil
}
//TODO support for retrieving holdings from OKCOIN
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the OKCoin exchange
func (e *OKCoin) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
var response exchange.ExchangeAccountInfo
response.ExchangeName = e.GetName()
return response, nil
}

View File

@@ -0,0 +1,988 @@
package okcoin
import (
"errors"
"log"
"net/url"
"strconv"
"strings"
"github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/exchanges"
)
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"
)
var (
okcoinDefaultsSet = false
)
type OKCoin struct {
exchange.ExchangeBase
RESTErrors map[string]string
WebsocketErrors map[string]string
FuturesValues []string
WebsocketConn *websocket.Conn
}
func (o *OKCoin) SetDefaults() {
o.SetErrorDefaults()
o.SetWebsocketErrorDefaults()
o.Enabled = false
o.Verbose = false
o.Websocket = false
o.RESTPollingDelay = 10
o.FuturesValues = []string{"this_week", "next_week", "quarter"}
if !okcoinDefaultsSet {
o.APIUrl = OKCOIN_API_URL
o.Name = "OKCOIN International"
o.WebsocketURL = OKCOIN_WEBSOCKET_URL
okcoinDefaultsSet = true
} else {
o.APIUrl = OKCOIN_API_URL_CHINA
o.Name = "OKCOIN China"
o.WebsocketURL = OKCOIN_WEBSOCKET_URL_CHINA
}
}
func (o *OKCoin) Setup(exch config.ExchangeConfig) {
if !exch.Enabled {
o.SetEnabled(false)
} else {
o.Enabled = true
o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
o.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
o.RESTPollingDelay = exch.RESTPollingDelay
o.Verbose = exch.Verbose
o.Websocket = exch.Websocket
o.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
o.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
o.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
}
}
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) GetTicker(symbol string) (OKCoinTicker, error) {
resp := OKCoinTickerResponse{}
vals := url.Values{}
vals.Set("symbol", symbol)
path := common.EncodeURLValues(o.APIUrl+OKCOIN_TICKER, vals)
err := common.SendHTTPGetRequest(path, true, &resp)
if err != nil {
return OKCoinTicker{}, err
}
return resp.Ticker, nil
}
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 := common.EncodeURLValues(o.APIUrl+OKCOIN_DEPTH, vals)
err := common.SendHTTPGetRequest(path, true, &resp)
if err != nil {
return resp, err
}
return resp, nil
}
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 := common.EncodeURLValues(o.APIUrl+OKCOIN_TRADES, vals)
err := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_KLINE, vals)
err := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_TICKER, vals)
err := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_DEPTH, vals)
err := common.SendHTTPGetRequest(path, true, &result)
if err != nil {
return result, err
}
return result, nil
}
func (o *OKCoin) GetFuturesTrades(symbol, contractType string) ([]OKCoinFuturesTrades, error) {
result := []OKCoinFuturesTrades{}
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("contract_type", contractType)
path := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_TRADES, vals)
err := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_INDEX, vals)
err := common.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 := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_ESTIMATED_PRICE, vals)
err := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_KLINE, vals)
err := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_HOLD_AMOUNT, vals)
err := common.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 := common.EncodeURLValues(o.APIUrl+OKCOIN_FUTURES_EXPLOSIVE, vals)
err := common.SendHTTPGetRequest(path, true, &resp)
if err != nil {
return nil, err
}
return resp.Data, nil
}
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
}
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
}
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 = common.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
}
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", common.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
}
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
}
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
}
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
}
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
}
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
}
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.APIKey)
hasher := common.GetMD5([]byte(v.Encode() + "&secret_key=" + o.APISecret))
v.Set("sign", strings.ToUpper(common.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 := common.SendHTTPRequest("POST", path, headers, strings.NewReader(encoded))
if err != nil {
return err
}
if o.Verbose {
log.Printf("Recieved raw: \n%s\n", resp)
}
err = common.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",
}
}

View File

@@ -0,0 +1,558 @@
package okcoin
import (
"fmt"
"log"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
)
const (
OKCOIN_WEBSOCKET_USD_REALTRADES = "ok_usd_realtrades"
OKCOIN_WEBSOCKET_CNY_REALTRADES = "ok_cny_realtrades"
OKCOIN_WEBSOCKET_SPOTUSD_TRADE = "ok_spotusd_trade"
OKCOIN_WEBSOCKET_SPOTCNY_TRADE = "ok_spotcny_trade"
OKCOIN_WEBSOCKET_SPOTUSD_CANCEL_ORDER = "ok_spotusd_cancel_order"
OKCOIN_WEBSOCKET_SPOTCNY_CANCEL_ORDER = "ok_spotcny_cancel_order"
OKCOIN_WEBSOCKET_SPOTUSD_USERINFO = "ok_spotusd_userinfo"
OKCOIN_WEBSOCKET_SPOTCNY_USERINFO = "ok_spotcny_userinfo"
OKCOIN_WEBSOCKET_SPOTUSD_ORDER_INFO = "ok_spotusd_order_info"
OKCOIN_WEBSOCKET_SPOTCNY_ORDER_INFO = "ok_spotcny_order_info"
OKCOIN_WEBSOCKET_FUTURES_TRADE = "ok_futuresusd_trade"
OKCOIN_WEBSOCKET_FUTURES_CANCEL_ORDER = "ok_futuresusd_cancel_order"
OKCOIN_WEBSOCKET_FUTURES_REALTRADES = "ok_usd_future_realtrades"
OKCOIN_WEBSOCKET_FUTURES_USERINFO = "ok_futureusd_userinfo"
OKCOIN_WEBSOCKET_FUTURES_ORDER_INFO = "ok_futureusd_order_info"
)
func (o *OKCoin) PingHandler(message string) error {
err := o.WebsocketConn.WriteControl(websocket.PingMessage, []byte("{'event':'ping'}"), time.Now().Add(time.Second))
if err != nil {
log.Println(err)
return err
}
return nil
}
func (o *OKCoin) AddChannel(channel string) {
event := OKCoinWebsocketEvent{"addChannel", channel}
json, err := common.JSONEncode(event)
if err != nil {
log.Println(err)
return
}
err = o.WebsocketConn.WriteMessage(websocket.TextMessage, json)
if err != nil {
log.Println(err)
return
}
if o.Verbose {
log.Printf("%s Adding channel: %s\n", o.GetName(), channel)
}
}
func (o *OKCoin) RemoveChannel(channel string) {
event := OKCoinWebsocketEvent{"removeChannel", channel}
json, err := common.JSONEncode(event)
if err != nil {
log.Println(err)
return
}
err = o.WebsocketConn.WriteMessage(websocket.TextMessage, json)
if err != nil {
log.Println(err)
return
}
if o.Verbose {
log.Printf("%s Removing channel: %s\n", o.GetName(), channel)
}
}
func (o *OKCoin) WebsocketSpotTrade(symbol, orderType string, price, amount float64) {
values := make(map[string]string)
values["symbol"] = symbol
values["type"] = orderType
values["price"] = strconv.FormatFloat(price, 'f', -1, 64)
values["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
channel := ""
if o.WebsocketURL == OKCOIN_WEBSOCKET_URL_CHINA {
channel = OKCOIN_WEBSOCKET_SPOTCNY_TRADE
} else {
channel = OKCOIN_WEBSOCKET_SPOTUSD_TRADE
}
o.AddChannelAuthenticated(channel, values)
}
func (o *OKCoin) WebsocketFuturesTrade(symbol, contractType string, price, amount float64, orderType, matchPrice, leverage int) {
values := make(map[string]string)
values["symbol"] = symbol
values["contract_type"] = contractType
values["price"] = strconv.FormatFloat(price, 'f', -1, 64)
values["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
values["type"] = strconv.Itoa(orderType)
values["match_price"] = strconv.Itoa(matchPrice)
values["lever_rate"] = strconv.Itoa(orderType)
o.AddChannelAuthenticated(OKCOIN_WEBSOCKET_FUTURES_TRADE, values)
}
func (o *OKCoin) WebsocketSpotCancel(symbol string, orderID int64) {
values := make(map[string]string)
values["symbol"] = symbol
values["order_id"] = strconv.FormatInt(orderID, 10)
channel := ""
if o.WebsocketURL == OKCOIN_WEBSOCKET_URL_CHINA {
channel = OKCOIN_WEBSOCKET_SPOTCNY_CANCEL_ORDER
} else {
channel = OKCOIN_WEBSOCKET_SPOTUSD_CANCEL_ORDER
}
o.AddChannelAuthenticated(channel, values)
}
func (o *OKCoin) WebsocketFuturesCancel(symbol, contractType string, orderID int64) {
values := make(map[string]string)
values["symbol"] = symbol
values["order_id"] = strconv.FormatInt(orderID, 10)
values["contract_type"] = contractType
o.AddChannelAuthenticated(OKCOIN_WEBSOCKET_FUTURES_CANCEL_ORDER, values)
}
func (o *OKCoin) WebsocketSpotOrderInfo(symbol string, orderID int64) {
values := make(map[string]string)
values["symbol"] = symbol
values["order_id"] = strconv.FormatInt(orderID, 10)
channel := ""
if o.WebsocketURL == OKCOIN_WEBSOCKET_URL_CHINA {
channel = OKCOIN_WEBSOCKET_SPOTCNY_ORDER_INFO
} else {
channel = OKCOIN_WEBSOCKET_SPOTUSD_ORDER_INFO
}
o.AddChannelAuthenticated(channel, values)
}
func (o *OKCoin) WebsocketFuturesOrderInfo(symbol, contractType string, orderID int64, orderStatus, currentPage, pageLength int) {
values := make(map[string]string)
values["symbol"] = symbol
values["order_id"] = strconv.FormatInt(orderID, 10)
values["contract_type"] = contractType
values["status"] = strconv.Itoa(orderStatus)
values["current_page"] = strconv.Itoa(currentPage)
values["page_length"] = strconv.Itoa(pageLength)
o.AddChannelAuthenticated(OKCOIN_WEBSOCKET_FUTURES_ORDER_INFO, values)
}
func (o *OKCoin) ConvertToURLValues(values map[string]string) url.Values {
urlVals := url.Values{}
for i, x := range values {
urlVals.Set(i, x)
}
return urlVals
}
func (o *OKCoin) WebsocketSign(values map[string]string) string {
values["api_key"] = o.APIKey
urlVals := o.ConvertToURLValues(values)
return strings.ToUpper(common.HexEncodeToString(common.GetMD5([]byte(urlVals.Encode() + "&secret_key=" + o.APISecret))))
}
func (o *OKCoin) AddChannelAuthenticated(channel string, values map[string]string) {
values["sign"] = o.WebsocketSign(values)
event := OKCoinWebsocketEventAuth{"addChannel", channel, values}
json, err := common.JSONEncode(event)
if err != nil {
log.Println(err)
return
}
err = o.WebsocketConn.WriteMessage(websocket.TextMessage, json)
if err != nil {
log.Println(err)
return
}
if o.Verbose {
log.Printf("%s Adding authenticated channel: %s\n", o.GetName(), channel)
}
}
func (o *OKCoin) RemoveChannelAuthenticated(conn *websocket.Conn, channel string, values map[string]string) {
values["sign"] = o.WebsocketSign(values)
event := OKCoinWebsocketEventAuthRemove{"removeChannel", channel, values}
json, err := common.JSONEncode(event)
if err != nil {
log.Println(err)
return
}
err = o.WebsocketConn.WriteMessage(websocket.TextMessage, json)
if err != nil {
log.Println(err)
return
}
if o.Verbose {
log.Printf("%s Removing authenticated channel: %s\n", o.GetName(), channel)
}
}
func (o *OKCoin) WebsocketClient() {
klineValues := []string{"1min", "3min", "5min", "15min", "30min", "1hour", "2hour", "4hour", "6hour", "12hour", "day", "3day", "week"}
currencyChan, userinfoChan := "", ""
if o.WebsocketURL == OKCOIN_WEBSOCKET_URL_CHINA {
currencyChan = OKCOIN_WEBSOCKET_CNY_REALTRADES
userinfoChan = OKCOIN_WEBSOCKET_SPOTCNY_USERINFO
} else {
currencyChan = OKCOIN_WEBSOCKET_USD_REALTRADES
userinfoChan = OKCOIN_WEBSOCKET_SPOTUSD_USERINFO
}
for o.Enabled && o.Websocket {
var Dialer websocket.Dialer
var err error
o.WebsocketConn, _, err = Dialer.Dial(o.WebsocketURL, http.Header{})
if err != nil {
log.Printf("%s Unable to connect to Websocket. Error: %s\n", o.GetName(), err)
continue
}
if o.Verbose {
log.Printf("%s Connected to Websocket.\n", o.GetName())
}
o.WebsocketConn.SetPingHandler(o.PingHandler)
if o.AuthenticatedAPISupport {
if o.WebsocketURL == OKCOIN_WEBSOCKET_URL {
o.AddChannelAuthenticated(OKCOIN_WEBSOCKET_FUTURES_REALTRADES, map[string]string{})
o.AddChannelAuthenticated(OKCOIN_WEBSOCKET_FUTURES_USERINFO, map[string]string{})
}
o.AddChannelAuthenticated(currencyChan, map[string]string{})
o.AddChannelAuthenticated(userinfoChan, map[string]string{})
}
for _, x := range o.EnabledPairs {
currency := common.StringToLower(x)
currencyUL := currency[0:3] + "_" + currency[3:]
if o.AuthenticatedAPISupport {
o.WebsocketSpotOrderInfo(currencyUL, -1)
}
if o.WebsocketURL == OKCOIN_WEBSOCKET_URL {
o.AddChannel(fmt.Sprintf("ok_%s_future_index", currency))
for _, y := range o.FuturesValues {
if o.AuthenticatedAPISupport {
o.WebsocketFuturesOrderInfo(currencyUL, y, -1, 1, 1, 50)
}
o.AddChannel(fmt.Sprintf("ok_%s_future_ticker_%s", currency, y))
o.AddChannel(fmt.Sprintf("ok_%s_future_depth_%s_60", currency, y))
o.AddChannel(fmt.Sprintf("ok_%s_future_trade_v1_%s", currency, y))
for _, z := range klineValues {
o.AddChannel(fmt.Sprintf("ok_future_%s_kline_%s_%s", currency, y, z))
}
}
} else {
o.AddChannel(fmt.Sprintf("ok_%s_ticker", currency))
o.AddChannel(fmt.Sprintf("ok_%s_depth60", currency))
o.AddChannel(fmt.Sprintf("ok_%s_trades_v1", currency))
for _, y := range klineValues {
o.AddChannel(fmt.Sprintf("ok_%s_kline_%s", currency, y))
}
}
}
for o.Enabled && o.Websocket {
msgType, resp, err := o.WebsocketConn.ReadMessage()
if err != nil {
log.Println(err)
break
}
switch msgType {
case websocket.TextMessage:
response := []interface{}{}
err = common.JSONDecode(resp, &response)
if err != nil {
log.Println(err)
continue
}
for _, y := range response {
z := y.(map[string]interface{})
channel := z["channel"]
data := z["data"]
success := z["success"]
errorcode := z["errorcode"]
channelStr, ok := channel.(string)
if !ok {
log.Println("Unable to convert channel to string")
continue
}
if success != "true" && success != nil {
errorCodeStr, ok := errorcode.(string)
if !ok {
log.Printf("%s Websocket: Unable to convert errorcode to string.\n", o.GetName())
log.Printf("%s Websocket: channel %s error code: %s.\n", o.GetName(), channelStr, errorcode)
} else {
log.Printf("%s Websocket: channel %s error: %s.\n", o.GetName(), channelStr, o.WebsocketErrors[errorCodeStr])
}
continue
}
if success == "true" {
if data == nil {
continue
}
}
dataJSON, err := common.JSONEncode(data)
if err != nil {
log.Println(err)
continue
}
switch true {
case common.StringContains(channelStr, "ticker") && !common.StringContains(channelStr, "future"):
tickerValues := []string{"buy", "high", "last", "low", "sell", "timestamp"}
tickerMap := data.(map[string]interface{})
ticker := OKCoinWebsocketTicker{}
ticker.Vol = tickerMap["vol"].(string)
for _, z := range tickerValues {
result := reflect.TypeOf(tickerMap[z]).String()
if result == "string" {
value, err := strconv.ParseFloat(tickerMap[z].(string), 64)
if err != nil {
log.Println(err)
continue
}
switch z {
case "buy":
ticker.Buy = value
case "high":
ticker.High = value
case "last":
ticker.Last = value
case "low":
ticker.Low = value
case "sell":
ticker.Sell = value
case "timestamp":
ticker.Timestamp = value
}
} else if result == "float64" {
switch z {
case "buy":
ticker.Buy = tickerMap[z].(float64)
case "high":
ticker.High = tickerMap[z].(float64)
case "last":
ticker.Last = tickerMap[z].(float64)
case "low":
ticker.Low = tickerMap[z].(float64)
case "sell":
ticker.Sell = tickerMap[z].(float64)
case "timestamp":
ticker.Timestamp = tickerMap[z].(float64)
}
}
}
case common.StringContains(channelStr, "ticker") && common.StringContains(channelStr, "future"):
ticker := OKCoinWebsocketFuturesTicker{}
err = common.JSONDecode(dataJSON, &ticker)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "depth"):
orderbook := OKCoinWebsocketOrderbook{}
err = common.JSONDecode(dataJSON, &orderbook)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "trades_v1") || common.StringContains(channelStr, "trade_v1"):
type TradeResponse struct {
Data [][]string
}
trades := TradeResponse{}
err = common.JSONDecode(dataJSON, &trades.Data)
if err != nil {
log.Println(err)
continue
}
// to-do: convert from string array to trade struct
case common.StringContains(channelStr, "kline"):
klines := []interface{}{}
err := common.JSONDecode(dataJSON, &klines)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "spot") && common.StringContains(channelStr, "realtrades"):
if string(dataJSON) == "null" {
continue
}
realtrades := OKCoinWebsocketRealtrades{}
err := common.JSONDecode(dataJSON, &realtrades)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "future") && common.StringContains(channelStr, "realtrades"):
if string(dataJSON) == "null" {
continue
}
realtrades := OKCoinWebsocketFuturesRealtrades{}
err := common.JSONDecode(dataJSON, &realtrades)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "spot") && common.StringContains(channelStr, "trade") || common.StringContains(channelStr, "futures") && common.StringContains(channelStr, "trade"):
tradeOrder := OKCoinWebsocketTradeOrderResponse{}
err := common.JSONDecode(dataJSON, &tradeOrder)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "cancel_order"):
cancelOrder := OKCoinWebsocketTradeOrderResponse{}
err := common.JSONDecode(dataJSON, &cancelOrder)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "spot") && common.StringContains(channelStr, "userinfo"):
userinfo := OKCoinWebsocketUserinfo{}
err = common.JSONDecode(dataJSON, &userinfo)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "futureusd_userinfo"):
userinfo := OKCoinWebsocketFuturesUserInfo{}
err = common.JSONDecode(dataJSON, &userinfo)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "spot") && common.StringContains(channelStr, "order_info"):
type OrderInfoResponse struct {
Result bool `json:"result"`
Orders []OKCoinWebsocketOrder `json:"orders"`
}
var orders OrderInfoResponse
err := common.JSONDecode(dataJSON, &orders)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "futureusd_order_info"):
type OrderInfoResponse struct {
Result bool `json:"result"`
Orders []OKCoinWebsocketFuturesOrder `json:"orders"`
}
var orders OrderInfoResponse
err := common.JSONDecode(dataJSON, &orders)
if err != nil {
log.Println(err)
continue
}
case common.StringContains(channelStr, "future_index"):
index := OKCoinWebsocketFutureIndex{}
err = common.JSONDecode(dataJSON, &index)
if err != nil {
log.Println(err)
continue
}
}
}
}
}
o.WebsocketConn.Close()
log.Printf("%s Websocket client disconnected.", o.GetName())
}
}
func (o *OKCoin) SetWebsocketErrorDefaults() {
o.WebsocketErrors = map[string]string{
"10001": "Illegal parameters",
"10002": "Authentication failure",
"10003": "This connection has requested other user data",
"10004": "This connection did not request this user data",
"10005": "System error",
"10009": "Order does not exist",
"10010": "Insufficient funds",
"10011": "Order quantity too low",
"10012": "Only support btc_usd/btc_cny ltc_usd/ltc_cny",
"10014": "Order price must be between 0 - 1,000,000",
"10015": "Channel subscription temporally not available",
"10016": "Insufficient coins",
"10017": "WebSocket authorization error",
"10100": "User frozen",
"10216": "Non-public API",
"20001": "User does not exist",
"20002": "User frozen",
"20003": "Frozen due to force liquidation",
"20004": "Future account frozen",
"20005": "User future account does not exist",
"20006": "Required field can not be null",
"20007": "Illegal parameter",
"20008": "Future account fund balance is zero",
"20009": "Future contract status error",
"20010": "Risk rate information does not exist",
"20011": `Risk rate bigger than 90% before opening position`,
"20012": `Risk rate bigger than 90% after opening position`,
"20013": "Temporally no counter party price",
"20014": "System error",
"20015": "Order does not exist",
"20016": "Liquidation quantity bigger than holding",
"20017": "Not authorized/illegal order ID",
"20018": `Order price higher than 105% or lower than 95% of the price of last minute`,
"20019": "IP restrained to access the resource",
"20020": "Secret key does not exist",
"20021": "Index information does not exist",
"20022": "Wrong API interface",
"20023": "Fixed margin user",
"20024": "Signature does not match",
"20025": "Leverage rate error",
}
}