Files
gocryptotrader/exchanges/okcoin/okcoin.go
Scott b1e6534e7c Withdraw Crypto wrapper mapping (#226)
* Initial commit

* Updates signature for all withdrawal methods to use new withdrawRequest struct type

* Implements crypto withdraw features & tests for Alphapoint, ANX, Binance, Bitfinex, Bitflyer, Bithumb, Bitmex, Bitstamp, Bittrex, BTCC, BTCmarkets, CoinbasePro, Coinut. Updates WithdrawRequest type with more members. Breaking change to update real order testing for increased code coverage

* Updates all realOrder tests to run when no API key is present. Updates exchange functions to handle errors better

* Implements crypto withdrawals for Exmo, GateIO, Gemini, HitBTC, Huobi, HuobiHadax, Kraken, LakeBTC, Liqui, Localbitcoins, OKCoin, OKEX, Poloniex, Wex, Yobit and ZB. Updates real order test formatting for all real order tests

* Update alphapoint. Fixes anx typos. Adds function WithdrawFiatFundsToInternationalBank to exchange wrapper interface. Adds WithdrawFiatFundsToInternationalBank to alphapoint, bitmex, coinbasepro. Updates Kraken to use TradePassword property

* Reverts alphapoint to use ErrNotYetImplemented

* Fixes line spacing and removes unnecessary line
2019-01-03 13:15:07 +11:00

1081 lines
32 KiB
Go

package okcoin
import (
"errors"
"fmt"
"log"
"net/url"
"strconv"
"strings"
"time"
"github.com/thrasher-/gocryptotrader/currency/symbol"
"github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/exchanges"
"github.com/thrasher-/gocryptotrader/exchanges/request"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
)
const (
okcoinAPIURL = "https://www.okcoin.com/api/v1/"
okcoinAPIURLChina = "https://www.okcoin.com/api/v1/"
okcoinAPIURLBase = "https://www.okcoin.com/api/"
okcoinAPIVersion = "1"
okcoinWebsocketURL = "wss://real.okcoin.com:10440/websocket/okcoinapi"
okcoinWebsocketURLChina = "wss://real.okcoin.cn:10440/websocket/okcoinapi"
okcoinInstruments = "instruments"
okcoinTicker = "ticker.do"
okcoinDepth = "depth.do"
okcoinTrades = "trades.do"
okcoinKline = "kline.do"
okcoinUserInfo = "userinfo.do"
okcoinTrade = "trade.do"
okcoinTradeHistory = "trade_history.do"
okcoinTradeBatch = "batch_trade.do"
okcoinOrderCancel = "cancel_order.do"
okcoinOrderInfo = "order_info.do"
okcoinOrdersInfo = "orders_info.do"
okcoinOrderHistory = "order_history.do"
okcoinWithdraw = "withdraw.do"
okcoinWithdrawCancel = "cancel_withdraw.do"
okcoinWithdrawInfo = "withdraw_info.do"
okcoinOrderFee = "order_fee.do"
okcoinLendDepth = "lend_depth.do"
okcoinBorrowsInfo = "borrows_info.do"
okcoinBorrowMoney = "borrow_money.do"
okcoinBorrowCancel = "cancel_borrow.do"
okcoinBorrowOrderInfo = "borrow_order_info.do"
okcoinRepayment = "repayment.do"
okcoinUnrepaymentsInfo = "unrepayments_info.do"
okcoinAccountRecords = "account_records.do"
okcoinFuturesTicker = "future_ticker.do"
okcoinFuturesDepth = "future_depth.do"
okcoinFuturesTrades = "future_trades.do"
okcoinFuturesIndex = "future_index.do"
okcoinExchangeRate = "exchange_rate.do"
okcoinFuturesEstimatedPrice = "future_estimated_price.do"
okcoinFuturesKline = "future_kline.do"
okcoinFuturesHoldAmount = "future_hold_amount.do"
okcoinFuturesUserInfo = "future_userinfo.do"
okcoinFuturesPosition = "future_position.do"
okcoinFuturesTrade = "future_trade.do"
okcoinFuturesTradeHistory = "future_trades_history.do"
okcoinFuturesTradeBatch = "future_batch_trade.do"
okcoinFuturesCancel = "future_cancel.do"
okcoinFuturesOrderInfo = "future_order_info.do"
okcoinFuturesOrdersInfo = "future_orders_info.do"
okcoinFuturesUserInfo4Fix = "future_userinfo_4fix.do"
okcoinFuturesposition4Fix = "future_position_4fix.do"
okcoinFuturesExplosive = "future_explosive.do"
okcoinFuturesDevolve = "future_devolve.do"
okcoinAuthRate = 0
okcoinUnauthRate = 0
)
// OKCoin is the overarching type across this package
type OKCoin struct {
exchange.Base
RESTErrors map[string]string
WebsocketErrors map[string]string
FuturesValues []string
WebsocketConn *websocket.Conn
}
// setCurrencyPairFormats sets currency pair formatting for this package
func (o *OKCoin) setCurrencyPairFormats() {
o.RequestCurrencyPairFormat.Delimiter = "_"
o.RequestCurrencyPairFormat.Uppercase = false
o.ConfigCurrencyPairFormat.Delimiter = ""
o.ConfigCurrencyPairFormat.Uppercase = true
}
// SetDefaults sets current default values for this package
func (o *OKCoin) SetDefaults() {
o.SetErrorDefaults()
o.SetWebsocketErrorDefaults()
o.Enabled = false
o.Verbose = false
o.RESTPollingDelay = 10
o.AssetTypes = []string{ticker.Spot}
o.APIWithdrawPermissions = exchange.AutoWithdrawCrypto | exchange.WithdrawFiatViaWebsiteOnly
o.SupportsAutoPairUpdating = false
o.SupportsRESTTickerBatching = false
o.WebsocketInit()
}
// Setup sets exchange configuration parameters
func (o *OKCoin) Setup(exch config.ExchangeConfig) {
if !exch.Enabled {
o.SetEnabled(false)
} else {
if exch.Name == "OKCOIN International" {
o.AssetTypes = append(o.AssetTypes, o.FuturesValues...)
o.APIUrlDefault = okcoinAPIURL
o.APIUrl = o.APIUrlDefault
o.Name = "OKCOIN International"
o.WebsocketURL = okcoinWebsocketURL
o.setCurrencyPairFormats()
o.Requester = request.New(o.Name,
request.NewRateLimit(time.Second, okcoinAuthRate),
request.NewRateLimit(time.Second, okcoinUnauthRate),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
o.ConfigCurrencyPairFormat.Delimiter = "_"
o.ConfigCurrencyPairFormat.Uppercase = true
o.RequestCurrencyPairFormat.Uppercase = false
o.RequestCurrencyPairFormat.Delimiter = "_"
o.SupportsAutoPairUpdating = true
} else {
o.APIUrlDefault = okcoinAPIURLChina
o.APIUrl = o.APIUrlDefault
o.Name = "OKCOIN China"
o.WebsocketURL = okcoinWebsocketURLChina
o.setCurrencyPairFormats()
o.Requester = request.New(o.Name,
request.NewRateLimit(time.Second, okcoinAuthRate),
request.NewRateLimit(time.Second, okcoinUnauthRate),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
o.ConfigCurrencyPairFormat.Delimiter = ""
o.ConfigCurrencyPairFormat.Uppercase = true
o.RequestCurrencyPairFormat.Uppercase = false
o.RequestCurrencyPairFormat.Delimiter = ""
}
o.Enabled = true
o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
o.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
o.SetHTTPClientTimeout(exch.HTTPTimeout)
o.SetHTTPClientUserAgent(exch.HTTPUserAgent)
o.RESTPollingDelay = exch.RESTPollingDelay
o.Verbose = exch.Verbose
o.Websocket.SetEnabled(exch.Websocket)
o.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
o.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
o.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
err := o.SetCurrencyPairFormat()
if err != nil {
log.Fatal(err)
}
err = o.SetAssetTypes()
if err != nil {
log.Fatal(err)
}
err = o.SetAutoPairDefaults()
if err != nil {
log.Fatal(err)
}
err = o.SetAPIURL(exch)
if err != nil {
log.Fatal(err)
}
err = o.SetClientProxyAddress(exch.ProxyAddress)
if err != nil {
log.Fatal(err)
}
err = o.WebsocketSetup(o.WsConnect,
exch.Name,
exch.Websocket,
okcoinWebsocketURL,
o.WebsocketURL)
if err != nil {
log.Fatal(err)
}
}
}
// GetSpotInstruments returns a list of tradable spot instruments and their properties
func (o *OKCoin) GetSpotInstruments() ([]SpotInstrument, error) {
var resp []SpotInstrument
path := fmt.Sprintf("%sspot/v3/%s", okcoinAPIURLBase, okcoinInstruments)
err := o.SendHTTPRequest(path, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
// GetTicker returns the current ticker
func (o *OKCoin) GetTicker(symbol string) (Ticker, error) {
resp := TickerResponse{}
vals := url.Values{}
vals.Set("symbol", symbol)
path := common.EncodeURLValues(o.APIUrl+okcoinTicker, vals)
return resp.Ticker, o.SendHTTPRequest(path, &resp)
}
// GetOrderBook returns the current order book by size
func (o *OKCoin) GetOrderBook(symbol string, size int64, merge bool) (Orderbook, error) {
resp := Orderbook{}
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+okcoinDepth, vals)
return resp, o.SendHTTPRequest(path, &resp)
}
// GetTrades returns historic trades since a timestamp
func (o *OKCoin) GetTrades(symbol string, since int64) ([]Trades, error) {
result := []Trades{}
vals := url.Values{}
vals.Set("symbol", symbol)
if since != 0 {
vals.Set("since", strconv.FormatInt(since, 10))
}
path := common.EncodeURLValues(o.APIUrl+okcoinTrades, vals)
return result, o.SendHTTPRequest(path, &result)
}
// GetKline returns kline data
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+okcoinKline, vals)
return resp, o.SendHTTPRequest(path, &resp)
}
// GetFuturesTicker returns a current ticker for the futures market
func (o *OKCoin) GetFuturesTicker(symbol, contractType string) (FuturesTicker, error) {
resp := FuturesTickerResponse{}
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("contract_type", contractType)
path := common.EncodeURLValues(o.APIUrl+okcoinFuturesTicker, vals)
return resp.Ticker, o.SendHTTPRequest(path, &resp)
}
// GetFuturesDepth returns current depth for the futures market
func (o *OKCoin) GetFuturesDepth(symbol, contractType string, size int64, merge bool) (Orderbook, error) {
result := Orderbook{}
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+okcoinFuturesDepth, vals)
return result, o.SendHTTPRequest(path, &result)
}
// GetFuturesTrades returns historic trades for the futures market
func (o *OKCoin) GetFuturesTrades(symbol, contractType string) ([]FuturesTrades, error) {
result := []FuturesTrades{}
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("contract_type", contractType)
path := common.EncodeURLValues(o.APIUrl+okcoinFuturesTrades, vals)
return result, o.SendHTTPRequest(path, &result)
}
// GetFuturesIndex returns an index for the futures market
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+okcoinFuturesIndex, vals)
return result.Index, o.SendHTTPRequest(path, &result)
}
// GetFuturesExchangeRate returns the exchange rate for the futures market
func (o *OKCoin) GetFuturesExchangeRate() (float64, error) {
type Response struct {
Rate float64 `json:"rate"`
}
result := Response{}
return result.Rate, o.SendHTTPRequest(o.APIUrl+okcoinExchangeRate, &result)
}
// GetFuturesEstimatedPrice returns a current estimated futures price for a
// currency
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+okcoinFuturesEstimatedPrice, vals)
return result.Price, o.SendHTTPRequest(path, &result)
}
// GetFuturesKline returns kline data for a specific currency on the futures
// market
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+okcoinFuturesKline, vals)
return resp, o.SendHTTPRequest(path, &resp)
}
// GetFuturesHoldAmount returns the hold amount for a futures trade
func (o *OKCoin) GetFuturesHoldAmount(symbol, contractType string) ([]FuturesHoldAmount, error) {
resp := []FuturesHoldAmount{}
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("contract_type", contractType)
path := common.EncodeURLValues(o.APIUrl+okcoinFuturesHoldAmount, vals)
return resp, o.SendHTTPRequest(path, &resp)
}
// GetFuturesExplosive returns the explosive for a futures contract
func (o *OKCoin) GetFuturesExplosive(symbol, contractType string, status, currentPage, pageLength int64) ([]FuturesExplosive, error) {
type Response struct {
Data []FuturesExplosive `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+okcoinFuturesExplosive, vals)
return resp.Data, o.SendHTTPRequest(path, &resp)
}
// GetUserInfo returns user information associated with the calling APIkeys
func (o *OKCoin) GetUserInfo() (UserInfo, error) {
result := UserInfo{}
return result,
o.SendAuthenticatedHTTPRequest(okcoinUserInfo, url.Values{}, &result)
}
// Trade initiates a new trade
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(okcoinTrade, v, &result)
if err != nil {
return 0, err
}
if !result.Result {
return 0, errors.New("unable to place order")
}
return result.OrderID, nil
}
// GetTradeHistory returns client trade history
func (o *OKCoin) GetTradeHistory(symbol string, TradeID int64) ([]Trades, error) {
result := []Trades{}
v := url.Values{}
v.Set("symbol", symbol)
v.Set("since", strconv.FormatInt(TradeID, 10))
err := o.SendAuthenticatedHTTPRequest(okcoinTradeHistory, v, &result)
if err != nil {
return nil, err
}
return result, nil
}
// BatchTrade initiates a trade by batch order
func (o *OKCoin) BatchTrade(orderData string, symbol, orderType string) (BatchTrade, error) {
v := url.Values{}
v.Set("orders_data", orderData)
v.Set("symbol", symbol)
v.Set("type", orderType)
result := BatchTrade{}
err := o.SendAuthenticatedHTTPRequest(okcoinTradeBatch, v, &result)
if err != nil {
return result, err
}
return result, nil
}
// CancelExistingOrder cancels a specific order or list of orders by orderID
func (o *OKCoin) CancelExistingOrder(orderID []int64, symbol string) (CancelOrderResponse, error) {
v := url.Values{}
orders := []string{}
result := CancelOrderResponse{}
orderStr := strconv.FormatInt(orderID[0], 10)
if len(orderID) > 1 {
for x := range orderID {
orders = append(orders, strconv.FormatInt(orderID[x], 10))
}
orderStr = common.JoinStrings(orders, ",")
}
v.Set("order_id", orderStr)
v.Set("symbol", symbol)
return result, o.SendAuthenticatedHTTPRequest(okcoinOrderCancel, v, &result)
}
// GetOrderInformation returns order information by orderID
func (o *OKCoin) GetOrderInformation(orderID int64, symbol string) ([]OrderInfo, error) {
type Response struct {
Result bool `json:"result"`
Orders []OrderInfo `json:"orders"`
}
v := url.Values{}
v.Set("symbol", symbol)
v.Set("order_id", strconv.FormatInt(orderID, 10))
result := Response{}
err := o.SendAuthenticatedHTTPRequest(okcoinOrderInfo, 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
}
// GetOrderInfoBatch returns order info on a batch of orders
func (o *OKCoin) GetOrderInfoBatch(orderID []int64, symbol string) ([]OrderInfo, error) {
type Response struct {
Result bool `json:"result"`
Orders []OrderInfo `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(okcoinOrderInfo, 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
}
// GetOrderHistory returns a history of orders
func (o *OKCoin) GetOrderHistory(pageLength, currentPage int64, status, symbol string) (OrderHistory, 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 := OrderHistory{}
err := o.SendAuthenticatedHTTPRequest(okcoinOrderHistory, v, &result)
if err != nil {
return result, err
}
return result, nil
}
// Withdrawal withdraws a cryptocurrency to a supplied address
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))
v.Set("target", "address")
result := WithdrawalResponse{}
err := o.SendAuthenticatedHTTPRequest(okcoinWithdraw, v, &result)
if err != nil {
return 0, err
}
if !result.Result {
return 0, errors.New("unable to process withdrawal request")
}
return result.WithdrawID, nil
}
// CancelWithdrawal cancels a withdrawal
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 := WithdrawalResponse{}
err := o.SendAuthenticatedHTTPRequest(okcoinWithdrawCancel, 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
}
// GetWithdrawalInfo returns withdrawal information
func (o *OKCoin) GetWithdrawalInfo(symbol string, withdrawalID int64) ([]WithdrawInfo, error) {
type Response struct {
Result bool
Withdraw []WithdrawInfo `json:"withdraw"`
}
v := url.Values{}
v.Set("symbol", symbol)
v.Set("withdrawal_id", strconv.FormatInt(withdrawalID, 10))
result := Response{}
err := o.SendAuthenticatedHTTPRequest(okcoinWithdrawInfo, 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
}
// GetOrderFeeInfo returns order fee information
func (o *OKCoin) GetOrderFeeInfo(symbol string, orderID int64) (OrderFeeInfo, error) {
type Response struct {
Data OrderFeeInfo `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(okcoinOrderFee, 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
}
// GetLendDepth returns the depth of lends
func (o *OKCoin) GetLendDepth(symbol string) ([]LendDepth, error) {
type Response struct {
LendDepth []LendDepth `json:"lend_depth"`
}
v := url.Values{}
v.Set("symbol", symbol)
result := Response{}
err := o.SendAuthenticatedHTTPRequest(okcoinLendDepth, v, &result)
if err != nil {
return nil, err
}
return result.LendDepth, nil
}
// GetBorrowInfo returns borrow information
func (o *OKCoin) GetBorrowInfo(symbol string) (BorrowInfo, error) {
v := url.Values{}
v.Set("symbol", symbol)
result := BorrowInfo{}
err := o.SendAuthenticatedHTTPRequest(okcoinBorrowsInfo, v, &result)
if err != nil {
return result, nil
}
return result, nil
}
// Borrow initiates a borrow request
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 := BorrowResponse{}
err := o.SendAuthenticatedHTTPRequest(okcoinBorrowMoney, v, &result)
if err != nil {
return 0, err
}
if !result.Result {
return 0, errors.New("unable to borrow")
}
return result.BorrowID, nil
}
// CancelBorrow cancels a borrow request
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 := BorrowResponse{}
err := o.SendAuthenticatedHTTPRequest(okcoinBorrowCancel, v, &result)
if err != nil {
return false, err
}
if !result.Result {
return false, errors.New("unable to cancel borrow")
}
return true, nil
}
// GetBorrowOrderInfo returns information about a borrow order
func (o *OKCoin) GetBorrowOrderInfo(borrowID int64) (BorrowInfo, error) {
type Response struct {
Result bool `json:"result"`
BorrowOrder BorrowInfo `json:"borrow_order"`
}
v := url.Values{}
v.Set("borrow_id", strconv.FormatInt(borrowID, 10))
result := Response{}
err := o.SendAuthenticatedHTTPRequest(okcoinBorrowOrderInfo, 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
}
// GetRepaymentInfo returns information on a repayment
func (o *OKCoin) GetRepaymentInfo(borrowID int64) (bool, error) {
v := url.Values{}
v.Set("borrow_id", strconv.FormatInt(borrowID, 10))
result := BorrowResponse{}
err := o.SendAuthenticatedHTTPRequest(okcoinRepayment, v, &result)
if err != nil {
return false, err
}
if !result.Result {
return false, errors.New("unable to get repayment info")
}
return true, nil
}
// GetUnrepaymentsInfo returns information on an unrepayment
func (o *OKCoin) GetUnrepaymentsInfo(symbol string, currentPage, pageLength int) ([]BorrowOrder, error) {
type Response struct {
Unrepayments []BorrowOrder `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(okcoinUnrepaymentsInfo, v, &result)
if err != nil {
return nil, err
}
if !result.Result {
return nil, errors.New("unable to get unrepayments info")
}
return result.Unrepayments, nil
}
// GetAccountRecords returns account records
func (o *OKCoin) GetAccountRecords(symbol string, recType, currentPage, pageLength int) ([]AccountRecords, error) {
type Response struct {
Records []AccountRecords `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(okcoinAccountRecords, v, &result)
if err != nil {
return nil, err
}
return result.Records, nil
}
// GetFuturesUserInfo returns information on a users futures
func (o *OKCoin) GetFuturesUserInfo() {
err := o.SendAuthenticatedHTTPRequest(okcoinFuturesUserInfo, url.Values{}, nil)
if err != nil {
log.Println(err)
}
}
// GetFuturesPosition returns position on a futures contract
func (o *OKCoin) GetFuturesPosition(symbol, contractType string) {
v := url.Values{}
v.Set("symbol", symbol)
v.Set("contract_type", contractType)
err := o.SendAuthenticatedHTTPRequest(okcoinFuturesPosition, v, nil)
if err != nil {
log.Println(err)
}
}
// FuturesTrade initiates a new futures trade
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(okcoinFuturesTrade, v, nil)
if err != nil {
log.Println(err)
}
}
// FuturesBatchTrade initiates a batch of futures contract trades
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(okcoinFuturesTradeBatch, v, nil)
if err != nil {
log.Println(err)
}
}
// CancelFuturesOrder cancels a futures contract order
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(okcoinFuturesCancel, v, nil)
if err != nil {
log.Println(err)
}
}
// GetFuturesOrderInfo returns information on a specific futures contract order
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(okcoinFuturesOrderInfo, v, nil)
if err != nil {
log.Println(err)
}
}
// GetFutureOrdersInfo returns information on a range of futures orders
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(okcoinFuturesOrdersInfo, v, nil)
if err != nil {
log.Println(err)
}
}
// GetFuturesUserInfo4Fix returns futures user info fix rate
func (o *OKCoin) GetFuturesUserInfo4Fix() {
v := url.Values{}
err := o.SendAuthenticatedHTTPRequest(okcoinFuturesUserInfo4Fix, v, nil)
if err != nil {
log.Println(err)
}
}
// GetFuturesUserPosition4Fix returns futures user info on a fixed position
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(okcoinFuturesUserInfo4Fix, v, nil)
if err != nil {
log.Println(err)
}
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (o *OKCoin) SendHTTPRequest(path string, result interface{}) error {
return o.SendPayload("GET", path, nil, nil, result, false, o.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
func (o *OKCoin) SendAuthenticatedHTTPRequest(method string, v url.Values, result interface{}) (err error) {
if !o.AuthenticatedAPISupport {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, o.Name)
}
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"
return o.SendPayload("POST", path, headers, strings.NewReader(encoded), result, true, o.Verbose)
}
// SetErrorDefaults sets default error map
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",
}
}
// GetFee returns an estimate of fee based on type of transaction
func (o *OKCoin) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) {
var fee float64
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
case exchange.InternationalBankWithdrawalFee:
fee = calculateInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.PurchasePrice, feeBuilder.Amount)
case exchange.CryptocurrencyWithdrawalFee:
fee = getWithdrawalFee(feeBuilder.FirstCurrency)
}
if fee < 0 {
fee = 0
}
return fee, nil
}
func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) {
// TODO volume based fees
if isMaker {
fee = 0.0005
} else {
fee = 0.0015
}
return fee * amount * purchasePrice
}
func calculateInternationalBankWithdrawalFee(currency string, purchasePrice, amount float64) (fee float64) {
if currency == symbol.USD {
if purchasePrice*amount*0.001 < 15 {
fee = 15
} else {
fee = purchasePrice * amount * 0.001
}
}
return fee
}
func getWithdrawalFee(currency string) float64 {
return WithdrawalFees[currency]
}