mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
Package exchanges
This commit is contained in:
159
exchanges/alphapoint/alphapoint_types.go
Normal file
159
exchanges/alphapoint/alphapoint_types.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package alphapoint
|
||||
|
||||
type AlphapointTrade struct {
|
||||
TID int64 `json:"tid"`
|
||||
Price float64 `json:"px"`
|
||||
Quantity float64 `json:"qty"`
|
||||
Unixtime int `json:"unixtime"`
|
||||
UTCTicks int64 `json:"utcticks"`
|
||||
IncomingOrderSide int `json:"incomingOrderSide"`
|
||||
IncomingServerOrderID int `json:"incomingServerOrderId"`
|
||||
BookServerOrderID int `json:"bookServerOrderId"`
|
||||
}
|
||||
|
||||
type AlphapointTrades struct {
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
DateTimeUTC int64 `json:"dateTimeUtc"`
|
||||
Instrument string `json:"ins"`
|
||||
StartIndex int `json:"startIndex"`
|
||||
Count int `json:"count"`
|
||||
Trades []AlphapointTrade `json:"trades"`
|
||||
}
|
||||
|
||||
type AlphapointTradesByDate struct {
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
DateTimeUTC int64 `json:"dateTimeUtc"`
|
||||
Instrument string `json:"ins"`
|
||||
StartDate int64 `json:"startDate"`
|
||||
EndDate int64 `json:"endDate"`
|
||||
Trades []AlphapointTrade `json:"trades"`
|
||||
}
|
||||
|
||||
type AlphapointTicker struct {
|
||||
High float64 `json:"high"`
|
||||
Last float64 `json:"last"`
|
||||
Bid float64 `json:"bid"`
|
||||
Volume float64 `json:"volume"`
|
||||
Low float64 `json:"low"`
|
||||
Ask float64 `json:"ask"`
|
||||
Total24HrQtyTraded float64 `json:"Total24HrQtyTraded"`
|
||||
Total24HrNumTrades float64 `json:"Total24HrNumTrades"`
|
||||
SellOrderCount float64 `json:"sellOrderCount"`
|
||||
BuyOrderCount float64 `json:"buyOrderCount"`
|
||||
NumOfCreateOrders float64 `json:"numOfCreateOrders"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointOrderbookEntry struct {
|
||||
Quantity float64 `json:"qty"`
|
||||
Price float64 `json:"px"`
|
||||
}
|
||||
|
||||
type AlphapointOrderbook struct {
|
||||
Bids []AlphapointOrderbookEntry `json:"bids"`
|
||||
Asks []AlphapointOrderbookEntry `json:"asks"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointProductPair struct {
|
||||
Name string `json:"name"`
|
||||
Productpaircode int `json:"productPairCode"`
|
||||
Product1Label string `json:"product1Label"`
|
||||
Product1Decimalplaces int `json:"product1DecimalPlaces"`
|
||||
Product2Label string `json:"product2Label"`
|
||||
Product2Decimalplaces int `json:"product2DecimalPlaces"`
|
||||
}
|
||||
|
||||
type AlphapointProductPairs struct {
|
||||
ProductPairs []AlphapointProductPair `json:"productPairs"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointProduct struct {
|
||||
Name string `json:"name"`
|
||||
IsDigital bool `json:"isDigital"`
|
||||
ProductCode int `json:"productCode"`
|
||||
DecimalPlaces int `json:"decimalPlaces"`
|
||||
FullName string `json:"fullName"`
|
||||
}
|
||||
|
||||
type AlphapointProducts struct {
|
||||
Products []AlphapointProduct `json:"products"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointUserInfo struct {
|
||||
UserInfoKVP []struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
} `json:"userInfoKVP"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointAccountInfo struct {
|
||||
Currencies []struct {
|
||||
Name string `json:"name"`
|
||||
Balance int `json:"balance"`
|
||||
Hold int `json:"hold"`
|
||||
} `json:"currencies"`
|
||||
ProductPairs []struct {
|
||||
ProductPairName string `json:"productPairName"`
|
||||
ProductPairCode int `json:"productPairCode"`
|
||||
TradeCount int `json:"tradeCount"`
|
||||
TradeVolume int `json:"tradeVolume"`
|
||||
} `json:"productPairs"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointOrder struct {
|
||||
Serverorderid int `json:"ServerOrderId"`
|
||||
AccountID int `json:"AccountId"`
|
||||
Price int `json:"Price"`
|
||||
QtyTotal int `json:"QtyTotal"`
|
||||
QtyRemaining int `json:"QtyRemaining"`
|
||||
ReceiveTime int64 `json:"ReceiveTime"`
|
||||
Side int `json:"Side"`
|
||||
}
|
||||
|
||||
type AlphapointOpenOrders struct {
|
||||
Instrument string `json:"ins"`
|
||||
Openorders []AlphapointOrder `json:"openOrders"`
|
||||
}
|
||||
|
||||
type AlphapointOrderInfo struct {
|
||||
OpenOrders []AlphapointOpenOrders `json:"openOrdersInfo"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
DateTimeUTC int64 `json:"dateTimeUtc"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointDepositAddresses struct {
|
||||
Name string `json:"name"`
|
||||
DepositAddress string `json:"depositAddress"`
|
||||
}
|
||||
|
||||
type AlphapointWebsocketTicker struct {
|
||||
MessageType string `json:"messageType"`
|
||||
ProductPair string `json:"prodPair"`
|
||||
High float64 `json:"high"`
|
||||
Low float64 `json:"low"`
|
||||
Last float64 `json:"last"`
|
||||
Volume float64 `json:"volume"`
|
||||
Volume24Hrs float64 `json:"volume24hrs"`
|
||||
Volume24HrsProduct2 float64 `json:"volume24hrsProduct2"`
|
||||
Total24HrQtyTraded float64 `json:"Total24HrQtyTraded"`
|
||||
Total24HrProduct2Traded float64 `json:"Total24HrProduct2Traded"`
|
||||
Total24HrNumTrades float64 `json:"Total24HrNumTrades"`
|
||||
Bid float64 `json:"bid"`
|
||||
Ask float64 `json:"ask"`
|
||||
BuyOrderCount int `json:"buyOrderCount"`
|
||||
SellOrderCount int `json:"sellOrderCount"`
|
||||
}
|
||||
20
exchanges/alphapoint/alphapoint_wrapper.go
Normal file
20
exchanges/alphapoint/alphapoint_wrapper.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package alphapoint
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/thraser-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (a *Alphapoint) GetTickerPrice(currency string) TickerPrice {
|
||||
var tickerPrice TickerPrice
|
||||
ticker, err := a.GetTicker(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return TickerPrice{}
|
||||
}
|
||||
tickerPrice.Ask = ticker.Ask
|
||||
tickerPrice.Bid = ticker.Bid
|
||||
|
||||
return tickerPrice
|
||||
}
|
||||
458
exchanges/alphapoint/alphapointhttp.go
Normal file
458
exchanges/alphapoint/alphapointhttp.go
Normal file
@@ -0,0 +1,458 @@
|
||||
package alphapoint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
const (
|
||||
ALPHAPOINT_DEFAULT_API_URL = "https://sim3.alphapoint.com:8400"
|
||||
ALPHAPOINT_API_VERSION = "1"
|
||||
ALPHAPOINT_TICKER = "GetTicker"
|
||||
ALPHAPOINT_TRADES = "GetTrades"
|
||||
ALPHAPOINT_TRADESBYDATE = "GetTradesByDate"
|
||||
ALPHAPOINT_ORDERBOOK = "GetOrderBook"
|
||||
ALPHAPOINT_PRODUCT_PAIRS = "GetProductPairs"
|
||||
ALPHAPOINT_PRODUCTS = "GetProducts"
|
||||
ALPHAPOINT_CREATE_ACCOUNT = "CreateAccount"
|
||||
ALPHAPOINT_USERINFO = "GetUserInfo"
|
||||
ALPHAPOINT_ACCOUNT_INFO = "GetAccountInfo"
|
||||
ALPHAPOINT_ACCOUNT_TRADES = "GetAccountTrades"
|
||||
ALPHAPOINT_DEPOSIT_ADDRESSES = "GetDepositAddresses"
|
||||
ALPHAPOINT_WITHDRAW = "Withdraw"
|
||||
ALPHAPOINT_CREATE_ORDER = "CreateOrder"
|
||||
ALPHAPOINT_MODIFY_ORDER = "ModifyOrder"
|
||||
ALPHAPOINT_CANCEL_ORDER = "CancelOrder"
|
||||
ALPHAPOINT_CANCEALLORDERS = "CancelAllOrders"
|
||||
ALPHAPOINT_OPEN_ORDERS = "GetAccountOpenOrders"
|
||||
ALPHAPOINT_ORDER_FEE = "GetOrderFee"
|
||||
)
|
||||
|
||||
type Alphapoint struct {
|
||||
WebsocketConn *websocket.Conn
|
||||
}
|
||||
|
||||
func (a *Alphapoint) SetDefaults() {
|
||||
a.APIUrl = ALPHAPOINT_DEFAULT_API_URL
|
||||
a.WebsocketURL = ALPHAPOINT_DEFAULT_WEBSOCKET_URL
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetTicker(symbol string) (AlphapointTicker, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["productPair"] = symbol
|
||||
response := AlphapointTicker{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_TICKER, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetTrades(symbol string, startIndex, count int) (AlphapointTrades, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["startIndex"] = startIndex
|
||||
request["Count"] = count
|
||||
response := AlphapointTrades{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_TRADES, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetTradesByDate(symbol string, startDate, endDate int64) (AlphapointTradesByDate, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["startDate"] = startDate
|
||||
request["endDate"] = endDate
|
||||
response := AlphapointTradesByDate{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_TRADESBYDATE, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetOrderbook(symbol string) (AlphapointOrderbook, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["productPair"] = symbol
|
||||
response := AlphapointOrderbook{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_ORDERBOOK, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetProductPairs() (AlphapointProductPairs, error) {
|
||||
response := AlphapointProductPairs{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_PRODUCT_PAIRS, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetProducts() (AlphapointProducts, error) {
|
||||
response := AlphapointProducts{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_PRODUCTS, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) CreateAccount(firstName, lastName, email, phone, password string) error {
|
||||
request := make(map[string]interface{})
|
||||
request["firstname"] = firstName
|
||||
request["lastname"] = lastName
|
||||
request["email"] = email
|
||||
request["phone"] = phone
|
||||
request["password"] = password
|
||||
|
||||
type Response struct {
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_CREATE_ACCOUNT, request, &response)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return errors.New(response.RejectReason)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetUserInfo() (AlphapointUserInfo, error) {
|
||||
response := AlphapointUserInfo{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_USERINFO, map[string]interface{}{}, &response)
|
||||
if err != nil {
|
||||
return AlphapointUserInfo{}, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetAccountInfo() (AlphapointAccountInfo, error) {
|
||||
response := AlphapointAccountInfo{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_ACCOUNT_INFO, map[string]interface{}{}, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetName() string {
|
||||
return a.ExchangeName
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Alphapoint exchange
|
||||
func (e *Alphapoint) GetExchangeAccountInfo() (ExchangeAccountInfo, error) {
|
||||
var response ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
account, err := e.GetAccountInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(account.Currencies); i++ {
|
||||
var exchangeCurrency ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = account.Currencies[i].Name
|
||||
exchangeCurrency.TotalValue = float64(account.Currencies[i].Balance)
|
||||
exchangeCurrency.Hold = float64(account.Currencies[i].Hold)
|
||||
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
//If it all works out
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetAccountTrades(symbol string, startIndex, count int) (AlphapointTrades, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["startIndex"] = startIndex
|
||||
request["count"] = count
|
||||
|
||||
response := AlphapointTrades{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_ACCOUNT_TRADES, request, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetDepositAddresses() ([]AlphapointDepositAddresses, error) {
|
||||
type Response struct {
|
||||
Addresses []AlphapointDepositAddresses
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_DEPOSIT_ADDRESSES, map[string]interface{}{}, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return nil, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.Addresses, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) WithdrawCoins(symbol, product string, amount float64, address string) error {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["product"] = product
|
||||
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
request["sendToAddress"] = address
|
||||
|
||||
type Response struct {
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_WITHDRAW, request, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return errors.New(response.RejectReason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) CreateOrder(symbol, side string, orderType int, quantity, price float64) (int64, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["side"] = side
|
||||
request["orderType"] = orderType
|
||||
request["qty"] = strconv.FormatFloat(quantity, 'f', -1, 64)
|
||||
request["px"] = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
|
||||
type Response struct {
|
||||
ServerOrderID int64 `json:"serverOrderId"`
|
||||
DateTimeUTC float64 `json:"dateTimeUtc"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_CREATE_ORDER, request, &response)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.ServerOrderID, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) ModifyOrder(symbol string, OrderID, action int64) (int64, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["serverOrderId"] = OrderID
|
||||
request["modifyAction"] = action
|
||||
|
||||
type Response struct {
|
||||
ModifyOrderID int64 `json:"modifyOrderId"`
|
||||
ServerOrderID int64 `json:"serverOrderId"`
|
||||
DateTimeUTC float64 `json:"dateTimeUtc"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_MODIFY_ORDER, request, &response)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.ModifyOrderID, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) CancelOrder(symbol string, OrderID int64) (int64, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["serverOrderId"] = OrderID
|
||||
|
||||
type Response struct {
|
||||
CancelOrderID int64 `json:"cancelOrderId"`
|
||||
ServerOrderID int64 `json:"serverOrderId"`
|
||||
DateTimeUTC float64 `json:"dateTimeUtc"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_CANCEL_ORDER, request, &response)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.CancelOrderID, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) CancelAllOrders(symbol string) error {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
|
||||
type Response struct {
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_CANCEALLORDERS, request, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return errors.New(response.RejectReason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetOrders() ([]AlphapointOpenOrders, error) {
|
||||
response := AlphapointOrderInfo{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_OPEN_ORDERS, map[string]interface{}{}, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return nil, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.OpenOrders, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetOrderFee(symbol, side string, quantity, price float64) (float64, error) {
|
||||
type Response struct {
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
Fee float64 `json:"fee"`
|
||||
FeeProduct string `json:"feeProduct"`
|
||||
}
|
||||
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["side"] = side
|
||||
request["qty"] = strconv.FormatFloat(quantity, 'f', -1, 64)
|
||||
request["px"] = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_ORDER_FEE, request, &response)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.Fee, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) SendRequest(method, path string, data map[string]interface{}, result interface{}) error {
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
path = fmt.Sprintf("%s/ajax/v%s/%s", a.APIUrl, ALPHAPOINT_API_VERSION, path)
|
||||
PayloadJson, err := common.JSONEncode(data)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, path, headers, bytes.NewBuffer(PayloadJson))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[string]interface{}, result interface{}) error {
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
data["apiKey"] = a.APIKey
|
||||
nonce := time.Now().UnixNano()
|
||||
nonceStr := strconv.FormatInt(nonce, 10)
|
||||
data["apiNonce"] = nonce
|
||||
hmac := common.GetHMAC(common.HASH_SHA256, []byte(nonceStr+a.UserID+a.APIKey), []byte(a.APISecret))
|
||||
data["apiSig"] = common.StringToUpper(common.HexEncodeToString(hmac))
|
||||
path = fmt.Sprintf("%s/ajax/v%s/%s", a.APIUrl, ALPHAPOINT_API_VERSION, path)
|
||||
PayloadJson, err := common.JSONEncode(data)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, path, headers, bytes.NewBuffer(PayloadJson))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
71
exchanges/alphapoint/alphapointwebsocket.go
Normal file
71
exchanges/alphapoint/alphapointwebsocket.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package alphapoint
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
const (
|
||||
ALPHAPOINT_DEFAULT_WEBSOCKET_URL = "wss://sim3.alphapoint.com:8401/v1/GetTicker/"
|
||||
)
|
||||
|
||||
func (a *Alphapoint) WebsocketClient() {
|
||||
for a.ExchangeEnabled && a.WebsocketEnabled {
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
a.WebsocketConn, _, err = Dialer.Dial(a.WebsocketURL, http.Header{})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Error: %s\n", a.ExchangeName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if a.Verbose {
|
||||
log.Printf("%s Connected to Websocket.\n", a.ExchangeName)
|
||||
}
|
||||
|
||||
err = a.WebsocketConn.WriteMessage(websocket.TextMessage, []byte(`{"messageType": "logon"}`))
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
for a.ExchangeEnabled && a.WebsocketEnabled {
|
||||
msgType, resp, err := a.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
|
||||
switch msgType {
|
||||
case websocket.TextMessage:
|
||||
type MsgType struct {
|
||||
MessageType string `json:"messageType"`
|
||||
}
|
||||
|
||||
msgType := MsgType{}
|
||||
err := common.JSONDecode(resp, &msgType)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch msgType.MessageType {
|
||||
case "Ticker":
|
||||
ticker := AlphapointWebsocketTicker{}
|
||||
err = common.JSONDecode(resp, &ticker)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
a.WebsocketConn.Close()
|
||||
log.Printf("%s Websocket client disconnected.", a.ExchangeName)
|
||||
}
|
||||
}
|
||||
54
exchanges/anx/anx_types.go
Normal file
54
exchanges/anx/anx_types.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package anx
|
||||
|
||||
type ANXOrder struct {
|
||||
OrderType string `json:"orderType"`
|
||||
BuyTradedCurrency bool `json:"buyTradedCurrency"`
|
||||
TradedCurrency string `json:"tradedCurrency"`
|
||||
SettlementCurrency string `json:"settlementCurrency"`
|
||||
TradedCurrencyAmount string `json:"tradedCurrencyAmount"`
|
||||
SettlementCurrencyAmount string `json:"settlementCurrencyAmount"`
|
||||
LimitPriceInSettlementCurrency string `json:"limitPriceInSettlementCurrency"`
|
||||
ReplaceExistingOrderUUID string `json:"replaceExistingOrderUuid"`
|
||||
ReplaceOnlyIfActive bool `json:"replaceOnlyIfActive"`
|
||||
}
|
||||
|
||||
type ANXOrderResponse struct {
|
||||
BuyTradedCurrency bool `json:"buyTradedCurrency"`
|
||||
ExecutedAverageRate string `json:"executedAverageRate"`
|
||||
LimitPriceInSettlementCurrency string `json:"limitPriceInSettlementCurrency"`
|
||||
OrderID string `json:"orderId"`
|
||||
OrderStatus string `json:"orderStatus"`
|
||||
OrderType string `json:"orderType"`
|
||||
ReplaceExistingOrderUUID string `json:"replaceExistingOrderId"`
|
||||
SettlementCurrency string `json:"settlementCurrency"`
|
||||
SettlementCurrencyAmount string `json:"settlementCurrencyAmount"`
|
||||
SettlementCurrencyOutstanding string `json:"settlementCurrencyOutstanding"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
TradedCurrency string `json:"tradedCurrency"`
|
||||
TradedCurrencyAmount string `json:"tradedCurrencyAmount"`
|
||||
TradedCurrencyOutstanding string `json:"tradedCurrencyOutstanding"`
|
||||
}
|
||||
|
||||
type ANXTickerComponent struct {
|
||||
Currency string `json:"currency"`
|
||||
Display string `json:"display"`
|
||||
DisplayShort string `json:"display_short"`
|
||||
Value float64 `json:"value,string"`
|
||||
ValueInt int64 `json:"value_int,string"`
|
||||
}
|
||||
|
||||
type ANXTicker struct {
|
||||
Result string `json:"result"`
|
||||
Data struct {
|
||||
High ANXTickerComponent `json:"high"`
|
||||
Low ANXTickerComponent `json:"low"`
|
||||
Avg ANXTickerComponent `json:"avg"`
|
||||
Vwap ANXTickerComponent `json:"vwap"`
|
||||
Vol ANXTickerComponent `json:"vol"`
|
||||
Last ANXTickerComponent `json:"last"`
|
||||
Buy ANXTickerComponent `json:"buy"`
|
||||
Sell ANXTickerComponent `json:"sell"`
|
||||
Now string `json:"now"`
|
||||
UpdateTime string `json:"dataUpdateTime"`
|
||||
} `json:"data"`
|
||||
}
|
||||
67
exchanges/anx/anx_wrapper.go
Normal file
67
exchanges/anx/anx_wrapper.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package anx
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (a *ANX) Start() {
|
||||
go a.Run()
|
||||
}
|
||||
|
||||
func (a *ANX) Run() {
|
||||
if a.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", a.GetName(), a.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", a.GetName(), len(a.EnabledPairs), a.EnabledPairs)
|
||||
}
|
||||
|
||||
for a.Enabled {
|
||||
for _, x := range a.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := a.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("ANX %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(a.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * a.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ANX) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(a.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := a.GetTicker(currency)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Ask = tick.Data.Buy.Value
|
||||
tickerPrice.Bid = tick.Data.Sell.Value
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Low = tick.Data.Low.Value
|
||||
tickerPrice.Last = tick.Data.Last.Value
|
||||
tickerPrice.Volume = tick.Data.Vol.Value
|
||||
tickerPrice.High = tick.Data.High.Value
|
||||
ticker.ProcessTicker(a.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the ANX exchange
|
||||
func (e *ANX) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
return response, nil
|
||||
}
|
||||
338
exchanges/anx/anxhttp.go
Normal file
338
exchanges/anx/anxhttp.go
Normal file
@@ -0,0 +1,338 @@
|
||||
package anx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
ANX_API_URL = "https://anxpro.com/"
|
||||
ANX_API_VERSION = "3"
|
||||
ANX_APIKEY = "apiKey"
|
||||
ANX_DATA_TOKEN = "dataToken"
|
||||
ANX_ORDER_NEW = "order/new"
|
||||
ANX_ORDER_INFO = "order/info"
|
||||
ANX_SEND = "send"
|
||||
ANX_SUBACCOUNT_NEW = "subaccount/new"
|
||||
ANX_RECEIVE_ADDRESS = "receive"
|
||||
ANX_CREATE_ADDRESS = "receive/create"
|
||||
ANX_TICKER = "money/ticker"
|
||||
)
|
||||
|
||||
type ANX struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (a *ANX) SetDefaults() {
|
||||
a.Name = "ANX"
|
||||
a.Enabled = false
|
||||
a.TakerFee = 0.6
|
||||
a.MakerFee = 0.3
|
||||
a.Verbose = false
|
||||
a.Websocket = false
|
||||
a.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
//Setup is run on startup to setup exchange with config values
|
||||
func (a *ANX) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
a.SetEnabled(false)
|
||||
} else {
|
||||
a.Enabled = true
|
||||
a.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
a.SetAPIKeys(exch.APIKey, exch.APISecret, "", true)
|
||||
a.RESTPollingDelay = exch.RESTPollingDelay
|
||||
a.Verbose = exch.Verbose
|
||||
a.Websocket = exch.Websocket
|
||||
a.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
a.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
a.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ANX) GetFee(maker bool) float64 {
|
||||
if maker {
|
||||
return a.MakerFee
|
||||
} else {
|
||||
return a.TakerFee
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ANX) GetTicker(currency string) (ANXTicker, error) {
|
||||
var ticker ANXTicker
|
||||
err := common.SendHTTPGetRequest(fmt.Sprintf("%sapi/2/%s/%s", ANX_API_URL, currency, ANX_TICKER), true, &ticker)
|
||||
if err != nil {
|
||||
return ANXTicker{}, err
|
||||
}
|
||||
return ticker, nil
|
||||
}
|
||||
|
||||
func (a *ANX) GetAPIKey(username, password, otp, deviceID string) (string, string) {
|
||||
request := make(map[string]interface{})
|
||||
request["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
|
||||
request["username"] = username
|
||||
request["password"] = password
|
||||
|
||||
if otp != "" {
|
||||
request["otp"] = otp
|
||||
}
|
||||
|
||||
request["deviceId"] = deviceID
|
||||
|
||||
type APIKeyResponse struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
APISecret string `json:"apiSecret"`
|
||||
ResultCode string `json:"resultCode"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
var response APIKeyResponse
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(ANX_APIKEY, request, &response)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
if response.ResultCode != "OK" {
|
||||
log.Printf("Response code is not OK: %s\n", response.ResultCode)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return response.APIKey, response.APISecret
|
||||
}
|
||||
|
||||
func (a *ANX) GetDataToken() string {
|
||||
request := make(map[string]interface{})
|
||||
|
||||
type DataTokenResponse struct {
|
||||
ResultCode string `json:"resultCode"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Token string `json:"token"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
var response DataTokenResponse
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(ANX_DATA_TOKEN, request, &response)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if response.ResultCode != "OK" {
|
||||
log.Printf("Response code is not OK: %s\n", response.ResultCode)
|
||||
return ""
|
||||
}
|
||||
|
||||
return response.Token
|
||||
}
|
||||
|
||||
func (a *ANX) NewOrder(orderType string, buy bool, tradedCurrency, tradedCurrencyAmount, settlementCurrency, settlementCurrencyAmount, limitPriceSettlement string,
|
||||
replace bool, replaceUUID string, replaceIfActive bool) {
|
||||
request := make(map[string]interface{})
|
||||
|
||||
var order ANXOrder
|
||||
order.OrderType = orderType
|
||||
order.BuyTradedCurrency = buy
|
||||
|
||||
if buy {
|
||||
order.TradedCurrencyAmount = tradedCurrencyAmount
|
||||
} else {
|
||||
order.SettlementCurrencyAmount = settlementCurrencyAmount
|
||||
}
|
||||
|
||||
order.TradedCurrency = tradedCurrency
|
||||
order.SettlementCurrency = settlementCurrency
|
||||
order.LimitPriceInSettlementCurrency = limitPriceSettlement
|
||||
|
||||
if replace {
|
||||
order.ReplaceExistingOrderUUID = replaceUUID
|
||||
order.ReplaceOnlyIfActive = replaceIfActive
|
||||
}
|
||||
|
||||
request["order"] = order
|
||||
|
||||
type OrderResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
ResultCode string `json:"resultCode"`
|
||||
}
|
||||
var response OrderResponse
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(ANX_ORDER_NEW, request, &response)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if response.ResultCode != "OK" {
|
||||
log.Printf("Response code is not OK: %s\n", response.ResultCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ANX) OrderInfo(orderID string) (ANXOrderResponse, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["orderId"] = orderID
|
||||
|
||||
type OrderInfoResponse struct {
|
||||
Order ANXOrderResponse `json:"order"`
|
||||
ResultCode string `json:"resultCode"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
var response OrderInfoResponse
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(ANX_ORDER_INFO, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return ANXOrderResponse{}, err
|
||||
}
|
||||
|
||||
if response.ResultCode != "OK" {
|
||||
log.Printf("Response code is not OK: %s\n", response.ResultCode)
|
||||
return ANXOrderResponse{}, errors.New(response.ResultCode)
|
||||
}
|
||||
return response.Order, nil
|
||||
}
|
||||
|
||||
func (a *ANX) Send(currency, address, otp, amount string) (string, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ccy"] = currency
|
||||
request["amount"] = amount
|
||||
request["address"] = address
|
||||
|
||||
if otp != "" {
|
||||
request["otp"] = otp
|
||||
}
|
||||
|
||||
type SendResponse struct {
|
||||
TransactionID string `json:"transactionId"`
|
||||
ResultCode string `json:"resultCode"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
var response SendResponse
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(ANX_SEND, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if response.ResultCode != "OK" {
|
||||
log.Printf("Response code is not OK: %s\n", response.ResultCode)
|
||||
return "", errors.New(response.ResultCode)
|
||||
}
|
||||
return response.TransactionID, nil
|
||||
}
|
||||
|
||||
func (a *ANX) CreateNewSubAccount(currency, name string) (string, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ccy"] = currency
|
||||
request["customRef"] = name
|
||||
|
||||
type SubaccountResponse struct {
|
||||
SubAccount string `json:"subAccount"`
|
||||
ResultCode string `json:"resultCode"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
var response SubaccountResponse
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(ANX_SUBACCOUNT_NEW, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if response.ResultCode != "OK" {
|
||||
log.Printf("Response code is not OK: %s\n", response.ResultCode)
|
||||
return "", errors.New(response.ResultCode)
|
||||
}
|
||||
return response.SubAccount, nil
|
||||
}
|
||||
|
||||
func (a *ANX) GetDepositAddress(currency, name string, new bool) (string, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ccy"] = currency
|
||||
|
||||
if name != "" {
|
||||
request["subAccount"] = name
|
||||
}
|
||||
|
||||
type AddressResponse struct {
|
||||
Address string `json:"address"`
|
||||
SubAccount string `json:"subAccount"`
|
||||
ResultCode string `json:"resultCode"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
var response AddressResponse
|
||||
|
||||
path := ANX_RECEIVE_ADDRESS
|
||||
if new {
|
||||
path = ANX_CREATE_ADDRESS
|
||||
}
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(path, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if response.ResultCode != "OK" {
|
||||
log.Printf("Response code is not OK: %s\n", response.ResultCode)
|
||||
return "", errors.New(response.ResultCode)
|
||||
}
|
||||
|
||||
return response.Address, nil
|
||||
}
|
||||
|
||||
func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interface{}, result interface{}) (err error) {
|
||||
request := make(map[string]interface{})
|
||||
request["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
|
||||
path = fmt.Sprintf("api/%s/%s", ANX_API_VERSION, path)
|
||||
|
||||
if params != nil {
|
||||
for key, value := range params {
|
||||
request[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
PayloadJson, err := common.JSONEncode(request)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
if a.Verbose {
|
||||
log.Printf("Request JSON: %s\n", PayloadJson)
|
||||
}
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(path+string("\x00")+string(PayloadJson)), []byte(a.APISecret))
|
||||
headers := make(map[string]string)
|
||||
headers["Rest-Key"] = a.APIKey
|
||||
headers["Rest-Sign"] = common.Base64Encode([]byte(hmac))
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", ANX_API_URL+path, headers, bytes.NewBuffer(PayloadJson))
|
||||
|
||||
if a.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
|
||||
}
|
||||
298
exchanges/bitfinex/bitfinex_types.go
Normal file
298
exchanges/bitfinex/bitfinex_types.go
Normal file
@@ -0,0 +1,298 @@
|
||||
package bitfinex
|
||||
|
||||
type BitfinexStats struct {
|
||||
Period int64
|
||||
Volume float64 `json:",string"`
|
||||
}
|
||||
|
||||
type BitfinexTicker struct {
|
||||
Mid float64 `json:",string"`
|
||||
Bid float64 `json:",string"`
|
||||
Ask float64 `json:",string"`
|
||||
Last float64 `json:"Last_price,string"`
|
||||
Low float64 `json:",string"`
|
||||
High float64 `json:",string"`
|
||||
Volume float64 `json:",string"`
|
||||
Timestamp string
|
||||
}
|
||||
|
||||
type BitfinexMarginLimits struct {
|
||||
On_Pair string
|
||||
InitialMargin float64 `json:"initial_margin,string"`
|
||||
MarginRequirement float64 `json:"margin_requirement,string"`
|
||||
TradableBalance float64 `json:"tradable_balance,string"`
|
||||
}
|
||||
|
||||
type BitfinexMarginInfo struct {
|
||||
MarginBalance float64 `json:"margin_balance,string"`
|
||||
TradableBalance float64 `json:"tradable_balance,string"`
|
||||
UnrealizedPL int64 `json:"unrealized_pl"`
|
||||
UnrealizedSwap int64 `json:"unrealized_swap"`
|
||||
NetValue float64 `json:"net_value,string"`
|
||||
RequiredMargin int64 `json:"required_margin"`
|
||||
Leverage float64 `json:"leverage,string"`
|
||||
MarginRequirement float64 `json:"margin_requirement,string"`
|
||||
MarginLimits []BitfinexMarginLimits `json:"margin_limits"`
|
||||
Message string
|
||||
}
|
||||
|
||||
type BitfinexOrder struct {
|
||||
ID int64
|
||||
Symbol string
|
||||
Exchange string
|
||||
Price float64 `json:"price,string"`
|
||||
AverageExecutionPrice float64 `json:"avg_execution_price,string"`
|
||||
Side string
|
||||
Type string
|
||||
Timestamp string
|
||||
IsLive bool `json:"is_live"`
|
||||
IsCancelled bool `json:"is_cancelled"`
|
||||
IsHidden bool `json:"is_hidden"`
|
||||
WasForced bool `json:"was_forced"`
|
||||
OriginalAmount float64 `json:"original_amount,string"`
|
||||
RemainingAmount float64 `json:"remaining_amount,string"`
|
||||
ExecutedAmount float64 `json:"executed_amount,string"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BitfinexPlaceOrder struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Exchange string `json:"exchange"`
|
||||
Side string `json:"side"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type BitfinexBalance struct {
|
||||
Type string
|
||||
Currency string
|
||||
Amount float64 `json:"amount,string"`
|
||||
Available float64 `json:"available,string"`
|
||||
}
|
||||
|
||||
type BitfinexOffer struct {
|
||||
ID int64
|
||||
Currency string
|
||||
Rate float64 `json:"rate,string"`
|
||||
Period int64
|
||||
Direction string
|
||||
Timestamp string
|
||||
Type string
|
||||
IsLive bool `json:"is_live"`
|
||||
IsCancelled bool `json:"is_cancelled"`
|
||||
OriginalAmount float64 `json:"original_amount,string"`
|
||||
RemainingAmount float64 `json:"remaining_amount,string"`
|
||||
ExecutedAmount float64 `json:"remaining_amount,string"`
|
||||
}
|
||||
|
||||
type BitfinexBookStructure struct {
|
||||
Price, Amount, Timestamp string
|
||||
}
|
||||
|
||||
type BitfinexFee struct {
|
||||
Currency string
|
||||
TakerFees float64
|
||||
MakerFees float64
|
||||
}
|
||||
|
||||
type BitfinexOrderbook struct {
|
||||
Bids []BitfinexBookStructure
|
||||
Asks []BitfinexBookStructure
|
||||
}
|
||||
|
||||
type BitfinexTradeStructure struct {
|
||||
Timestamp, Tid int64
|
||||
Price, Amount, Exchange, Type string
|
||||
}
|
||||
|
||||
type BitfinexSymbolDetails struct {
|
||||
Pair string `json:"pair"`
|
||||
PricePrecision int `json:"price_precision"`
|
||||
InitialMargin float64 `json:"initial_margin,string"`
|
||||
MinimumMargin float64 `json:"minimum_margin,string"`
|
||||
MaximumOrderSize float64 `json:"maximum_order_size,string"`
|
||||
MinimumOrderSize float64 `json:"minimum_order_size,string"`
|
||||
Expiration string `json:"expiration"`
|
||||
}
|
||||
|
||||
type BitfinexLends struct {
|
||||
Rate float64 `json:"rate,string"`
|
||||
AmountLent float64 `json:"amount_lent,string"`
|
||||
AmountUsed float64 `json:"amount_used,string"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BitfinexAccountInfo struct {
|
||||
MakerFees string `json:"maker_fees"`
|
||||
TakerFees string `json:"taker_fees"`
|
||||
Fees []struct {
|
||||
Pairs string `json:"pairs"`
|
||||
MakerFees string `json:"maker_fees"`
|
||||
TakerFees string `json:"taker_fees"`
|
||||
} `json:"fees"`
|
||||
}
|
||||
|
||||
type BitfinexDepositResponse struct {
|
||||
Result string `json:"string"`
|
||||
Method string `json:"method"`
|
||||
Currency string `json:"currency"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type BitfinexOrderMultiResponse struct {
|
||||
Orders []BitfinexOrder `json:"order_ids"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type BitfinexLendbookBidAsk struct {
|
||||
Rate float64 `json:"rate,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Period int `json:"period"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
FlashReturnRate string `json:"frr"`
|
||||
}
|
||||
|
||||
type BitfinexLendbook struct {
|
||||
Bids []BitfinexLendbookBidAsk `json:"bids"`
|
||||
Asks []BitfinexLendbookBidAsk `json:"asks"`
|
||||
}
|
||||
|
||||
type BitfinexPosition struct {
|
||||
ID int64 `json:"id"`
|
||||
Symbol string `json:"string"`
|
||||
Status string `json:"active"`
|
||||
Base float64 `json:"base,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Swap float64 `json:"swap,string"`
|
||||
PL float64 `json:"pl,string"`
|
||||
}
|
||||
|
||||
type BitfinexBalanceHistory struct {
|
||||
Currency string `json:"currency"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Balance float64 `json:"balance,string"`
|
||||
Description string `json:"description"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BitfinexMovementHistory struct {
|
||||
ID int64 `json:"id"`
|
||||
Currency string `json:"currency"`
|
||||
Method string `json:"method"`
|
||||
Type string `json:"withdrawal"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BitfinexTradeHistory struct {
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Exchange string `json:"exchange"`
|
||||
Type string `json:"type"`
|
||||
FeeCurrency string `json:"fee_currency"`
|
||||
FeeAmount float64 `json:"fee_amount,string"`
|
||||
TID int64 `json:"tid"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BitfinexMarginFunds struct {
|
||||
ID int64 `json:"id"`
|
||||
PositionID int64 `json:"position_id"`
|
||||
Currency string `json:"currency"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Period int `json:"period"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BitfinexMarginTotalTakenFunds struct {
|
||||
PositionPair string `json:"position_pair"`
|
||||
TotalSwaps float64 `json:"total_swaps,string"`
|
||||
}
|
||||
|
||||
type BitfinexWalletTransfer struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type BitfinexWithdrawal struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
WithdrawalID int64 `json:"withdrawal_id"`
|
||||
}
|
||||
|
||||
type BitfinexGenericResponse struct {
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
type BitfinexWebsocketChanInfo struct {
|
||||
Channel string
|
||||
Pair string
|
||||
}
|
||||
|
||||
type BitfinexWebsocketBook struct {
|
||||
Price float64
|
||||
Count int
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketTrade struct {
|
||||
ID int64
|
||||
Timestamp int64
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketTicker struct {
|
||||
Bid float64
|
||||
BidSize float64
|
||||
Ask float64
|
||||
AskSize float64
|
||||
DailyChange float64
|
||||
DialyChangePerc float64
|
||||
LastPrice float64
|
||||
Volume float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketPosition struct {
|
||||
Pair string
|
||||
Status string
|
||||
Amount float64
|
||||
Price float64
|
||||
MarginFunding float64
|
||||
MarginFundingType int
|
||||
}
|
||||
|
||||
type BitfinexWebsocketWallet struct {
|
||||
Name string
|
||||
Currency string
|
||||
Balance float64
|
||||
UnsettledInterest float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketOrder struct {
|
||||
OrderID int64
|
||||
Pair string
|
||||
Amount float64
|
||||
OrigAmount float64
|
||||
OrderType string
|
||||
Status string
|
||||
Price float64
|
||||
PriceAvg float64
|
||||
Timestamp string
|
||||
Notify int
|
||||
}
|
||||
|
||||
type BitfinexWebsocketTradeExecuted struct {
|
||||
TradeID int64
|
||||
Pair string
|
||||
Timestamp int64
|
||||
OrderID int64
|
||||
AmountExecuted float64
|
||||
PriceExecuted float64
|
||||
}
|
||||
108
exchanges/bitfinex/bitfinex_wrapper.go
Normal file
108
exchanges/bitfinex/bitfinex_wrapper.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package bitfinex
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (b *Bitfinex) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
func (b *Bitfinex) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||
}
|
||||
|
||||
if b.Websocket {
|
||||
go b.WebsocketClient()
|
||||
}
|
||||
|
||||
exchangeProducts, err := b.GetSymbols()
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available symbols.\n", b.GetName())
|
||||
} else {
|
||||
log.Println(exchangeProducts)
|
||||
/*
|
||||
exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",")
|
||||
diff := common.StringSliceDifference(b.AvailablePairs, exchangeProducts)
|
||||
if len(diff) > 0 {
|
||||
exch, err := bot.config.GetExchangeConfig(b.Name)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Printf("%s Updating available pairs. Difference: %s.\n", b.Name, diff)
|
||||
exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",")
|
||||
bot.config.UpdateExchangeConfig(exch)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
for b.Enabled {
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := b.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Printf("Bitfinex %s Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(b.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tick, err := ticker.GetTicker(b.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tickerNew, err := b.GetTicker(currency, nil)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
tickerPrice.Ask = tickerNew.Ask
|
||||
tickerPrice.Bid = tickerNew.Bid
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Low = tickerNew.Low
|
||||
tickerPrice.Last = tickerNew.Last
|
||||
tickerPrice.Volume = tickerNew.Volume
|
||||
tickerPrice.High = tickerNew.High
|
||||
ticker.ProcessTicker(b.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Bitfinex exchange
|
||||
func (e *Bitfinex) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetAccountBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !e.Enabled {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Amount
|
||||
exchangeCurrency.Hold = accountBalance[i].Available
|
||||
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
630
exchanges/bitfinex/bitfinexhttp.go
Normal file
630
exchanges/bitfinex/bitfinexhttp.go
Normal file
@@ -0,0 +1,630 @@
|
||||
package bitfinex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
BITFINEX_API_URL = "https://api.bitfinex.com/v1/"
|
||||
BITFINEX_API_VERSION = "1"
|
||||
BITFINEX_TICKER = "pubticker/"
|
||||
BITFINEX_STATS = "stats/"
|
||||
BITFINEX_LENDBOOK = "lendbook/"
|
||||
BITFINEX_ORDERBOOK = "book/"
|
||||
BITFINEX_TRADES = "trades/"
|
||||
BITFINEX_LENDS = "lends/"
|
||||
BITFINEX_SYMBOLS = "symbols/"
|
||||
BITFINEX_SYMBOLS_DETAILS = "symbols_details/"
|
||||
BITFINEX_ACCOUNT_INFO = "account_infos"
|
||||
BITFINEX_DEPOSIT = "deposit/new"
|
||||
BITFINEX_ORDER_NEW = "order/new"
|
||||
BITFINEX_ORDER_NEW_MULTI = "order/new/multi"
|
||||
BITFINEX_ORDER_CANCEL = "order/cancel"
|
||||
BITFINEX_ORDER_CANCEL_MULTI = "order/cancel/multi"
|
||||
BITFINEX_ORDER_CANCEL_ALL = "order/cancel/all"
|
||||
BITFINEX_ORDER_CANCEL_REPLACE = "order/cancel/replace"
|
||||
BITFINEX_ORDER_STATUS = "order/status"
|
||||
BITFINEX_ORDERS = "orders"
|
||||
BITFINEX_POSITIONS = "positions"
|
||||
BITFINEX_CLAIM_POSITION = "position/claim"
|
||||
BITFINEX_HISTORY = "history"
|
||||
BITFINEX_HISTORY_MOVEMENTS = "history/movements"
|
||||
BITFINEX_TRADE_HISTORY = "mytrades"
|
||||
BITFINEX_OFFER_NEW = "offer/new"
|
||||
BITFINEX_OFFER_CANCEL = "offer/cancel"
|
||||
BITFINEX_OFFER_STATUS = "offer/status"
|
||||
BITFINEX_OFFERS = "offers"
|
||||
BITFINEX_MARGIN_ACTIVE_FUNDS = "taken_funds"
|
||||
BITFINEX_MARGIN_TOTAL_FUNDS = "total_taken_funds"
|
||||
BITFINEX_MARGIN_CLOSE = "funding/close"
|
||||
BITFINEX_BALANCES = "balances"
|
||||
BITFINEX_MARGIN_INFO = "margin_infos"
|
||||
BITFINEX_TRANSFER = "transfer"
|
||||
BITFINEX_WITHDRAWAL = "withdrawal"
|
||||
)
|
||||
|
||||
type Bitfinex struct {
|
||||
exchange.ExchangeBase
|
||||
WebsocketConn *websocket.Conn
|
||||
WebsocketSubdChannels map[int]BitfinexWebsocketChanInfo
|
||||
}
|
||||
|
||||
func (b *Bitfinex) SetDefaults() {
|
||||
b.Name = "Bitfinex"
|
||||
b.Enabled = false
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.WebsocketSubdChannels = make(map[int]BitfinexWebsocketChanInfo)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
} else {
|
||||
b.Enabled = true
|
||||
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
b.RESTPollingDelay = exch.RESTPollingDelay
|
||||
b.Verbose = exch.Verbose
|
||||
b.Websocket = exch.Websocket
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetTicker(symbol string, values url.Values) (BitfinexTicker, error) {
|
||||
path := common.EncodeURLValues(BITFINEX_API_URL+BITFINEX_TICKER+symbol, values)
|
||||
response := BitfinexTicker{}
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetStats(symbol string) (BitfinexStats, error) {
|
||||
response := BitfinexStats{}
|
||||
err := common.SendHTTPGetRequest(BITFINEX_API_URL+BITFINEX_STATS+symbol, true, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetLendbook(symbol string, values url.Values) (BitfinexLendbook, error) {
|
||||
path := common.EncodeURLValues(BITFINEX_API_URL+BITFINEX_LENDBOOK+symbol, values)
|
||||
response := BitfinexLendbook{}
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetOrderbook(symbol string, values url.Values) (BitfinexOrderbook, error) {
|
||||
path := common.EncodeURLValues(BITFINEX_API_URL+BITFINEX_ORDERBOOK+symbol, values)
|
||||
response := BitfinexOrderbook{}
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetTrades(symbol string, values url.Values) ([]BitfinexTradeStructure, error) {
|
||||
path := common.EncodeURLValues(BITFINEX_API_URL+BITFINEX_TRADES+symbol, values)
|
||||
response := []BitfinexTradeStructure{}
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetLends(symbol string, values url.Values) ([]BitfinexLends, error) {
|
||||
path := common.EncodeURLValues(BITFINEX_API_URL+BITFINEX_LENDS+symbol, values)
|
||||
response := []BitfinexLends{}
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetSymbols() ([]string, error) {
|
||||
products := []string{}
|
||||
err := common.SendHTTPGetRequest(BITFINEX_API_URL+BITFINEX_SYMBOLS, true, &products)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return products, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetSymbolsDetails() ([]BitfinexSymbolDetails, error) {
|
||||
response := []BitfinexSymbolDetails{}
|
||||
err := common.SendHTTPGetRequest(BITFINEX_API_URL+BITFINEX_SYMBOLS_DETAILS, true, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetAccountInfo() ([]BitfinexAccountInfo, error) {
|
||||
response := []BitfinexAccountInfo{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ACCOUNT_INFO, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) NewDeposit(method, walletName string, renew int) (BitfinexDepositResponse, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["method"] = method
|
||||
request["wallet_name"] = walletName
|
||||
request["renew"] = renew
|
||||
response := BitfinexDepositResponse{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_DEPOSIT, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) NewOrder(Symbol string, Amount float64, Price float64, Buy bool, Type string, Hidden bool) (BitfinexOrder, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["symbol"] = Symbol
|
||||
request["amount"] = strconv.FormatFloat(Amount, 'f', -1, 64)
|
||||
request["price"] = strconv.FormatFloat(Price, 'f', -1, 64)
|
||||
request["exchange"] = "bitfinex"
|
||||
|
||||
if Buy {
|
||||
request["side"] = "buy"
|
||||
} else {
|
||||
request["side"] = "sell"
|
||||
}
|
||||
|
||||
request["type"] = Type
|
||||
//request["is_hidden"] = Hidden
|
||||
|
||||
response := BitfinexOrder{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDER_NEW, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) NewOrderMulti(orders []BitfinexPlaceOrder) (BitfinexOrderMultiResponse, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["orders"] = orders
|
||||
|
||||
response := BitfinexOrderMultiResponse{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDER_NEW_MULTI, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) CancelOrder(OrderID int64) (BitfinexOrder, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["order_id"] = OrderID
|
||||
response := BitfinexOrder{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDER_CANCEL, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) CancelMultiplateOrders(OrderIDs []int64) (string, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["order_ids"] = OrderIDs
|
||||
response := BitfinexGenericResponse{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDER_CANCEL_MULTI, request, nil)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Result, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) CancelAllOrders() (string, error) {
|
||||
response := BitfinexGenericResponse{}
|
||||
err := b.SendAuthenticatedHTTPRequest("GET", BITFINEX_ORDER_CANCEL_ALL, nil, nil)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Result, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) ReplaceOrder(OrderID int64, Symbol string, Amount float64, Price float64, Buy bool, Type string, Hidden bool) (BitfinexOrder, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["order_id"] = OrderID
|
||||
request["symbol"] = Symbol
|
||||
request["amount"] = strconv.FormatFloat(Amount, 'f', -1, 64)
|
||||
request["price"] = strconv.FormatFloat(Price, 'f', -1, 64)
|
||||
request["exchange"] = "bitfinex"
|
||||
|
||||
if Buy {
|
||||
request["side"] = "buy"
|
||||
} else {
|
||||
request["side"] = "sell"
|
||||
}
|
||||
|
||||
request["type"] = Type
|
||||
//request["is_hidden"] = Hidden
|
||||
|
||||
response := BitfinexOrder{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDER_CANCEL_REPLACE, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetOrderStatus(OrderID int64) (BitfinexOrder, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["order_id"] = OrderID
|
||||
orderStatus := BitfinexOrder{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDER_STATUS, request, &orderStatus)
|
||||
|
||||
if err != nil {
|
||||
return orderStatus, err
|
||||
}
|
||||
|
||||
return orderStatus, err
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetActiveOrders() ([]BitfinexOrder, error) {
|
||||
response := []BitfinexOrder{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDERS, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetActivePositions() ([]BitfinexPosition, error) {
|
||||
response := []BitfinexPosition{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_POSITIONS, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) ClaimPosition(PositionID int) (BitfinexPosition, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["position_id"] = PositionID
|
||||
response := BitfinexPosition{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_CLAIM_POSITION, nil, nil)
|
||||
|
||||
if err != nil {
|
||||
return BitfinexPosition{}, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetBalanceHistory(symbol string, timeSince time.Time, timeUntil time.Time, limit int, wallet string) ([]BitfinexBalanceHistory, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["currency"] = symbol
|
||||
|
||||
if !timeSince.IsZero() {
|
||||
request["since"] = timeSince
|
||||
}
|
||||
|
||||
if !timeUntil.IsZero() {
|
||||
request["until"] = timeUntil
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
request["limit"] = limit
|
||||
}
|
||||
|
||||
if len(wallet) > 0 {
|
||||
request["wallet"] = wallet
|
||||
}
|
||||
|
||||
response := []BitfinexBalanceHistory{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_HISTORY, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetMovementHistory(symbol, method string, timeSince, timeUntil time.Time, limit int) ([]BitfinexMovementHistory, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["currency"] = symbol
|
||||
|
||||
if len(method) > 0 {
|
||||
request["method"] = method
|
||||
}
|
||||
|
||||
if !timeSince.IsZero() {
|
||||
request["since"] = timeSince
|
||||
}
|
||||
|
||||
if !timeUntil.IsZero() {
|
||||
request["until"] = timeUntil
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
request["limit"] = limit
|
||||
}
|
||||
|
||||
response := []BitfinexMovementHistory{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_HISTORY_MOVEMENTS, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetTradeHistory(symbol string, timestamp, until time.Time, limit, reverse int) ([]BitfinexTradeHistory, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["currency"] = symbol
|
||||
request["timestamp"] = timestamp
|
||||
|
||||
if !until.IsZero() {
|
||||
request["until"] = until
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
request["limit"] = limit
|
||||
}
|
||||
|
||||
if reverse > 0 {
|
||||
request["reverse"] = reverse
|
||||
}
|
||||
|
||||
response := []BitfinexTradeHistory{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_TRADE_HISTORY, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) NewOffer(symbol string, amount, rate float64, period int64, direction string) int64 {
|
||||
request := make(map[string]interface{})
|
||||
request["currency"] = symbol
|
||||
request["amount"] = amount
|
||||
request["rate"] = rate
|
||||
request["period"] = period
|
||||
request["direction"] = direction
|
||||
|
||||
type OfferResponse struct {
|
||||
Offer_Id int64
|
||||
}
|
||||
|
||||
response := OfferResponse{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_OFFER_NEW, request, &response)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return 0
|
||||
}
|
||||
|
||||
return response.Offer_Id
|
||||
}
|
||||
|
||||
func (b *Bitfinex) CancelOffer(OfferID int64) (BitfinexOffer, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["offer_id"] = OfferID
|
||||
response := BitfinexOffer{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_OFFER_CANCEL, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetOfferStatus(OfferID int64) (BitfinexOffer, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["offer_id"] = OfferID
|
||||
response := BitfinexOffer{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_ORDER_STATUS, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetActiveOffers() ([]BitfinexOffer, error) {
|
||||
response := []BitfinexOffer{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_OFFERS, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetActiveMarginFunding() ([]BitfinexMarginFunds, error) {
|
||||
response := []BitfinexMarginFunds{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_MARGIN_ACTIVE_FUNDS, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetMarginTotalTakenFunds() ([]BitfinexMarginTotalTakenFunds, error) {
|
||||
response := []BitfinexMarginTotalTakenFunds{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_MARGIN_TOTAL_FUNDS, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) CloseMarginFunding(SwapID int64) (BitfinexOffer, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["swap_id"] = SwapID
|
||||
response := BitfinexOffer{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_MARGIN_CLOSE, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetAccountBalance() ([]BitfinexBalance, error) {
|
||||
response := []BitfinexBalance{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_BALANCES, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetMarginInfo() ([]BitfinexMarginInfo, error) {
|
||||
response := []BitfinexMarginInfo{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_MARGIN_INFO, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WalletTransfer(amount float64, currency, walletFrom, walletTo string) ([]BitfinexWalletTransfer, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["amount"] = amount
|
||||
request["currency"] = currency
|
||||
request["walletfrom"] = walletFrom
|
||||
request["walletTo"] = walletTo
|
||||
|
||||
response := []BitfinexWalletTransfer{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_TRANSFER, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) Withdrawal(withdrawType, wallet, address string, amount float64) ([]BitfinexWithdrawal, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["withdrawal_type"] = withdrawType
|
||||
request["walletselected"] = wallet
|
||||
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
request["address"] = address
|
||||
|
||||
response := []BitfinexWithdrawal{}
|
||||
err := b.SendAuthenticatedHTTPRequest("POST", BITFINEX_WITHDRAWAL, request, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
|
||||
request := make(map[string]interface{})
|
||||
request["request"] = fmt.Sprintf("/v%s/%s", BITFINEX_API_VERSION, path)
|
||||
request["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
|
||||
if params != nil {
|
||||
for key, value := range params {
|
||||
request[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
PayloadJson, err := common.JSONEncode(request)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Request JSON: %s\n", PayloadJson)
|
||||
}
|
||||
|
||||
PayloadBase64 := common.Base64Encode(PayloadJson)
|
||||
hmac := common.GetHMAC(common.HASH_SHA512_384, []byte(PayloadBase64), []byte(b.APISecret))
|
||||
headers := make(map[string]string)
|
||||
headers["X-BFX-APIKEY"] = b.APIKey
|
||||
headers["X-BFX-PAYLOAD"] = PayloadBase64
|
||||
headers["X-BFX-SIGNATURE"] = common.HexEncodeToString(hmac)
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, BITFINEX_API_URL+path, headers, strings.NewReader(""))
|
||||
|
||||
if b.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
|
||||
}
|
||||
284
exchanges/bitfinex/bitfinexwebsocket.go
Normal file
284
exchanges/bitfinex/bitfinexwebsocket.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package bitfinex
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
const (
|
||||
BITFINEX_WEBSOCKET = "wss://api2.bitfinex.com:3000/ws"
|
||||
BITFINEX_WEBSOCKET_VERSION = "1.1"
|
||||
BITFINEX_WEBSOCKET_POSITION_SNAPSHOT = "ps"
|
||||
BITFINEX_WEBSOCKET_POSITION_NEW = "pn"
|
||||
BITFINEX_WEBSOCKET_POSITION_UPDATE = "pu"
|
||||
BITFINEX_WEBSOCKET_POSITION_CLOSE = "pc"
|
||||
BITFINEX_WEBSOCKET_WALLET_SNAPSHOT = "ws"
|
||||
BITFINEX_WEBSOCKET_WALLET_UPDATE = "wu"
|
||||
BITFINEX_WEBSOCKET_ORDER_SNAPSHOT = "os"
|
||||
BITFINEX_WEBSOCKET_ORDER_NEW = "on"
|
||||
BITFINEX_WEBSOCKET_ORDER_UPDATE = "ou"
|
||||
BITFINEX_WEBSOCKET_ORDER_CANCEL = "oc"
|
||||
BITFINEX_WEBSOCKET_TRADE_EXECUTED = "te"
|
||||
BITFINEX_WEBSOCKET_HEARTBEAT = "hb"
|
||||
)
|
||||
|
||||
func (b *Bitfinex) WebsocketPingHandler() error {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "ping"
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSend(data interface{}) error {
|
||||
json, err := common.JSONEncode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.WebsocketConn.WriteMessage(websocket.TextMessage, json)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSubscribe(channel string, params map[string]string) {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "subscribe"
|
||||
request["channel"] = channel
|
||||
|
||||
if len(params) > 0 {
|
||||
for k, v := range params {
|
||||
request[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSendAuth() error {
|
||||
request := make(map[string]interface{})
|
||||
payload := "AUTH" + strconv.FormatInt(time.Now().UnixNano(), 10)[:13]
|
||||
request["event"] = "auth"
|
||||
request["apiKey"] = b.APIKey
|
||||
request["authSig"] = common.HexEncodeToString(common.GetHMAC(common.HASH_SHA512_384, []byte(payload), []byte(b.APISecret)))
|
||||
request["authPayload"] = payload
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSendUnauth() error {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "unauth"
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketAddSubscriptionChannel(chanID int, channel, pair string) {
|
||||
chanInfo := BitfinexWebsocketChanInfo{Pair: pair, Channel: channel}
|
||||
b.WebsocketSubdChannels[chanID] = chanInfo
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("%s Subscribed to Channel: %s Pair: %s ChannelID: %d\n", b.GetName(), channel, pair, chanID)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketClient() {
|
||||
channels := []string{"book", "trades", "ticker"}
|
||||
for b.Enabled && b.Websocket {
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
b.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Error: %s\n", b.GetName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
msgType, resp, err := b.WebsocketConn.ReadMessage()
|
||||
if msgType != websocket.TextMessage {
|
||||
continue
|
||||
}
|
||||
|
||||
type WebsocketHandshake struct {
|
||||
Event string `json:"event"`
|
||||
Code int64 `json:"code"`
|
||||
Version float64 `json:"version"`
|
||||
}
|
||||
|
||||
hs := WebsocketHandshake{}
|
||||
err = common.JSONDecode(resp, &hs)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if hs.Event == "info" {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Connected to Websocket.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
for _, x := range channels {
|
||||
for _, y := range b.EnabledPairs {
|
||||
params := make(map[string]string)
|
||||
if x == "book" {
|
||||
params["prec"] = "P0"
|
||||
}
|
||||
params["pair"] = y
|
||||
b.WebsocketSubscribe(x, params)
|
||||
}
|
||||
}
|
||||
|
||||
if b.AuthenticatedAPISupport {
|
||||
err = b.WebsocketSendAuth()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
for b.Enabled && b.Websocket {
|
||||
msgType, resp, err := b.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
|
||||
switch msgType {
|
||||
case websocket.TextMessage:
|
||||
var result interface{}
|
||||
err := common.JSONDecode(resp, &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch reflect.TypeOf(result).String() {
|
||||
case "map[string]interface {}":
|
||||
eventData := result.(map[string]interface{})
|
||||
event := eventData["event"]
|
||||
|
||||
switch event {
|
||||
case "subscribed":
|
||||
b.WebsocketAddSubscriptionChannel(int(eventData["chanId"].(float64)), eventData["channel"].(string), eventData["pair"].(string))
|
||||
case "auth":
|
||||
status := eventData["status"].(string)
|
||||
|
||||
if status == "OK" {
|
||||
b.WebsocketAddSubscriptionChannel(0, "account", "N/A")
|
||||
} else if status == "fail" {
|
||||
log.Printf("%s Websocket unable to AUTH. Error code: %s\n", b.GetName(), eventData["code"].(string))
|
||||
b.AuthenticatedAPISupport = false
|
||||
}
|
||||
}
|
||||
case "[]interface {}":
|
||||
chanData := result.([]interface{})
|
||||
chanID := int(chanData[0].(float64))
|
||||
chanInfo, ok := b.WebsocketSubdChannels[chanID]
|
||||
|
||||
if !ok {
|
||||
log.Printf("Unable to locate chanID: %d\n", chanID)
|
||||
} else {
|
||||
if len(chanData) == 2 {
|
||||
if reflect.TypeOf(chanData[1]).String() == "string" {
|
||||
if chanData[1].(string) == BITFINEX_WEBSOCKET_HEARTBEAT {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
switch chanInfo.Channel {
|
||||
case "book":
|
||||
orderbook := []BitfinexWebsocketBook{}
|
||||
switch len(chanData) {
|
||||
case 2:
|
||||
data := chanData[1].([]interface{})
|
||||
for _, x := range data {
|
||||
y := x.([]interface{})
|
||||
orderbook = append(orderbook, BitfinexWebsocketBook{Price: y[0].(float64), Count: int(y[1].(float64)), Amount: y[2].(float64)})
|
||||
}
|
||||
case 4:
|
||||
orderbook = append(orderbook, BitfinexWebsocketBook{Price: chanData[1].(float64), Count: int(chanData[2].(float64)), Amount: chanData[3].(float64)})
|
||||
}
|
||||
case "ticker":
|
||||
ticker := BitfinexWebsocketTicker{Bid: chanData[1].(float64), BidSize: chanData[2].(float64), Ask: chanData[3].(float64), AskSize: chanData[4].(float64),
|
||||
DailyChange: chanData[5].(float64), DialyChangePerc: chanData[6].(float64), LastPrice: chanData[7].(float64), Volume: chanData[8].(float64)}
|
||||
|
||||
log.Printf("Bitfinex %s Websocket Last %f Volume %f\n", chanInfo.Pair, ticker.LastPrice, ticker.Volume)
|
||||
case "account":
|
||||
switch chanData[1].(string) {
|
||||
case BITFINEX_WEBSOCKET_POSITION_SNAPSHOT:
|
||||
positionSnapshot := []BitfinexWebsocketPosition{}
|
||||
data := chanData[2].([]interface{})
|
||||
for _, x := range data {
|
||||
y := x.([]interface{})
|
||||
positionSnapshot = append(positionSnapshot, BitfinexWebsocketPosition{Pair: y[0].(string), Status: y[1].(string), Amount: y[2].(float64), Price: y[3].(float64),
|
||||
MarginFunding: y[4].(float64), MarginFundingType: int(y[5].(float64))})
|
||||
}
|
||||
log.Println(positionSnapshot)
|
||||
case BITFINEX_WEBSOCKET_POSITION_NEW, BITFINEX_WEBSOCKET_POSITION_UPDATE, BITFINEX_WEBSOCKET_POSITION_CLOSE:
|
||||
data := chanData[2].([]interface{})
|
||||
position := BitfinexWebsocketPosition{Pair: data[0].(string), Status: data[1].(string), Amount: data[2].(float64), Price: data[3].(float64),
|
||||
MarginFunding: data[4].(float64), MarginFundingType: int(data[5].(float64))}
|
||||
log.Println(position)
|
||||
case BITFINEX_WEBSOCKET_WALLET_SNAPSHOT:
|
||||
data := chanData[2].([]interface{})
|
||||
walletSnapshot := []BitfinexWebsocketWallet{}
|
||||
for _, x := range data {
|
||||
y := x.([]interface{})
|
||||
walletSnapshot = append(walletSnapshot, BitfinexWebsocketWallet{Name: y[0].(string), Currency: y[1].(string), Balance: y[2].(float64), UnsettledInterest: y[3].(float64)})
|
||||
}
|
||||
log.Println(walletSnapshot)
|
||||
case BITFINEX_WEBSOCKET_WALLET_UPDATE:
|
||||
data := chanData[2].([]interface{})
|
||||
wallet := BitfinexWebsocketWallet{Name: data[0].(string), Currency: data[1].(string), Balance: data[2].(float64), UnsettledInterest: data[3].(float64)}
|
||||
log.Println(wallet)
|
||||
case BITFINEX_WEBSOCKET_ORDER_SNAPSHOT:
|
||||
orderSnapshot := []BitfinexWebsocketOrder{}
|
||||
data := chanData[2].([]interface{})
|
||||
for _, x := range data {
|
||||
y := x.([]interface{})
|
||||
orderSnapshot = append(orderSnapshot, BitfinexWebsocketOrder{OrderID: int64(y[0].(float64)), Pair: y[1].(string), Amount: y[2].(float64), OrigAmount: y[3].(float64),
|
||||
OrderType: y[4].(string), Status: y[5].(string), Price: y[6].(float64), PriceAvg: y[7].(float64), Timestamp: y[8].(string)})
|
||||
}
|
||||
log.Println(orderSnapshot)
|
||||
case BITFINEX_WEBSOCKET_ORDER_NEW, BITFINEX_WEBSOCKET_ORDER_UPDATE, BITFINEX_WEBSOCKET_ORDER_CANCEL:
|
||||
data := chanData[2].([]interface{})
|
||||
order := BitfinexWebsocketOrder{OrderID: int64(data[0].(float64)), Pair: data[1].(string), Amount: data[2].(float64), OrigAmount: data[3].(float64),
|
||||
OrderType: data[4].(string), Status: data[5].(string), Price: data[6].(float64), PriceAvg: data[7].(float64), Timestamp: data[8].(string), Notify: int(data[9].(float64))}
|
||||
log.Println(order)
|
||||
case BITFINEX_WEBSOCKET_TRADE_EXECUTED:
|
||||
data := chanData[2].([]interface{})
|
||||
trade := BitfinexWebsocketTradeExecuted{TradeID: int64(data[0].(float64)), Pair: data[1].(string), Timestamp: int64(data[2].(float64)), OrderID: int64(data[3].(float64)),
|
||||
AmountExecuted: data[4].(float64), PriceExecuted: data[5].(float64)}
|
||||
log.Println(trade)
|
||||
}
|
||||
case "trades":
|
||||
trades := []BitfinexWebsocketTrade{}
|
||||
switch len(chanData) {
|
||||
case 2:
|
||||
data := chanData[1].([]interface{})
|
||||
for _, x := range data {
|
||||
y := x.([]interface{})
|
||||
trades = append(trades, BitfinexWebsocketTrade{ID: int64(y[0].(float64)), Timestamp: int64(y[1].(float64)), Price: y[2].(float64), Amount: y[3].(float64)})
|
||||
}
|
||||
case 5:
|
||||
trade := BitfinexWebsocketTrade{ID: int64(chanData[1].(float64)), Timestamp: int64(chanData[2].(float64)), Price: chanData[3].(float64), Amount: chanData[4].(float64)}
|
||||
trades = append(trades, trade)
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Bitfinex %s Websocket Trade ID %d Timestamp %d Price %f Amount %f\n", chanInfo.Pair, trade.ID, trade.Timestamp, trade.Price, trade.Amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WebsocketConn.Close()
|
||||
log.Printf("%s Websocket client disconnected.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
109
exchanges/bitstamp/bitstamp_types.go
Normal file
109
exchanges/bitstamp/bitstamp_types.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package bitstamp
|
||||
|
||||
type BitstampTicker struct {
|
||||
Last float64 `json:"last,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Vwap float64 `json:"vwap,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
Bid float64 `json:"bid,string"`
|
||||
Ask float64 `json:"ask,string"`
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Open float64 `json:"open,string"`
|
||||
}
|
||||
|
||||
type BitstampBalances struct {
|
||||
BTCReserved float64 `json:"btc_reserved,string"`
|
||||
BTCEURFee float64 `json:"btceur_fee,string"`
|
||||
BTCAvailable float64 `json:"btc_available,string"`
|
||||
XRPAvailable float64 `json:"xrp_available,string"`
|
||||
EURAvailable float64 `json:"eur_available,string"`
|
||||
USDReserved float64 `json:"usd_reserved,string"`
|
||||
EURReserved float64 `json:"eur_reserved,string"`
|
||||
XRPEURFee float64 `json:"xrpeur_fee,string"`
|
||||
XRPReserved float64 `json:"xrp_reserved,string"`
|
||||
XRPBalance float64 `json:"xrp_balance,string"`
|
||||
XRPUSDFee float64 `json:"xrpusd_fee,string"`
|
||||
EURBalance float64 `json:"eur_balance,string"`
|
||||
BTCBalance float64 `json:"btc_balance,string"`
|
||||
BTCUSDFee float64 `json:"btcusd_fee,string"`
|
||||
USDBalance float64 `json:"usd_balance,string"`
|
||||
USDAvailable float64 `json:"usd_available,string"`
|
||||
EURUSDFee float64 `json:"eurusd_fee,string"`
|
||||
}
|
||||
|
||||
type BitstampOrderbookBase struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BitstampOrderbook struct {
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Bids []BitstampOrderbookBase
|
||||
Asks []BitstampOrderbookBase
|
||||
}
|
||||
|
||||
type BitstampTransactions struct {
|
||||
Date int64 `json:"date,string"`
|
||||
TradeID int64 `json:"tid,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Type int `json:"type,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
}
|
||||
|
||||
type BitstampEURUSDConversionRate struct {
|
||||
Buy float64 `json:"buy,string"`
|
||||
Sell float64 `json:"sell,string"`
|
||||
}
|
||||
|
||||
type BitstampUserTransactions struct {
|
||||
Date string `json:"datetime"`
|
||||
TransID int64 `json:"id"`
|
||||
Type int `json:"type,string"`
|
||||
USD float64 `json:"usd"`
|
||||
EUR float64 `json:"eur"`
|
||||
BTC float64 `json:"btc"`
|
||||
XRP float64 `json:"xrp"`
|
||||
BTCUSD float64 `json:"btc_usd"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BitstampOrder struct {
|
||||
ID int64 `json:"id"`
|
||||
Date string `json:"datetime"`
|
||||
Type int `json:"type"`
|
||||
Price float64 `json:"price"`
|
||||
Amount float64 `json:"amount"`
|
||||
}
|
||||
|
||||
type BitstampOrderStatus struct {
|
||||
Status string
|
||||
Transactions []struct {
|
||||
TradeID int64 `json:"tid"`
|
||||
USD float64 `json:"usd,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
BTC float64 `json:"btc,string"`
|
||||
}
|
||||
}
|
||||
|
||||
type BitstampWithdrawalRequests struct {
|
||||
OrderID int64 `json:"id"`
|
||||
Date string `json:"datetime"`
|
||||
Type int `json:"type"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Status int `json:"status"`
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
type BitstampUnconfirmedBTCTransactions struct {
|
||||
Amount float64 `json:"amount,string"`
|
||||
Address string `json:"address"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
}
|
||||
|
||||
type BitstampXRPDepositResponse struct {
|
||||
Address string `json:"address"`
|
||||
DestinationTag int64 `json:"destination_tag"`
|
||||
}
|
||||
90
exchanges/bitstamp/bitstamp_wrapper.go
Normal file
90
exchanges/bitstamp/bitstamp_wrapper.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package bitstamp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (b *Bitstamp) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
func (b *Bitstamp) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||
}
|
||||
|
||||
if b.Websocket {
|
||||
go b.PusherClient()
|
||||
}
|
||||
|
||||
for b.Enabled {
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := b.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("Bitstamp %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(b.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := b.GetTicker(currency, false)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
|
||||
}
|
||||
tickerPrice.Ask = tick.Ask
|
||||
tickerPrice.Bid = tick.Bid
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Volume
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(b.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Bitstamp exchange
|
||||
func (e *Bitstamp) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
var btcExchangeInfo exchange.ExchangeAccountCurrencyInfo
|
||||
btcExchangeInfo.CurrencyName = "BTC"
|
||||
btcExchangeInfo.TotalValue = accountBalance.BTCBalance
|
||||
btcExchangeInfo.Hold = accountBalance.BTCReserved
|
||||
response.Currencies = append(response.Currencies, btcExchangeInfo)
|
||||
|
||||
var usdExchangeInfo exchange.ExchangeAccountCurrencyInfo
|
||||
usdExchangeInfo.CurrencyName = "USD"
|
||||
usdExchangeInfo.TotalValue = accountBalance.USDBalance
|
||||
usdExchangeInfo.Hold = accountBalance.USDReserved
|
||||
response.Currencies = append(response.Currencies, usdExchangeInfo)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
511
exchanges/bitstamp/bitstamphttp.go
Normal file
511
exchanges/bitstamp/bitstamphttp.go
Normal file
@@ -0,0 +1,511 @@
|
||||
package bitstamp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
BITSTAMP_API_URL = "https://www.bitstamp.net/api"
|
||||
BITSTAMP_API_VERSION = "2"
|
||||
BITSTAMP_API_TICKER = "ticker"
|
||||
BITSTAMP_API_TICKER_HOURLY = "ticker_hour"
|
||||
BITSTAMP_API_ORDERBOOK = "order_book"
|
||||
BITSTAMP_API_TRANSACTIONS = "transactions"
|
||||
BITSTAMP_API_EURUSD = "eur_usd"
|
||||
BITSTAMP_API_BALANCE = "balance"
|
||||
BITSTAMP_API_USER_TRANSACTIONS = "user_transactions"
|
||||
BITSTAMP_API_OPEN_ORDERS = "open_orders"
|
||||
BITSTAMP_API_ORDER_STATUS = "order_status"
|
||||
BITSTAMP_API_CANCEL_ORDER = "cancel_order"
|
||||
BITSTAMP_API_CANCEL_ALL_ORDERS = "cancel_all_orders"
|
||||
BITSTAMP_API_BUY = "buy"
|
||||
BITSTAMP_API_SELL = "sell"
|
||||
BITSTAMP_API_MARKET = "market"
|
||||
BITSTAMP_API_WITHDRAWAL_REQUESTS = "withdrawal_requests"
|
||||
BITSTAMP_API_BITCOIN_WITHDRAWAL = "bitcoin_withdrawal"
|
||||
BITSTAMP_API_BITCOIN_DEPOSIT = "bitcoin_deposit_address"
|
||||
BITSTAMP_API_UNCONFIRMED_BITCOIN = "unconfirmed_btc"
|
||||
BITSTAMP_API_RIPPLE_WITHDRAWAL = "ripple_withdrawal"
|
||||
BITSTAMP_API_RIPPLE_DESPOIT = "ripple_address"
|
||||
BITSTAMP_API_TRANSFER_TO_MAIN = "transfer-to-main"
|
||||
BITSTAMP_API_TRANSFER_FROM_MAIN = "transfer-from-main"
|
||||
BITSTAMP_API_XRP_WITHDRAWAL = "xrp_withdrawal"
|
||||
BITSTAMP_API_XRP_DESPOIT = "xrp_address"
|
||||
)
|
||||
|
||||
type Bitstamp struct {
|
||||
exchange.ExchangeBase
|
||||
Balance BitstampBalances
|
||||
}
|
||||
|
||||
func (b *Bitstamp) SetDefaults() {
|
||||
b.Name = "Bitstamp"
|
||||
b.Enabled = false
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (b *Bitstamp) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
} else {
|
||||
b.Enabled = true
|
||||
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
b.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false)
|
||||
b.RESTPollingDelay = exch.RESTPollingDelay
|
||||
b.Verbose = exch.Verbose
|
||||
b.Websocket = exch.Websocket
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetFee(currency string) float64 {
|
||||
switch currency {
|
||||
case "BTCUSD":
|
||||
return b.Balance.BTCUSDFee
|
||||
case "BTCEUR":
|
||||
return b.Balance.BTCEURFee
|
||||
case "XRPEUR":
|
||||
return b.Balance.XRPEURFee
|
||||
case "XRPUSD":
|
||||
return b.Balance.XRPUSDFee
|
||||
case "EURUSD":
|
||||
return b.Balance.EURUSDFee
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetTicker(currency string, hourly bool) (BitstampTicker, error) {
|
||||
tickerEndpoint := BITSTAMP_API_TICKER
|
||||
if hourly {
|
||||
tickerEndpoint = BITSTAMP_API_TICKER_HOURLY
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/v%s/%s/%s/", BITSTAMP_API_URL, BITSTAMP_API_VERSION, tickerEndpoint, common.StringToLower(currency))
|
||||
ticker := BitstampTicker{}
|
||||
|
||||
err := common.SendHTTPGetRequest(path, true, &ticker)
|
||||
|
||||
if err != nil {
|
||||
return ticker, err
|
||||
}
|
||||
|
||||
return ticker, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetOrderbook(currency string) (BitstampOrderbook, error) {
|
||||
type response struct {
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Bids [][]string
|
||||
Asks [][]string
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
path := fmt.Sprintf("%s/v%s/%s/%s/", BITSTAMP_API_URL, BITSTAMP_API_VERSION, BITSTAMP_API_ORDERBOOK, common.StringToLower(currency))
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
if err != nil {
|
||||
return BitstampOrderbook{}, err
|
||||
}
|
||||
|
||||
orderbook := BitstampOrderbook{}
|
||||
orderbook.Timestamp = resp.Timestamp
|
||||
|
||||
for _, x := range resp.Bids {
|
||||
price, err := strconv.ParseFloat(x[0], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat(x[1], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Bids = append(orderbook.Bids, BitstampOrderbookBase{price, amount})
|
||||
}
|
||||
|
||||
for _, x := range resp.Asks {
|
||||
price, err := strconv.ParseFloat(x[0], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat(x[1], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Asks = append(orderbook.Asks, BitstampOrderbookBase{price, amount})
|
||||
}
|
||||
|
||||
return orderbook, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetTransactions(currency string, values url.Values) ([]BitstampTransactions, error) {
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s/", BITSTAMP_API_URL, BITSTAMP_API_VERSION, BITSTAMP_API_TRANSACTIONS, common.StringToLower(currency)), values)
|
||||
transactions := []BitstampTransactions{}
|
||||
err := common.SendHTTPGetRequest(path, true, &transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetEURUSDConversionRate() (BitstampEURUSDConversionRate, error) {
|
||||
rate := BitstampEURUSDConversionRate{}
|
||||
path := fmt.Sprintf("%s/%s", BITSTAMP_API_URL, BITSTAMP_API_EURUSD)
|
||||
err := common.SendHTTPGetRequest(path, true, &rate)
|
||||
|
||||
if err != nil {
|
||||
return rate, err
|
||||
}
|
||||
return rate, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetBalance() (BitstampBalances, error) {
|
||||
balance := BitstampBalances{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BALANCE, true, url.Values{}, &balance)
|
||||
|
||||
if err != nil {
|
||||
return balance, err
|
||||
}
|
||||
return balance, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetUserTransactions(values url.Values) ([]BitstampUserTransactions, error) {
|
||||
type Response struct {
|
||||
Date string `json:"datetime"`
|
||||
TransID int64 `json:"id"`
|
||||
Type int `json:"type,string"`
|
||||
USD interface{} `json:"usd"`
|
||||
EUR float64 `json:"eur"`
|
||||
XRP float64 `json:"xrp"`
|
||||
BTC interface{} `json:"btc"`
|
||||
BTCUSD interface{} `json:"btc_usd"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
response := []Response{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_USER_TRANSACTIONS, true, values, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions := []BitstampUserTransactions{}
|
||||
|
||||
for _, y := range response {
|
||||
tx := BitstampUserTransactions{}
|
||||
tx.Date = y.Date
|
||||
tx.TransID = y.TransID
|
||||
tx.Type = y.Type
|
||||
|
||||
/* Hack due to inconsistent JSON values... */
|
||||
varType := reflect.TypeOf(y.USD).String()
|
||||
if varType == "string" {
|
||||
tx.USD, _ = strconv.ParseFloat(y.USD.(string), 64)
|
||||
} else {
|
||||
tx.USD = y.USD.(float64)
|
||||
}
|
||||
|
||||
tx.EUR = y.EUR
|
||||
tx.XRP = y.XRP
|
||||
|
||||
varType = reflect.TypeOf(y.BTC).String()
|
||||
if varType == "string" {
|
||||
tx.BTC, _ = strconv.ParseFloat(y.BTC.(string), 64)
|
||||
} else {
|
||||
tx.BTC = y.BTC.(float64)
|
||||
}
|
||||
|
||||
varType = reflect.TypeOf(y.BTCUSD).String()
|
||||
if varType == "string" {
|
||||
tx.BTCUSD, _ = strconv.ParseFloat(y.BTCUSD.(string), 64)
|
||||
} else {
|
||||
tx.BTCUSD = y.BTCUSD.(float64)
|
||||
}
|
||||
|
||||
tx.Fee = y.Fee
|
||||
tx.OrderID = y.OrderID
|
||||
transactions = append(transactions, tx)
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetOpenOrders(currency string) ([]BitstampOrder, error) {
|
||||
resp := []BitstampOrder{}
|
||||
path := fmt.Sprintf("%s/%s", BITSTAMP_API_OPEN_ORDERS, common.StringToLower(currency))
|
||||
err := b.SendAuthenticatedHTTPRequest(path, true, nil, &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetOrderStatus(OrderID int64) (BitstampOrderStatus, error) {
|
||||
var req = url.Values{}
|
||||
req.Add("id", strconv.FormatInt(OrderID, 10))
|
||||
resp := BitstampOrderStatus{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ORDER, false, req, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) CancelOrder(OrderID int64) (bool, error) {
|
||||
var req = url.Values{}
|
||||
result := false
|
||||
req.Add("id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ORDER, true, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) CancelAllOrders() (bool, error) {
|
||||
result := false
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ALL_ORDERS, false, nil, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) PlaceOrder(currency string, price float64, amount float64, buy, market bool) (BitstampOrder, error) {
|
||||
var req = url.Values{}
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("price", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
response := BitstampOrder{}
|
||||
orderType := BITSTAMP_API_BUY
|
||||
path := ""
|
||||
|
||||
if !buy {
|
||||
orderType = BITSTAMP_API_SELL
|
||||
}
|
||||
|
||||
path = fmt.Sprintf("%s/%s", orderType, common.StringToLower(currency))
|
||||
|
||||
if market {
|
||||
path = fmt.Sprintf("%s/%s/%s", orderType, BITSTAMP_API_MARKET, common.StringToLower(currency))
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(path, true, req, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetWithdrawalRequests(values url.Values) ([]BitstampWithdrawalRequests, error) {
|
||||
resp := []BitstampWithdrawalRequests{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_WITHDRAWAL_REQUESTS, false, values, &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) BitcoinWithdrawal(amount float64, address string, instant bool) (string, error) {
|
||||
var req = url.Values{}
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("address", address)
|
||||
|
||||
if instant {
|
||||
req.Add("instant", "1")
|
||||
} else {
|
||||
req.Add("instant", "0")
|
||||
}
|
||||
|
||||
type response struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_WITHDRAWAL, false, req, &resp)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetBitcoinDepositAddress() (string, error) {
|
||||
address := ""
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_DEPOSIT, false, url.Values{}, &address)
|
||||
|
||||
if err != nil {
|
||||
return address, err
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetUnconfirmedBitcoinDeposits() ([]BitstampUnconfirmedBTCTransactions, error) {
|
||||
response := []BitstampUnconfirmedBTCTransactions{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_UNCONFIRMED_BITCOIN, false, nil, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) RippleWithdrawal(amount float64, address, currency string) (bool, error) {
|
||||
var req = url.Values{}
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("address", address)
|
||||
req.Add("currency", currency)
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_WITHDRAWAL, false, req, nil)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetRippleDepositAddress() (string, error) {
|
||||
type response struct {
|
||||
Address string
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_DESPOIT, false, nil, &resp)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.Address, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount string, toMain bool) (bool, error) {
|
||||
var req = url.Values{}
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("currency", currency)
|
||||
req.Add("subAccount", subAccount)
|
||||
|
||||
path := BITSTAMP_API_TRANSFER_TO_MAIN
|
||||
if !toMain {
|
||||
path = BITSTAMP_API_TRANSFER_FROM_MAIN
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(path, true, req, nil)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) XRPWithdrawal(amount float64, address, destTag string) (string, error) {
|
||||
var req = url.Values{}
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("address", address)
|
||||
if destTag != "" {
|
||||
req.Add("destination_tag", destTag)
|
||||
}
|
||||
|
||||
type response struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_XRP_WITHDRAWAL, true, req, &resp)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetXRPDepositAddress() (BitstampXRPDepositResponse, error) {
|
||||
resp := BitstampXRPDepositResponse{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_XRP_DESPOIT, true, nil, &resp)
|
||||
|
||||
if err != nil {
|
||||
return BitstampXRPDepositResponse{}, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url.Values, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
|
||||
if values == nil {
|
||||
values = url.Values{}
|
||||
}
|
||||
|
||||
values.Set("key", b.APIKey)
|
||||
values.Set("nonce", nonce)
|
||||
hmac := common.GetHMAC(common.HASH_SHA256, []byte(nonce+b.ClientID+b.APIKey), []byte(b.APISecret))
|
||||
values.Set("signature", common.StringToUpper(common.HexEncodeToString(hmac)))
|
||||
|
||||
if v2 {
|
||||
path = fmt.Sprintf("%s/v%s/%s/", BITSTAMP_API_URL, BITSTAMP_API_VERSION, path)
|
||||
} else {
|
||||
path = fmt.Sprintf("%s/%s/", BITSTAMP_API_URL, path)
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Println("Sending POST request to " + path)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", path, headers, strings.NewReader(values.Encode()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
73
exchanges/bitstamp/bitstampwebsocket.go
Normal file
73
exchanges/bitstamp/bitstampwebsocket.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package bitstamp
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/toorop/go-pusher"
|
||||
)
|
||||
|
||||
type BitstampPusherOrderbook struct {
|
||||
Asks [][]string `json:"asks"`
|
||||
Bids [][]string `json:"bids"`
|
||||
}
|
||||
type BitstampPusherTrade struct {
|
||||
Price float64 `json:"price"`
|
||||
Amount float64 `json:"amount"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
const (
|
||||
BITSTAMP_PUSHER_KEY = "de504dc5763aeef9ff52"
|
||||
)
|
||||
|
||||
func (b *Bitstamp) PusherClient() {
|
||||
for b.Enabled && b.Websocket {
|
||||
pusherClient, err := pusher.NewClient(BITSTAMP_PUSHER_KEY)
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Error: %s\n", b.GetName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = pusherClient.Subscribe("live_trades")
|
||||
if err != nil {
|
||||
log.Printf("%s Websocket Trade subscription error: %s\n", b.GetName(), err)
|
||||
}
|
||||
|
||||
err = pusherClient.Subscribe("order_book")
|
||||
if err != nil {
|
||||
log.Printf("%s Websocket Trade subscription error: %s\n", b.GetName(), err)
|
||||
}
|
||||
|
||||
dataChannelTrade, err := pusherClient.Bind("data")
|
||||
if err != nil {
|
||||
log.Printf("%s Websocket Bind error: %s\n", b.GetName(), err)
|
||||
continue
|
||||
}
|
||||
tradeChannelTrade, err := pusherClient.Bind("trade")
|
||||
if err != nil {
|
||||
log.Printf("%s Websocket Bind error: %s\n", b.GetName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%s Pusher client connected.\n", b.GetName())
|
||||
|
||||
for b.Websocket {
|
||||
select {
|
||||
case data := <-dataChannelTrade:
|
||||
result := BitstampPusherOrderbook{}
|
||||
err := common.JSONDecode([]byte(data.Data), &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
case trade := <-tradeChannelTrade:
|
||||
result := BitstampPusherTrade{}
|
||||
err := common.JSONDecode([]byte(trade.Data), &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
log.Printf("%s Pusher trade: Price: %f Amount: %f\n", b.GetName(), result.Price, result.Amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
156
exchanges/btcc/btcc_types.go
Normal file
156
exchanges/btcc/btcc_types.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package btcc
|
||||
|
||||
type BTCCTicker struct {
|
||||
High float64 `json:",string"`
|
||||
Low float64 `json:",string"`
|
||||
Buy float64 `json:",string"`
|
||||
Sell float64 `json:",string"`
|
||||
Last float64 `json:",string"`
|
||||
Vol float64 `json:",string"`
|
||||
Date int64
|
||||
Vwap float64 `json:",string"`
|
||||
Prev_close float64 `json:",string"`
|
||||
Open float64 `json:",string"`
|
||||
}
|
||||
|
||||
type BTCCProfile struct {
|
||||
Username string
|
||||
TradePasswordEnabled bool `json:"trade_password_enabled,bool"`
|
||||
OTPEnabled bool `json:"otp_enabled,bool"`
|
||||
TradeFee float64 `json:"trade_fee"`
|
||||
TradeFeeCNYLTC float64 `json:"trade_fee_cnyltc"`
|
||||
TradeFeeBTCLTC float64 `json:"trade_fee_btcltc"`
|
||||
DailyBTCLimit float64 `json:"daily_btc_limit"`
|
||||
DailyLTCLimit float64 `json:"daily_ltc_limit"`
|
||||
BTCDespoitAddress string `json:"btc_despoit_address"`
|
||||
BTCWithdrawalAddress string `json:"btc_withdrawal_address"`
|
||||
LTCDepositAddress string `json:"ltc_deposit_address"`
|
||||
LTCWithdrawalAddress string `json:"ltc_withdrawal_request"`
|
||||
APIKeyPermission int64 `json:"api_key_permission"`
|
||||
}
|
||||
|
||||
type BTCCCurrencyGeneric struct {
|
||||
Currency string
|
||||
Symbol string
|
||||
Amount string
|
||||
AmountInt int64 `json:"amount_integer"`
|
||||
AmountDecimal float64 `json:"amount_decimal"`
|
||||
}
|
||||
|
||||
type BTCCOrder struct {
|
||||
ID int64
|
||||
Type string
|
||||
Price float64
|
||||
Currency string
|
||||
Amount float64
|
||||
AmountOrig float64 `json:"amount_original"`
|
||||
Date int64
|
||||
Status string
|
||||
Detail BTCCOrderDetail
|
||||
}
|
||||
|
||||
type BTCCOrderDetail struct {
|
||||
Dateline int64
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BTCCWithdrawal struct {
|
||||
ID int64
|
||||
Address string
|
||||
Currency string
|
||||
Amount float64
|
||||
Date int64
|
||||
Transaction string
|
||||
Status string
|
||||
}
|
||||
|
||||
type BTCCDeposit struct {
|
||||
ID int64
|
||||
Address string
|
||||
Currency string
|
||||
Amount float64
|
||||
Date int64
|
||||
Status string
|
||||
}
|
||||
|
||||
type BTCCBidAsk struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BTCCDepth struct {
|
||||
Bid []BTCCBidAsk
|
||||
Ask []BTCCBidAsk
|
||||
}
|
||||
|
||||
type BTCCTransaction struct {
|
||||
ID int64
|
||||
Type string
|
||||
BTCAmount float64 `json:"btc_amount"`
|
||||
LTCAmount float64 `json:"ltc_amount"`
|
||||
CNYAmount float64 `json:"cny_amount"`
|
||||
Date int64
|
||||
}
|
||||
|
||||
type BTCCIcebergOrder struct {
|
||||
ID int64
|
||||
Type string
|
||||
Price float64
|
||||
Market string
|
||||
Amount float64
|
||||
AmountOrig float64 `json:"amount_original"`
|
||||
DisclosedAmount float64 `json:"disclosed_amount"`
|
||||
Variance float64
|
||||
Date int64
|
||||
Status string
|
||||
}
|
||||
|
||||
type BTCCStopOrder struct {
|
||||
ID int64
|
||||
Type string
|
||||
StopPrice float64 `json:"stop_price"`
|
||||
TrailingAmt float64 `json:"trailing_amount"`
|
||||
TrailingPct float64 `json:"trailing_percentage"`
|
||||
Price float64
|
||||
Market string
|
||||
Amount float64
|
||||
Date int64
|
||||
Status string
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BTCCWebsocketOrder struct {
|
||||
Price float64 `json:"price"`
|
||||
TotalAmount float64 `json:"totalamount"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type BTCCWebsocketGroupOrder struct {
|
||||
Asks []BTCCWebsocketOrder `json:"ask"`
|
||||
Bids []BTCCWebsocketOrder `json:"bid"`
|
||||
Market string `json:"market"`
|
||||
}
|
||||
|
||||
type BTCCWebsocketTrade struct {
|
||||
Amount float64 `json:"amount"`
|
||||
Date float64 `json:"date"`
|
||||
Market string `json:"market"`
|
||||
Price float64 `json:"price"`
|
||||
TradeID float64 `json:"trade_id"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type BTCCWebsocketTicker struct {
|
||||
Buy float64 `json:"buy"`
|
||||
Date float64 `json:"date"`
|
||||
High float64 `json:"high"`
|
||||
Last float64 `json:"last"`
|
||||
Low float64 `json:"low"`
|
||||
Market string `json:"market"`
|
||||
Open float64 `json:"open"`
|
||||
PrevClose float64 `json:"prev_close"`
|
||||
Sell float64 `json:"sell"`
|
||||
Volume float64 `json:"vol"`
|
||||
Vwap float64 `json:"vwap"`
|
||||
}
|
||||
70
exchanges/btcc/btcc_wrapper.go
Normal file
70
exchanges/btcc/btcc_wrapper.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package btcc
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (b *BTCC) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
func (b *BTCC) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||
}
|
||||
|
||||
if b.Websocket {
|
||||
go b.WebsocketClient()
|
||||
}
|
||||
|
||||
for b.Enabled {
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := b.GetTickerPrice(common.StringToLower(currency))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("BTCC %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(b.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := b.GetTicker(currency)
|
||||
tickerPrice.Ask = tick.Sell
|
||||
tickerPrice.Bid = tick.Buy
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(b.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//TODO: Retrieve BTCC info
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Kraken exchange
|
||||
func (e *BTCC) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
return response, nil
|
||||
}
|
||||
592
exchanges/btcc/btcchttp.go
Normal file
592
exchanges/btcc/btcchttp.go
Normal file
@@ -0,0 +1,592 @@
|
||||
package btcc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
BTCC_API_URL = "https://api.btcc.com/"
|
||||
BTCC_API_AUTHENTICATED_METHOD = "api_trade_v1.php"
|
||||
BTCC_API_VER = "2.0.1.3"
|
||||
BTCC_ORDER_BUY = "buyOrder2"
|
||||
BTCC_ORDER_SELL = "sellOrder2"
|
||||
BTCC_ORDER_CANCEL = "cancelOrder"
|
||||
BTCC_ICEBERG_BUY = "buyIcebergOrder"
|
||||
BTCC_ICEBERG_SELL = "sellIcebergOrder"
|
||||
BTCC_ICEBERG_ORDER = "getIcebergOrder"
|
||||
BTCC_ICEBERG_ORDERS = "getIcebergOrders"
|
||||
BTCC_ICEBERG_CANCEL = "cancelIcebergOrder"
|
||||
BTCC_ACCOUNT_INFO = "getAccountInfo"
|
||||
BTCC_DEPOSITS = "getDeposits"
|
||||
BTCC_MARKETDEPTH = "getMarketDepth2"
|
||||
BTCC_ORDER = "getOrder"
|
||||
BTCC_ORDERS = "getOrders"
|
||||
BTCC_TRANSACTIONS = "getTransactions"
|
||||
BTCC_WITHDRAWAL = "getWithdrawal"
|
||||
BTCC_WITHDRAWALS = "getWithdrawals"
|
||||
BTCC_WITHDRAWAL_REQUEST = "requestWithdrawal"
|
||||
BTCC_STOPORDER_BUY = "buyStopOrder"
|
||||
BTCC_STOPORDER_SELL = "sellStopOrder"
|
||||
BTCC_STOPORDER_CANCEL = "cancelStopOrder"
|
||||
BTCC_STOPORDER = "getStopOrder"
|
||||
BTCC_STOPORDERS = "getStopOrders"
|
||||
)
|
||||
|
||||
type BTCC struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (b *BTCC) SetDefaults() {
|
||||
b.Name = "BTCC"
|
||||
b.Enabled = false
|
||||
b.Fee = 0
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
//Setup is run on startup to setup exchange with config values
|
||||
func (b *BTCC) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
} else {
|
||||
b.Enabled = true
|
||||
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
b.RESTPollingDelay = exch.RESTPollingDelay
|
||||
b.Verbose = exch.Verbose
|
||||
b.Websocket = exch.Websocket
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetFee() float64 {
|
||||
return b.Fee
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTicker(symbol string) (BTCCTicker, error) {
|
||||
type Response struct {
|
||||
Ticker BTCCTicker
|
||||
}
|
||||
|
||||
resp := Response{}
|
||||
req := fmt.Sprintf("%sdata/ticker?market=%s", BTCC_API_URL, symbol)
|
||||
err := common.SendHTTPGetRequest(req, true, &resp)
|
||||
if err != nil {
|
||||
return BTCCTicker{}, err
|
||||
}
|
||||
return resp.Ticker, nil
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTradesLast24h(symbol string) bool {
|
||||
req := fmt.Sprintf("%sdata/trades?market=%s", BTCC_API_URL, symbol)
|
||||
err := common.SendHTTPGetRequest(req, true, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTradeHistory(symbol string, limit, sinceTid int64, time time.Time) bool {
|
||||
req := fmt.Sprintf("%sdata/historydata?market=%s", BTCC_API_URL, symbol)
|
||||
v := url.Values{}
|
||||
|
||||
if limit > 0 {
|
||||
v.Set("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
if sinceTid > 0 {
|
||||
v.Set("since", strconv.FormatInt(sinceTid, 10))
|
||||
}
|
||||
if !time.IsZero() {
|
||||
v.Set("sincetype", strconv.FormatInt(time.Unix(), 10))
|
||||
}
|
||||
|
||||
req = common.EncodeURLValues(req, v)
|
||||
err := common.SendHTTPGetRequest(req, true, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *BTCC) GetOrderBook(symbol string, limit int) bool {
|
||||
req := fmt.Sprintf("%sdata/orderbook?market=%s&limit=%d", BTCC_API_URL, symbol, limit)
|
||||
err := common.SendHTTPGetRequest(req, true, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *BTCC) GetAccountInfo(infoType string) {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if len(infoType) > 0 {
|
||||
params = append(params, infoType)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ACCOUNT_INFO, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) PlaceOrder(buyOrder bool, price, amount float64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, strconv.FormatFloat(price, 'f', -1, 64))
|
||||
params = append(params, strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
req := BTCC_ORDER_BUY
|
||||
if !buyOrder {
|
||||
req = BTCC_ORDER_SELL
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(req, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) CancelOrder(orderID int64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, orderID)
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ORDER_CANCEL, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetDeposits(currency string, pending bool) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, currency)
|
||||
|
||||
if pending {
|
||||
params = append(params, pending)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_DEPOSITS, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetMarketDepth(market string, limit int64) {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if limit > 0 {
|
||||
params = append(params, limit)
|
||||
}
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_MARKETDEPTH, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetOrder(orderID int64, market string, detailed bool) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, orderID)
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
if detailed {
|
||||
params = append(params, detailed)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ORDER, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetOrders(openonly bool, market string, limit, offset, since int64, detailed bool) {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if openonly {
|
||||
params = append(params, openonly)
|
||||
}
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
params = append(params, limit)
|
||||
}
|
||||
|
||||
if offset > 0 {
|
||||
params = append(params, offset)
|
||||
}
|
||||
|
||||
if since > 0 {
|
||||
params = append(params, since)
|
||||
}
|
||||
|
||||
if detailed {
|
||||
params = append(params, detailed)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ORDERS, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTransactions(transType string, limit, offset, since int64, sinceType string) {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if len(transType) > 0 {
|
||||
params = append(params, transType)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
params = append(params, limit)
|
||||
}
|
||||
|
||||
if offset > 0 {
|
||||
params = append(params, offset)
|
||||
}
|
||||
|
||||
if since > 0 {
|
||||
params = append(params, since)
|
||||
}
|
||||
|
||||
if len(sinceType) > 0 {
|
||||
params = append(params, sinceType)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_TRANSACTIONS, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetWithdrawal(withdrawalID int64, currency string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, withdrawalID)
|
||||
|
||||
if len(currency) > 0 {
|
||||
params = append(params, currency)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_WITHDRAWAL, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetWithdrawals(currency string, pending bool) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, currency)
|
||||
|
||||
if pending {
|
||||
params = append(params, pending)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_WITHDRAWALS, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) RequestWithdrawal(currency string, amount float64) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, currency)
|
||||
params = append(params, amount)
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_WITHDRAWAL_REQUEST, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) IcebergOrder(buyOrder bool, price, amount, discAmount, variance float64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, strconv.FormatFloat(price, 'f', -1, 64))
|
||||
params = append(params, strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
params = append(params, strconv.FormatFloat(discAmount, 'f', -1, 64))
|
||||
params = append(params, strconv.FormatFloat(variance, 'f', -1, 64))
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
req := BTCC_ICEBERG_BUY
|
||||
if !buyOrder {
|
||||
req = BTCC_ICEBERG_SELL
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(req, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetIcebergOrder(orderID int64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, orderID)
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ICEBERG_ORDER, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetIcebergOrders(limit, offset int64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if limit > 0 {
|
||||
params = append(params, limit)
|
||||
}
|
||||
|
||||
if offset > 0 {
|
||||
params = append(params, offset)
|
||||
}
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ICEBERG_ORDERS, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) CancelIcebergOrder(orderID int64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, orderID)
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ICEBERG_CANCEL, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) PlaceStopOrder(buyOder bool, stopPrice, price, amount, trailingAmt, trailingPct float64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if stopPrice > 0 {
|
||||
params = append(params, stopPrice)
|
||||
}
|
||||
|
||||
params = append(params, strconv.FormatFloat(price, 'f', -1, 64))
|
||||
params = append(params, strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
if trailingAmt > 0 {
|
||||
params = append(params, strconv.FormatFloat(trailingAmt, 'f', -1, 64))
|
||||
}
|
||||
|
||||
if trailingPct > 0 {
|
||||
params = append(params, strconv.FormatFloat(trailingPct, 'f', -1, 64))
|
||||
}
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
req := BTCC_STOPORDER_BUY
|
||||
if !buyOder {
|
||||
req = BTCC_STOPORDER_SELL
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(req, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetStopOrder(orderID int64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, orderID)
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_STOPORDER, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetStopOrders(status, orderType string, stopPrice float64, limit, offset int64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if len(status) > 0 {
|
||||
params = append(params, status)
|
||||
}
|
||||
|
||||
if len(orderType) > 0 {
|
||||
params = append(params, orderType)
|
||||
}
|
||||
|
||||
if stopPrice > 0 {
|
||||
params = append(params, stopPrice)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
params = append(params, limit)
|
||||
}
|
||||
|
||||
if offset > 0 {
|
||||
params = append(params, limit)
|
||||
}
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_STOPORDERS, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) CancelStopOrder(orderID int64, market string) {
|
||||
params := make([]interface{}, 0)
|
||||
params = append(params, orderID)
|
||||
|
||||
if len(market) > 0 {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_STOPORDER_CANCEL, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) SendAuthenticatedHTTPRequest(method string, params []interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)[0:16]
|
||||
encoded := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=%d&method=%s¶ms=", nonce, b.APIKey, 1, method)
|
||||
|
||||
if len(params) == 0 {
|
||||
params = make([]interface{}, 0)
|
||||
} else {
|
||||
items := make([]string, 0)
|
||||
for _, x := range params {
|
||||
xType := fmt.Sprintf("%T", x)
|
||||
switch xType {
|
||||
case "int64", "int":
|
||||
{
|
||||
items = append(items, fmt.Sprintf("%d", x))
|
||||
}
|
||||
case "string":
|
||||
{
|
||||
items = append(items, fmt.Sprintf("%s", x))
|
||||
}
|
||||
case "float64":
|
||||
{
|
||||
items = append(items, fmt.Sprintf("%f", x))
|
||||
}
|
||||
case "bool":
|
||||
{
|
||||
if x == true {
|
||||
items = append(items, "1")
|
||||
} else {
|
||||
items = append(items, "")
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
items = append(items, fmt.Sprintf("%v", x))
|
||||
}
|
||||
}
|
||||
}
|
||||
encoded += common.JoinStrings(items, ",")
|
||||
}
|
||||
if b.Verbose {
|
||||
log.Println(encoded)
|
||||
}
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA1, []byte(encoded), []byte(b.APISecret))
|
||||
postData := make(map[string]interface{})
|
||||
postData["method"] = method
|
||||
postData["params"] = params
|
||||
postData["id"] = 1
|
||||
apiURL := BTCC_API_URL + BTCC_API_AUTHENTICATED_METHOD
|
||||
data, err := common.JSONEncode(postData)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Marshal POST data")
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Sending POST request to %s calling method %s with params %s\n", apiURL, method, data)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-type"] = "application/json-rpc"
|
||||
headers["Authorization"] = "Basic " + common.Base64Encode([]byte(b.APIKey+":"+common.HexEncodeToString(hmac)))
|
||||
headers["Json-Rpc-Tonce"] = nonce
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", apiURL, headers, strings.NewReader(string(data)))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Recv'd :%s\n", resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
116
exchanges/btcc/btccwebsocket.go
Normal file
116
exchanges/btcc/btccwebsocket.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package btcc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/socketio"
|
||||
)
|
||||
|
||||
const (
|
||||
BTCC_SOCKETIO_ADDRESS = "https://websocket.btcc.com"
|
||||
)
|
||||
|
||||
var BTCCSocket *socketio.SocketIO
|
||||
|
||||
func (b *BTCC) OnConnect(output chan socketio.Message) {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Connected to Websocket.", b.GetName())
|
||||
}
|
||||
|
||||
currencies := []string{}
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := common.StringToLower(x[3:] + x[0:3])
|
||||
currencies = append(currencies, currency)
|
||||
}
|
||||
endpoints := []string{"marketdata", "grouporder"}
|
||||
|
||||
for _, x := range endpoints {
|
||||
for _, y := range currencies {
|
||||
channel := fmt.Sprintf(`"%s_%s"`, x, y)
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket subscribing to channel: %s.", b.GetName(), channel)
|
||||
}
|
||||
output <- socketio.CreateMessageEvent("subscribe", channel, b.OnMessage, BTCCSocket.Version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) OnDisconnect(output chan socketio.Message) {
|
||||
log.Printf("%s Disconnected from websocket server.. Reconnecting.\n", b.GetName())
|
||||
b.WebsocketClient()
|
||||
}
|
||||
|
||||
func (b *BTCC) OnError() {
|
||||
log.Printf("%s Error with Websocket connection.. Reconnecting.\n", b.GetName())
|
||||
b.WebsocketClient()
|
||||
}
|
||||
|
||||
func (b *BTCC) OnMessage(message []byte, output chan socketio.Message) {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket message received which isn't handled by default.\n", b.GetName())
|
||||
log.Println(string(message))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) OnTicker(message []byte, output chan socketio.Message) {
|
||||
type Response struct {
|
||||
Ticker BTCCWebsocketTicker `json:"ticker"`
|
||||
}
|
||||
var resp Response
|
||||
err := common.JSONDecode(message, &resp)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) OnGroupOrder(message []byte, output chan socketio.Message) {
|
||||
type Response struct {
|
||||
GroupOrder BTCCWebsocketGroupOrder `json:"grouporder"`
|
||||
}
|
||||
var resp Response
|
||||
err := common.JSONDecode(message, &resp)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) OnTrade(message []byte, output chan socketio.Message) {
|
||||
trade := BTCCWebsocketTrade{}
|
||||
err := common.JSONDecode(message, &trade)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) WebsocketClient() {
|
||||
events := make(map[string]func(message []byte, output chan socketio.Message))
|
||||
events["grouporder"] = b.OnGroupOrder
|
||||
events["ticker"] = b.OnTicker
|
||||
events["trade"] = b.OnTrade
|
||||
|
||||
BTCCSocket = &socketio.SocketIO{
|
||||
Version: 1,
|
||||
OnConnect: b.OnConnect,
|
||||
OnEvent: events,
|
||||
OnError: b.OnError,
|
||||
OnMessage: b.OnMessage,
|
||||
OnDisconnect: b.OnDisconnect,
|
||||
}
|
||||
|
||||
for b.Enabled && b.Websocket {
|
||||
err := socketio.ConnectToSocket(BTCC_SOCKETIO_ADDRESS, BTCCSocket)
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Err: %s\n", b.GetName(), err)
|
||||
continue
|
||||
}
|
||||
log.Printf("%s Disconnected from Websocket.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
126
exchanges/btce/btce_types.go
Normal file
126
exchanges/btce/btce_types.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package btce
|
||||
|
||||
type BTCeTicker struct {
|
||||
High float64
|
||||
Low float64
|
||||
Avg float64
|
||||
Vol float64
|
||||
Vol_cur float64
|
||||
Last float64
|
||||
Buy float64
|
||||
Sell float64
|
||||
Updated int64
|
||||
}
|
||||
|
||||
type BTCEOrderbook struct {
|
||||
Asks [][]float64 `json:"asks"`
|
||||
Bids [][]float64 `json:"bids"`
|
||||
}
|
||||
|
||||
type BTCETrades struct {
|
||||
Type string `json:"type"`
|
||||
Price float64 `json:"bid"`
|
||||
Amount float64 `json:"amount"`
|
||||
TID int64 `json:"tid"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BTCEResponse struct {
|
||||
Return interface{} `json:"return"`
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type BTCEPair struct {
|
||||
DecimalPlaces int `json:"decimal_places"`
|
||||
MinPrice float64 `json:"min_price"`
|
||||
MaxPrice float64 `json:"max_price"`
|
||||
MinAmount float64 `json:"min_amount"`
|
||||
Hidden int `json:"hidden"`
|
||||
Fee float64 `json:"fee"`
|
||||
}
|
||||
|
||||
type BTCEInfo struct {
|
||||
ServerTime int64 `json:"server_time"`
|
||||
Pairs map[string]BTCEPair `json:"pairs"`
|
||||
}
|
||||
|
||||
type BTCEAccountInfo struct {
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
OpenOrders int `json:"open_orders"`
|
||||
Rights struct {
|
||||
Info int `json:"info"`
|
||||
Trade int `json:"trade"`
|
||||
Withdraw int `json:"withdraw"`
|
||||
} `json:"rights"`
|
||||
ServerTime float64 `json:"server_time"`
|
||||
TransactionCount int `json:"transaction_count"`
|
||||
}
|
||||
|
||||
type BTCEActiveOrders struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
TimestampCreated float64 `json:"time_created"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type BTCEOrderInfo struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
StartAmount float64 `json:"start_amount"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
TimestampCreated float64 `json:"time_created"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type BTCECancelOrder struct {
|
||||
OrderID float64 `json:"order_id"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCETrade struct {
|
||||
Received float64 `json:"received"`
|
||||
Remains float64 `json:"remains"`
|
||||
OrderID float64 `json:"order_id"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCETransHistory struct {
|
||||
Type int `json:"type"`
|
||||
Amount float64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
Description string `json:"desc"`
|
||||
Status int `json:"status"`
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BTCETradeHistory struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"type"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
OrderID float64 `json:"order_id"`
|
||||
MyOrder int `json:"is_your_order"`
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BTCEWithdrawCoins struct {
|
||||
TID int64 `json:"tId"`
|
||||
AmountSent float64 `json:"amountSent"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCECreateCoupon struct {
|
||||
Coupon string `json:"coupon"`
|
||||
TransID int64 `json:"transID"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCERedeemCoupon struct {
|
||||
CouponAmount float64 `json:"couponAmount,string"`
|
||||
CouponCurrency string `json:"couponCurrency"`
|
||||
TransID int64 `json:"transID"`
|
||||
}
|
||||
85
exchanges/btce/btce_wrapper.go
Normal file
85
exchanges/btce/btce_wrapper.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package btce
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (b *BTCE) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
func (b *BTCE) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||
}
|
||||
|
||||
pairs := []string{}
|
||||
for _, x := range b.EnabledPairs {
|
||||
x = common.StringToLower(x[0:3] + "_" + x[3:6])
|
||||
pairs = append(pairs, x)
|
||||
}
|
||||
pairsString := common.JoinStrings(pairs, "-")
|
||||
|
||||
for b.Enabled {
|
||||
go func() {
|
||||
ticker, err := b.GetTicker(pairsString)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
for x, y := range ticker {
|
||||
x = common.StringToUpper(x[0:3] + x[4:])
|
||||
log.Printf("BTC-e %s: Last %f High %f Low %f Volume %f\n", x, y.Last, y.High, y.Low, y.Vol_cur)
|
||||
b.Ticker[x] = y
|
||||
//AddExchangeInfo(b.GetName(), common.StringToUpper(x[0:3]), common.StringToUpper(x[4:]), y.Last, y.Vol_cur)
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCE) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, ok := b.Ticker[currency]
|
||||
if !ok {
|
||||
return tickerPrice, errors.New("Unable to get currency.")
|
||||
}
|
||||
tickerPrice.Ask = tick.Buy
|
||||
tickerPrice.Bid = tick.Sell
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol_cur
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(b.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the BTCE exchange
|
||||
func (e *BTCE) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetAccountInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
for x, y := range accountBalance.Funds {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||
exchangeCurrency.TotalValue = y
|
||||
exchangeCurrency.Hold = 0
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
341
exchanges/btce/btcehttp.go
Normal file
341
exchanges/btce/btcehttp.go
Normal file
@@ -0,0 +1,341 @@
|
||||
package btce
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
BTCE_API_PUBLIC_URL = "https://btc-e.com/api"
|
||||
BTCE_API_PRIVATE_URL = "https://btc-e.com/tapi"
|
||||
BTCE_API_PUBLIC_VERSION = "3"
|
||||
BTCE_API_PRIVATE_VERSION = "1"
|
||||
BTCE_INFO = "info"
|
||||
BTCE_TICKER = "ticker"
|
||||
BTCE_DEPTH = "depth"
|
||||
BTCE_TRADES = "trades"
|
||||
BTCE_ACCOUNT_INFO = "getInfo"
|
||||
BTCE_TRADE = "Trade"
|
||||
BTCE_ACTIVE_ORDERS = "ActiveOrders"
|
||||
BTCE_ORDER_INFO = "OrderInfo"
|
||||
BTCE_CANCEL_ORDER = "CancelOrder"
|
||||
BTCE_TRADE_HISTORY = "TradeHistory"
|
||||
BTCE_TRANSACTION_HISTORY = "TransHistory"
|
||||
BTCE_WITHDRAW_COIN = "WithdrawCoin"
|
||||
BTCE_CREATE_COUPON = "CreateCoupon"
|
||||
BTCE_REDEEM_COUPON = "RedeemCoupon"
|
||||
)
|
||||
|
||||
type BTCE struct {
|
||||
exchange.ExchangeBase
|
||||
Ticker map[string]BTCeTicker
|
||||
}
|
||||
|
||||
func (b *BTCE) SetDefaults() {
|
||||
b.Name = "BTCE"
|
||||
b.Enabled = false
|
||||
b.Fee = 0.2
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.Ticker = make(map[string]BTCeTicker)
|
||||
}
|
||||
|
||||
func (b *BTCE) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
} else {
|
||||
b.Enabled = true
|
||||
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
b.RESTPollingDelay = exch.RESTPollingDelay
|
||||
b.Verbose = exch.Verbose
|
||||
b.Websocket = exch.Websocket
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCE) GetFee() float64 {
|
||||
return b.Fee
|
||||
}
|
||||
|
||||
func (b *BTCE) GetInfo() (BTCEInfo, error) {
|
||||
req := fmt.Sprintf("%s/%s/%s/", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_INFO)
|
||||
resp := BTCEInfo{}
|
||||
err := common.SendHTTPGetRequest(req, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetTicker(symbol string) (map[string]BTCeTicker, error) {
|
||||
type Response struct {
|
||||
Data map[string]BTCeTicker
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_TICKER, symbol)
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Data, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetDepth(symbol string) (BTCEOrderbook, error) {
|
||||
type Response struct {
|
||||
Data map[string]BTCEOrderbook
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_DEPTH, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
if err != nil {
|
||||
return BTCEOrderbook{}, err
|
||||
}
|
||||
|
||||
depth := response.Data[symbol]
|
||||
return depth, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetTrades(symbol string) ([]BTCETrades, error) {
|
||||
type Response struct {
|
||||
Data map[string][]BTCETrades
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_TRADES, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
if err != nil {
|
||||
return []BTCETrades{}, err
|
||||
}
|
||||
|
||||
trades := response.Data[symbol]
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetAccountInfo() (BTCEAccountInfo, error) {
|
||||
var result BTCEAccountInfo
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_ACCOUNT_INFO, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetActiveOrders(pair string) (map[string]BTCEActiveOrders, error) {
|
||||
req := url.Values{}
|
||||
req.Add("pair", pair)
|
||||
|
||||
var result map[string]BTCEActiveOrders
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_ACTIVE_ORDERS, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetOrderInfo(OrderID int64) (map[string]BTCEOrderInfo, error) {
|
||||
req := url.Values{}
|
||||
req.Add("order_id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
var result map[string]BTCEOrderInfo
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_ORDER_INFO, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) CancelOrder(OrderID int64) (bool, error) {
|
||||
req := url.Values{}
|
||||
req.Add("order_id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
var result BTCECancelOrder
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_CANCEL_ORDER, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
//to-do: convert orderid to int64
|
||||
func (b *BTCE) Trade(pair, orderType string, amount, price float64) (float64, error) {
|
||||
req := url.Values{}
|
||||
req.Add("pair", pair)
|
||||
req.Add("type", orderType)
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("rate", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
|
||||
var result BTCETrade
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_TRADE, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result.OrderID, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetTransactionHistory(TIDFrom, Count, TIDEnd int64, order, since, end string) (map[string]BTCETransHistory, error) {
|
||||
req := url.Values{}
|
||||
req.Add("from", strconv.FormatInt(TIDFrom, 10))
|
||||
req.Add("count", strconv.FormatInt(Count, 10))
|
||||
req.Add("from_id", strconv.FormatInt(TIDFrom, 10))
|
||||
req.Add("end_id", strconv.FormatInt(TIDEnd, 10))
|
||||
req.Add("order", order)
|
||||
req.Add("since", since)
|
||||
req.Add("end", end)
|
||||
|
||||
var result map[string]BTCETransHistory
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_TRANSACTION_HISTORY, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetTradeHistory(TIDFrom, Count, TIDEnd int64, order, since, end, pair string) (map[string]BTCETradeHistory, error) {
|
||||
req := url.Values{}
|
||||
|
||||
req.Add("from", strconv.FormatInt(TIDFrom, 10))
|
||||
req.Add("count", strconv.FormatInt(Count, 10))
|
||||
req.Add("from_id", strconv.FormatInt(TIDFrom, 10))
|
||||
req.Add("end_id", strconv.FormatInt(TIDEnd, 10))
|
||||
req.Add("order", order)
|
||||
req.Add("since", since)
|
||||
req.Add("end", end)
|
||||
req.Add("pair", pair)
|
||||
|
||||
var result map[string]BTCETradeHistory
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_TRADE_HISTORY, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) WithdrawCoins(coin string, amount float64, address string) (BTCEWithdrawCoins, error) {
|
||||
req := url.Values{}
|
||||
|
||||
req.Add("coinName", coin)
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("address", address)
|
||||
|
||||
var result BTCEWithdrawCoins
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_WITHDRAW_COIN, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) CreateCoupon(currency string, amount float64) (BTCECreateCoupon, error) {
|
||||
req := url.Values{}
|
||||
|
||||
req.Add("currency", currency)
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
var result BTCECreateCoupon
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_CREATE_COUPON, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) RedeemCoupon(coupon string) (BTCERedeemCoupon, error) {
|
||||
req := url.Values{}
|
||||
|
||||
req.Add("coupon", coupon)
|
||||
|
||||
var result BTCERedeemCoupon
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCE_REDEEM_COUPON, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) SendAuthenticatedHTTPRequest(method string, values url.Values, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
values.Set("nonce", nonce)
|
||||
values.Set("method", method)
|
||||
|
||||
encoded := values.Encode()
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(encoded), []byte(b.APISecret))
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Sending POST request to %s calling method %s with params %s\n", BTCE_API_PRIVATE_URL, method, encoded)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Key"] = b.APIKey
|
||||
headers["Sign"] = common.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", BTCE_API_PRIVATE_URL, headers, strings.NewReader(encoded))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response := BTCEResponse{}
|
||||
err = common.JSONDecode([]byte(resp), &response)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Success != 1 {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
|
||||
JSONEncoded, err := common.JSONEncode(response.Return)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = common.JSONDecode(JSONEncoded, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
330
exchanges/btcmarkets/btcmarkets.go
Normal file
330
exchanges/btcmarkets/btcmarkets.go
Normal file
@@ -0,0 +1,330 @@
|
||||
package btcmarkets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
BTCMARKETS_API_URL = "https://api.btcmarkets.net"
|
||||
BTCMARKETS_API_VERSION = "0"
|
||||
BTCMARKETS_ACCOUNT_BALANCE = "/account/balance"
|
||||
BTCMARKETS_ORDER_CREATE = "/order/create"
|
||||
BTCMARKETS_ORDER_CANCEL = "/order/cancel"
|
||||
BTCMARKETS_ORDER_HISTORY = "/order/history"
|
||||
BTCMARKETS_ORDER_OPEN = "/order/open"
|
||||
BTCMARKETS_ORDER_TRADE_HISTORY = "/order/trade/history"
|
||||
BTCMARKETS_ORDER_DETAIL = "/order/detail"
|
||||
)
|
||||
|
||||
type BTCMarkets struct {
|
||||
exchange.ExchangeBase
|
||||
Ticker map[string]BTCMarketsTicker
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) SetDefaults() {
|
||||
b.Name = "BTC Markets"
|
||||
b.Enabled = false
|
||||
b.Fee = 0.85
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.Ticker = make(map[string]BTCMarketsTicker)
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
} else {
|
||||
b.Enabled = false
|
||||
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", true)
|
||||
b.RESTPollingDelay = exch.RESTPollingDelay
|
||||
b.Verbose = exch.Verbose
|
||||
b.Websocket = exch.Websocket
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetFee() float64 {
|
||||
return b.Fee
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetTicker(symbol string) (BTCMarketsTicker, error) {
|
||||
ticker := BTCMarketsTicker{}
|
||||
path := fmt.Sprintf("/market/%s/AUD/tick", symbol)
|
||||
err := common.SendHTTPGetRequest(BTCMARKETS_API_URL+path, true, &ticker)
|
||||
if err != nil {
|
||||
return BTCMarketsTicker{}, err
|
||||
}
|
||||
return ticker, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetOrderbook(symbol string) (BTCMarketsOrderbook, error) {
|
||||
orderbook := BTCMarketsOrderbook{}
|
||||
path := fmt.Sprintf("/market/%s/AUD/orderbook", symbol)
|
||||
err := common.SendHTTPGetRequest(BTCMARKETS_API_URL+path, true, &orderbook)
|
||||
if err != nil {
|
||||
return BTCMarketsOrderbook{}, err
|
||||
}
|
||||
return orderbook, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetTrades(symbol string, values url.Values) ([]BTCMarketsTrade, error) {
|
||||
trades := []BTCMarketsTrade{}
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/market/%s/AUD/trades", BTCMARKETS_API_URL, symbol), values)
|
||||
err := common.SendHTTPGetRequest(path, true, &trades)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) Order(currency, instrument string, price, amount int64, orderSide, orderType, clientReq string) (int, error) {
|
||||
type Order struct {
|
||||
Currency string `json:"currency"`
|
||||
Instrument string `json:"instrument"`
|
||||
Price int64 `json:"price"`
|
||||
Volume int64 `json:"volume"`
|
||||
OrderSide string `json:"orderSide"`
|
||||
OrderType string `json:"ordertype"`
|
||||
ClientRequestId string `json:"clientRequestId"`
|
||||
}
|
||||
order := Order{}
|
||||
order.Currency = currency
|
||||
order.Instrument = instrument
|
||||
order.Price = price * common.SATOSHIS_PER_BTC
|
||||
order.Volume = amount * common.SATOSHIS_PER_BTC
|
||||
order.OrderSide = orderSide
|
||||
order.OrderType = orderType
|
||||
order.ClientRequestId = clientReq
|
||||
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ID int `json:"id"`
|
||||
ClientRequestID string `json:"clientRequestId"`
|
||||
}
|
||||
var resp Response
|
||||
|
||||
err := b.SendAuthenticatedRequest("POST", BTCMARKETS_ORDER_CREATE, order, &resp)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return 0, fmt.Errorf("%s Unable to place order. Error message: %s\n", b.GetName(), resp.ErrorMessage)
|
||||
}
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) CancelOrder(orderID []int64) (bool, error) {
|
||||
type CancelOrder struct {
|
||||
OrderIDs []int64 `json:"orderIds"`
|
||||
}
|
||||
orders := CancelOrder{}
|
||||
orders.OrderIDs = append(orders.OrderIDs, orderID...)
|
||||
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
Responses []struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
ClientRequestID string `json:"clientRequestId"`
|
||||
}
|
||||
var resp Response
|
||||
|
||||
err := b.SendAuthenticatedRequest("POST", BTCMARKETS_ORDER_CANCEL, orders, &resp)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return false, fmt.Errorf("%s Unable to cancel order. Error message: %s\n", b.GetName(), resp.ErrorMessage)
|
||||
}
|
||||
|
||||
ordersToBeCancelled := len(orderID)
|
||||
ordersCancelled := 0
|
||||
for _, y := range resp.Responses {
|
||||
if y.Success {
|
||||
ordersCancelled++
|
||||
log.Printf("%s Cancelled order %d.\n", b.GetName(), y.ID)
|
||||
} else {
|
||||
log.Printf("%s Unable to cancel order %d. Error message: %s\n", b.GetName(), y.ID, y.ErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if ordersCancelled == ordersToBeCancelled {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, fmt.Errorf("%s Unable to cancel order(s).", b.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetOrders(currency, instrument string, limit, since int64, historic bool) ([]BTCMarketsOrder, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["currency"] = currency
|
||||
request["instrument"] = instrument
|
||||
request["limit"] = limit
|
||||
request["since"] = since
|
||||
|
||||
path := BTCMARKETS_ORDER_OPEN
|
||||
if historic {
|
||||
path = BTCMARKETS_ORDER_HISTORY
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
Orders []BTCMarketsOrder `json:"orders"`
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
err := b.SendAuthenticatedRequest("POST", path, request, &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, errors.New(resp.ErrorMessage)
|
||||
}
|
||||
|
||||
for i := range resp.Orders {
|
||||
resp.Orders[i].Price = resp.Orders[i].Price / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].OpenVolume = resp.Orders[i].OpenVolume / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].Volume = resp.Orders[i].Volume / common.SATOSHIS_PER_BTC
|
||||
|
||||
for x := range resp.Orders[i].Trades {
|
||||
resp.Orders[i].Trades[x].Fee = resp.Orders[i].Trades[x].Fee / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].Trades[x].Price = resp.Orders[i].Trades[x].Price / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].Trades[x].Volume = resp.Orders[i].Trades[x].Volume / common.SATOSHIS_PER_BTC
|
||||
}
|
||||
}
|
||||
return resp.Orders, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetOrderDetail(orderID []int64) ([]BTCMarketsOrder, error) {
|
||||
type OrderDetail struct {
|
||||
OrderIDs []int64 `json:"orderIds"`
|
||||
}
|
||||
orders := OrderDetail{}
|
||||
orders.OrderIDs = append(orders.OrderIDs, orderID...)
|
||||
|
||||
type response struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
Orders []BTCMarketsOrder `json:"orders"`
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
err := b.SendAuthenticatedRequest("POST", BTCMARKETS_ORDER_DETAIL, orders, &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, errors.New(resp.ErrorMessage)
|
||||
}
|
||||
|
||||
for i := range resp.Orders {
|
||||
resp.Orders[i].Price = resp.Orders[i].Price / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].OpenVolume = resp.Orders[i].OpenVolume / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].Volume = resp.Orders[i].Volume / common.SATOSHIS_PER_BTC
|
||||
|
||||
for x := range resp.Orders[i].Trades {
|
||||
resp.Orders[i].Trades[x].Fee = resp.Orders[i].Trades[x].Fee / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].Trades[x].Price = resp.Orders[i].Trades[x].Price / common.SATOSHIS_PER_BTC
|
||||
resp.Orders[i].Trades[x].Volume = resp.Orders[i].Trades[x].Volume / common.SATOSHIS_PER_BTC
|
||||
}
|
||||
}
|
||||
return resp.Orders, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetAccountBalance() ([]BTCMarketsAccountBalance, error) {
|
||||
balance := []BTCMarketsAccountBalance{}
|
||||
err := b.SendAuthenticatedRequest("GET", BTCMARKETS_ACCOUNT_BALANCE, nil, &balance)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range balance {
|
||||
if balance[i].Currency == "LTC" || balance[i].Currency == "BTC" {
|
||||
balance[i].Balance = balance[i].Balance / common.SATOSHIS_PER_BTC
|
||||
balance[i].PendingFunds = balance[i].PendingFunds / common.SATOSHIS_PER_BTC
|
||||
}
|
||||
}
|
||||
return balance, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interface{}, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
|
||||
request := ""
|
||||
payload := []byte("")
|
||||
|
||||
if data != nil {
|
||||
payload, err = common.JSONEncode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request = path + "\n" + nonce + "\n" + string(payload)
|
||||
} else {
|
||||
request = path + "\n" + nonce + "\n"
|
||||
}
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(request), []byte(b.APISecret))
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Sending %s request to URL %s with params %s\n", reqType, BTCMARKETS_API_URL+path, request)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Accept"] = "application/json"
|
||||
headers["Accept-Charset"] = "UTF-8"
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["apikey"] = b.APIKey
|
||||
headers["timestamp"] = nonce
|
||||
headers["signature"] = common.Base64Encode(hmac)
|
||||
|
||||
resp, err := common.SendHTTPRequest(reqType, BTCMARKETS_API_URL+path, headers, bytes.NewBuffer(payload))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
56
exchanges/btcmarkets/btcmarkets_types.go
Normal file
56
exchanges/btcmarkets/btcmarkets_types.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package btcmarkets
|
||||
|
||||
type BTCMarketsTicker struct {
|
||||
BestBID float64
|
||||
BestAsk float64
|
||||
LastPrice float64
|
||||
Currency string
|
||||
Instrument string
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
type BTCMarketsTrade struct {
|
||||
TradeID int64 `json:"tid"`
|
||||
Amount float64 `json:"amount"`
|
||||
Price float64 `json:"price"`
|
||||
Date int64 `json:"date"`
|
||||
}
|
||||
|
||||
type BTCMarketsOrderbook struct {
|
||||
Currency string `json:"currency"`
|
||||
Instrument string `json:"instrument"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Asks [][]float64 `json:"asks"`
|
||||
Bids [][]float64 `json:"bids"`
|
||||
}
|
||||
|
||||
type BTCMarketsTradeResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
CreationTime float64 `json:"creationTime"`
|
||||
Description string `json:"description"`
|
||||
Price float64 `json:"price"`
|
||||
Volume float64 `json:"volume"`
|
||||
Fee float64 `json:"fee"`
|
||||
}
|
||||
|
||||
type BTCMarketsOrder struct {
|
||||
ID int64 `json:"id"`
|
||||
Currency string `json:"currency"`
|
||||
Instrument string `json:"instrument"`
|
||||
OrderSide string `json:"orderSide"`
|
||||
OrderType string `json:"ordertype"`
|
||||
CreationTime float64 `json:"creationTime"`
|
||||
Status string `json:"status"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
Price float64 `json:"price"`
|
||||
Volume float64 `json:"volume"`
|
||||
OpenVolume float64 `json:"openVolume"`
|
||||
ClientRequestId string `json:"clientRequestId"`
|
||||
Trades []BTCMarketsTradeResponse `json:"trades"`
|
||||
}
|
||||
|
||||
type BTCMarketsAccountBalance struct {
|
||||
Balance float64 `json:"balance"`
|
||||
PendingFunds float64 `json:"pendingFunds"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
79
exchanges/btcmarkets/btcmarkets_wrapper.go
Normal file
79
exchanges/btcmarkets/btcmarkets_wrapper.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package btcmarkets
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (b *BTCMarkets) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||
}
|
||||
|
||||
for b.Enabled {
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := b.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//BTCMarketsLastUSD, _ := ConvertCurrency(ticker.Last, "AUD", "USD")
|
||||
//BTCMarketsBestBidUSD, _ := ConvertCurrency(ticker.Bid, "AUD", "USD")
|
||||
//BTCMarketsBestAskUSD, _ := ConvertCurrency(ticker.Ask, "AUD", "USD")
|
||||
//log.Printf("BTC Markets %s: Last %f (%f) Bid %f (%f) Ask %f (%f)\n", currency, BTCMarketsLastUSD, ticker.Last, BTCMarketsBestBidUSD, ticker.Bid, BTCMarketsBestAskUSD, ticker.Ask)
|
||||
log.Printf("BTC Markets %s: Last %f Bid %f Ask %f \n", currency, ticker.Last, ticker.Bid, ticker.Ask)
|
||||
//AddExchangeInfo(b.GetName(), currency[0:3], currency[3:], ticker.Last, 0)
|
||||
//AddExchangeInfo(b.GetName(), currency[0:3], "USD", BTCMarketsLastUSD, 0)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := b.GetTicker(currency)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
tickerPrice.Ask = tick.BestAsk
|
||||
tickerPrice.Bid = tick.BestBID
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Last = tick.LastPrice
|
||||
ticker.ProcessTicker(b.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the BTCMarkets exchange
|
||||
func (e *BTCMarkets) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetAccountBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Balance
|
||||
exchangeCurrency.Hold = accountBalance[i].PendingFunds
|
||||
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
@@ -5,12 +5,27 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
WarningBase64DecryptSecretKeyFailed = "WARNING -- Exchange %s unable to base64 decode secret key.. Disabling Authenticated API support."
|
||||
)
|
||||
|
||||
//ExchangeAccountInfo : Generic type to hold each exchange's holdings in all enabled currencies
|
||||
type ExchangeAccountInfo struct {
|
||||
ExchangeName string
|
||||
Currencies []ExchangeAccountCurrencyInfo
|
||||
}
|
||||
|
||||
//ExchangeAccountCurrencyInfo : Sub type to store currency name and value
|
||||
type ExchangeAccountCurrencyInfo struct {
|
||||
CurrencyName string
|
||||
TotalValue float64
|
||||
Hold float64
|
||||
}
|
||||
|
||||
type ExchangeBase struct {
|
||||
Name string
|
||||
Enabled bool
|
||||
@@ -27,6 +42,18 @@ type ExchangeBase struct {
|
||||
APIUrl string
|
||||
}
|
||||
|
||||
//IBotExchange : Enforces standard functions for all exchanges supported in gocryptotrader
|
||||
type IBotExchange interface {
|
||||
Setup(exch config.ExchangeConfig)
|
||||
Start()
|
||||
SetDefaults()
|
||||
GetName() string
|
||||
IsEnabled() bool
|
||||
GetTickerPrice(currency string) (ticker.TickerPrice, error)
|
||||
GetEnabledCurrencies() []string
|
||||
GetExchangeAccountInfo() (ExchangeAccountInfo, error)
|
||||
}
|
||||
|
||||
func (e *ExchangeBase) GetName() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
219
exchanges/gdax/gdax_types.go
Normal file
219
exchanges/gdax/gdax_types.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package gdax
|
||||
|
||||
type GDAXTicker struct {
|
||||
TradeID int64 `json:"trade_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
|
||||
type GDAXProduct struct {
|
||||
ID string `json:"id"`
|
||||
BaseCurrency string `json:"base_currency"`
|
||||
QuoteCurrency string `json:"quote_currency"`
|
||||
BaseMinSize float64 `json:"base_min_size,string"`
|
||||
BaseMaxSize int64 `json:"base_max_size,string"`
|
||||
QuoteIncrement float64 `json:"quote_increment,string"`
|
||||
DisplayName string `json:"string"`
|
||||
}
|
||||
|
||||
type GDAXOrderL1L2 struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
NumOrders float64
|
||||
}
|
||||
|
||||
type GDAXOrderL3 struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
OrderID string
|
||||
}
|
||||
|
||||
type GDAXOrderbookL1L2 struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
Bids [][]GDAXOrderL1L2 `json:"asks"`
|
||||
Asks [][]GDAXOrderL1L2 `json:"asks"`
|
||||
}
|
||||
|
||||
type GDAXOrderbookL3 struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
Bids [][]GDAXOrderL3 `json:"asks"`
|
||||
Asks [][]GDAXOrderL3 `json:"asks"`
|
||||
}
|
||||
|
||||
type GDAXOrderbookResponse struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
Bids [][]interface{} `json:"bids"`
|
||||
Asks [][]interface{} `json:"asks"`
|
||||
}
|
||||
|
||||
type GDAXTrade struct {
|
||||
TradeID int64 `json:"trade_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
Time string `json:"time"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
type GDAXStats struct {
|
||||
Open float64 `json:"open,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
}
|
||||
|
||||
type GDAXCurrency struct {
|
||||
ID string
|
||||
Name string
|
||||
MinSize float64 `json:"min_size,string"`
|
||||
}
|
||||
|
||||
type GDAXHistory struct {
|
||||
Time int64
|
||||
Low float64
|
||||
High float64
|
||||
Open float64
|
||||
Close float64
|
||||
Volume float64
|
||||
}
|
||||
|
||||
type GDAXAccountResponse struct {
|
||||
ID string `json:"id"`
|
||||
Balance float64 `json:"balance,string"`
|
||||
Hold float64 `json:"hold,string"`
|
||||
Available float64 `json:"available,string"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
type GDAXAccountLedgerResponse struct {
|
||||
ID string `json:"id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Balance float64 `json:"balance,string"`
|
||||
Type string `json:"type"`
|
||||
details interface{} `json:"details"`
|
||||
}
|
||||
|
||||
type GDAXAccountHolds struct {
|
||||
ID string `json:"id"`
|
||||
AccountID string `json:"account_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Type string `json:"type"`
|
||||
Reference string `json:"ref"`
|
||||
}
|
||||
|
||||
type GDAXOrdersResponse struct {
|
||||
ID string `json:"id"`
|
||||
Size float64 `json:"size,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
ProductID string `json:"product_id"`
|
||||
Status string `json:"status"`
|
||||
FilledSize float64 `json:"filled_size,string"`
|
||||
FillFees float64 `json:"fill_fees,string"`
|
||||
Settled bool `json:"settled"`
|
||||
Side string `json:"side"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
type GDAXOrderResponse struct {
|
||||
ID string `json:"id"`
|
||||
Size float64 `json:"size,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
DoneReason string `json:"done_reason"`
|
||||
Status string `json:"status"`
|
||||
Settled bool `json:"settled"`
|
||||
FilledSize float64 `json:"filled_size,string"`
|
||||
ProductID string `json:"product_id"`
|
||||
FillFees float64 `json:"fill_fees,string"`
|
||||
Side string `json:"side"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DoneAt string `json:"done_at"`
|
||||
}
|
||||
|
||||
type GDAXFillResponse struct {
|
||||
TradeID int `json:"trade_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
OrderID string `json:"order_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
Settled bool `json:"settled"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
type GDAXReportResponse struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
CompletedAt string `json:"completed_at"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
FileURL string `json:"file_url"`
|
||||
Params struct {
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
} `json:params"`
|
||||
}
|
||||
|
||||
type GDAXWebsocketSubscribe struct {
|
||||
Type string `json:"type"`
|
||||
ProductID string `json:"product_id"`
|
||||
}
|
||||
|
||||
type GDAXWebsocketReceived struct {
|
||||
Type string `json:"type"`
|
||||
Time string `json:"time"`
|
||||
Sequence int `json:"sequence"`
|
||||
OrderID string `json:"order_id"`
|
||||
Size float64 `json:"size,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
type GDAXWebsocketOpen struct {
|
||||
Type string `json:"type"`
|
||||
Time string `json:"time"`
|
||||
Sequence int `json:"sequence"`
|
||||
OrderID string `json:"order_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
RemainingSize float64 `json:"remaining_size,string"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
type GDAXWebsocketDone struct {
|
||||
Type string `json:"type"`
|
||||
Time string `json:"time"`
|
||||
Sequence int `json:"sequence"`
|
||||
Price float64 `json:"price,string"`
|
||||
OrderID string `json:"order_id"`
|
||||
Reason string `json:"reason"`
|
||||
Side string `json:"side"`
|
||||
RemainingSize float64 `json:"remaining_size,string"`
|
||||
}
|
||||
|
||||
type GDAXWebsocketMatch struct {
|
||||
Type string `json:"type"`
|
||||
TradeID int `json:"trade_id"`
|
||||
Sequence int `json:"sequence"`
|
||||
MakerOrderID string `json:"maker_order_id"`
|
||||
TakerOrderID string `json:"taker_order_id"`
|
||||
Time string `json:"time"`
|
||||
Size float64 `json:"size,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
type GDAXWebsocketChange struct {
|
||||
Type string `json:"type"`
|
||||
Time string `json:"time"`
|
||||
Sequence int `json:"sequence"`
|
||||
OrderID string `json:"order_id"`
|
||||
NewSize float64 `json:"new_size,string"`
|
||||
OldSize float64 `json:"old_size,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
116
exchanges/gdax/gdax_wrapper.go
Normal file
116
exchanges/gdax/gdax_wrapper.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package gdax
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (g *GDAX) Start() {
|
||||
go g.Run()
|
||||
}
|
||||
|
||||
func (g *GDAX) Run() {
|
||||
if g.Verbose {
|
||||
log.Printf("%s Websocket: %s. (url: %s).\n", g.GetName(), common.IsEnabled(g.Websocket), GDAX_WEBSOCKET_URL)
|
||||
log.Printf("%s polling delay: %ds.\n", g.GetName(), g.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", g.GetName(), len(g.EnabledPairs), g.EnabledPairs)
|
||||
}
|
||||
|
||||
if g.Websocket {
|
||||
go g.WebsocketClient()
|
||||
}
|
||||
|
||||
exchangeProducts, err := g.GetProducts()
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available products.\n", g.GetName())
|
||||
} else {
|
||||
log.Println(exchangeProducts)
|
||||
/*
|
||||
currencies := []string{}
|
||||
for _, x := range exchangeProducts {
|
||||
if x.ID != "BTC" && x.ID != "USD" && x.ID != "GBP" {
|
||||
currencies = append(currencies, x.ID[0:3]+x.ID[4:])
|
||||
}
|
||||
}
|
||||
diff := common.StringSliceDifference(g.AvailablePairs, currencies)
|
||||
if len(diff) > 0 {
|
||||
exch, err := bot.config.GetExchangeConfig(g.Name)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Printf("%s Updating available pairs. Difference: %s.\n", g.Name, diff)
|
||||
exch.AvailablePairs = common.JoinStrings(currencies, ",")
|
||||
bot.config.UpdateExchangeConfig(exch)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
for g.Enabled {
|
||||
for _, x := range g.EnabledPairs {
|
||||
currency := x[0:3] + "-" + x[3:]
|
||||
go func() {
|
||||
ticker, err := g.GetTickerPrice(currency)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("GDAX %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(g.GetName(), currency[0:3], currency[4:], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * g.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the GDAX exchange
|
||||
func (e *GDAX) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetAccounts()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Balance
|
||||
exchangeCurrency.Hold = accountBalance[i].Hold
|
||||
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(g.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := g.GetTicker(currency)
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
}
|
||||
|
||||
stats, err := g.GetStats(currency)
|
||||
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
}
|
||||
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[4:]
|
||||
tickerPrice.Volume = stats.Volume
|
||||
tickerPrice.Last = tick.Price
|
||||
tickerPrice.High = stats.High
|
||||
tickerPrice.Low = stats.Low
|
||||
ticker.ProcessTicker(g.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
427
exchanges/gdax/gdaxhttp.go
Normal file
427
exchanges/gdax/gdaxhttp.go
Normal file
@@ -0,0 +1,427 @@
|
||||
package gdax
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
GDAX_API_URL = "https://api.gdax.com/"
|
||||
GDAX_API_VERISON = "0"
|
||||
GDAX_PRODUCTS = "products"
|
||||
GDAX_ORDERBOOK = "book"
|
||||
GDAX_TICKER = "ticker"
|
||||
GDAX_TRADES = "trades"
|
||||
GDAX_HISTORY = "candles"
|
||||
GDAX_STATS = "stats"
|
||||
GDAX_CURRENCIES = "currencies"
|
||||
GDAX_ACCOUNTS = "accounts"
|
||||
GDAX_LEDGER = "ledger"
|
||||
GDAX_HOLDS = "holds"
|
||||
GDAX_ORDERS = "orders"
|
||||
GDAX_FILLS = "fills"
|
||||
GDAX_TRANSFERS = "transfers"
|
||||
GDAX_REPORTS = "reports"
|
||||
)
|
||||
|
||||
type GDAX struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (g *GDAX) SetDefaults() {
|
||||
g.Name = "GDAX"
|
||||
g.Enabled = false
|
||||
g.Verbose = false
|
||||
g.TakerFee = 0.25
|
||||
g.MakerFee = 0
|
||||
g.Verbose = false
|
||||
g.Websocket = false
|
||||
g.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (g *GDAX) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
g.SetEnabled(false)
|
||||
} else {
|
||||
g.Enabled = true
|
||||
g.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
g.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, true)
|
||||
g.RESTPollingDelay = exch.RESTPollingDelay
|
||||
g.Verbose = exch.Verbose
|
||||
g.Websocket = exch.Websocket
|
||||
g.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
g.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
g.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GDAX) GetFee(maker bool) float64 {
|
||||
if maker {
|
||||
return g.MakerFee
|
||||
} else {
|
||||
return g.TakerFee
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GDAX) GetProducts() ([]GDAXProduct, error) {
|
||||
products := []GDAXProduct{}
|
||||
err := common.SendHTTPGetRequest(GDAX_API_URL+GDAX_PRODUCTS, true, &products)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return products, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetOrderbook(symbol string, level int) (interface{}, error) {
|
||||
orderbook := GDAXOrderbookResponse{}
|
||||
path := ""
|
||||
if level > 0 {
|
||||
levelStr := strconv.Itoa(level)
|
||||
path = fmt.Sprintf("%s/%s/%s?level=%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_ORDERBOOK, levelStr)
|
||||
} else {
|
||||
path = fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_ORDERBOOK)
|
||||
}
|
||||
|
||||
err := common.SendHTTPGetRequest(path, true, &orderbook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if level == 3 {
|
||||
ob := GDAXOrderbookL3{}
|
||||
ob.Sequence = orderbook.Sequence
|
||||
for _, x := range orderbook.Asks {
|
||||
price, err := strconv.ParseFloat((x[0].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat((x[1].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
order := make([]GDAXOrderL3, 1)
|
||||
order[0].Price = price
|
||||
order[0].Amount = amount
|
||||
order[0].OrderID = x[2].(string)
|
||||
ob.Asks = append(ob.Asks, order)
|
||||
}
|
||||
for _, x := range orderbook.Bids {
|
||||
price, err := strconv.ParseFloat((x[0].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat((x[1].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
order := make([]GDAXOrderL3, 1)
|
||||
order[0].Price = price
|
||||
order[0].Amount = amount
|
||||
order[0].OrderID = x[2].(string)
|
||||
ob.Bids = append(ob.Bids, order)
|
||||
}
|
||||
return ob, nil
|
||||
} else {
|
||||
ob := GDAXOrderbookL1L2{}
|
||||
ob.Sequence = orderbook.Sequence
|
||||
for _, x := range orderbook.Asks {
|
||||
price, err := strconv.ParseFloat((x[0].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat((x[1].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
order := make([]GDAXOrderL1L2, 1)
|
||||
order[0].Price = price
|
||||
order[0].Amount = amount
|
||||
order[0].NumOrders = x[2].(float64)
|
||||
ob.Asks = append(ob.Asks, order)
|
||||
}
|
||||
for _, x := range orderbook.Bids {
|
||||
price, err := strconv.ParseFloat((x[0].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat((x[1].(string)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
order := make([]GDAXOrderL1L2, 1)
|
||||
order[0].Price = price
|
||||
order[0].Amount = amount
|
||||
order[0].NumOrders = x[2].(float64)
|
||||
ob.Bids = append(ob.Bids, order)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GDAX) GetTicker(symbol string) (GDAXTicker, error) {
|
||||
ticker := GDAXTicker{}
|
||||
path := fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_TICKER)
|
||||
err := common.SendHTTPGetRequest(path, true, &ticker)
|
||||
|
||||
if err != nil {
|
||||
return ticker, err
|
||||
}
|
||||
return ticker, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetTrades(symbol string) ([]GDAXTrade, error) {
|
||||
trades := []GDAXTrade{}
|
||||
path := fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_TRADES)
|
||||
err := common.SendHTTPGetRequest(path, true, &trades)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetHistoricRates(symbol string, start, end, granularity int64) ([]GDAXHistory, error) {
|
||||
history := []GDAXHistory{}
|
||||
values := url.Values{}
|
||||
|
||||
if start > 0 {
|
||||
values.Set("start", strconv.FormatInt(start, 10))
|
||||
}
|
||||
|
||||
if end > 0 {
|
||||
values.Set("end", strconv.FormatInt(end, 10))
|
||||
}
|
||||
|
||||
if granularity > 0 {
|
||||
values.Set("granularity", strconv.FormatInt(granularity, 10))
|
||||
}
|
||||
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_HISTORY), values)
|
||||
err := common.SendHTTPGetRequest(path, true, &history)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetStats(symbol string) (GDAXStats, error) {
|
||||
stats := GDAXStats{}
|
||||
path := fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_STATS)
|
||||
err := common.SendHTTPGetRequest(path, true, &stats)
|
||||
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetCurrencies() ([]GDAXCurrency, error) {
|
||||
currencies := []GDAXCurrency{}
|
||||
err := common.SendHTTPGetRequest(GDAX_API_URL+GDAX_CURRENCIES, true, ¤cies)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return currencies, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetAccounts() ([]GDAXAccountResponse, error) {
|
||||
resp := []GDAXAccountResponse{}
|
||||
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+GDAX_ACCOUNTS, nil, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetAccount(account string) (GDAXAccountResponse, error) {
|
||||
resp := GDAXAccountResponse{}
|
||||
path := fmt.Sprintf("%s/%s", GDAX_ACCOUNTS, account)
|
||||
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetAccountHistory(accountID string) ([]GDAXAccountLedgerResponse, error) {
|
||||
resp := []GDAXAccountLedgerResponse{}
|
||||
path := fmt.Sprintf("%s/%s/%s", GDAX_ACCOUNTS, accountID, GDAX_LEDGER)
|
||||
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetHolds(accountID string) ([]GDAXAccountHolds, error) {
|
||||
resp := []GDAXAccountHolds{}
|
||||
path := fmt.Sprintf("%s/%s/%s", GDAX_ACCOUNTS, accountID, GDAX_HOLDS)
|
||||
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) PlaceOrder(clientRef string, price, amount float64, side string, productID, stp string) (string, error) {
|
||||
request := make(map[string]interface{})
|
||||
|
||||
if clientRef != "" {
|
||||
request["client_oid"] = clientRef
|
||||
}
|
||||
|
||||
request["price"] = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
request["size"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
request["side"] = side
|
||||
request["product_id"] = productID
|
||||
|
||||
if stp != "" {
|
||||
request["stp"] = stp
|
||||
}
|
||||
|
||||
type OrderResponse struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
resp := OrderResponse{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+GDAX_ORDERS, request, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) CancelOrder(orderID string) error {
|
||||
path := fmt.Sprintf("%s/%s", GDAX_ORDERS, orderID)
|
||||
err := g.SendAuthenticatedHTTPRequest("DELETE", GDAX_API_URL+path, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetOrders(params url.Values) ([]GDAXOrdersResponse, error) {
|
||||
path := common.EncodeURLValues(GDAX_API_URL+GDAX_ORDERS, params)
|
||||
resp := []GDAXOrdersResponse{}
|
||||
err := g.SendAuthenticatedHTTPRequest("GET", path, nil, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetOrder(orderID string) (GDAXOrderResponse, error) {
|
||||
path := fmt.Sprintf("%s/%s", GDAX_ORDERS, orderID)
|
||||
resp := GDAXOrderResponse{}
|
||||
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetFills(params url.Values) ([]GDAXFillResponse, error) {
|
||||
path := common.EncodeURLValues(GDAX_API_URL+GDAX_FILLS, params)
|
||||
resp := []GDAXFillResponse{}
|
||||
err := g.SendAuthenticatedHTTPRequest("GET", path, nil, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) Transfer(transferType string, amount float64, accountID string) error {
|
||||
request := make(map[string]interface{})
|
||||
request["type"] = transferType
|
||||
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
request["GDAX_account_id"] = accountID
|
||||
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+GDAX_TRANSFERS, request, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetReport(reportType, startDate, endDate string) (GDAXReportResponse, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["type"] = reportType
|
||||
request["start_date"] = startDate
|
||||
request["end_date"] = endDate
|
||||
|
||||
resp := GDAXReportResponse{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+GDAX_REPORTS, request, &resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetReportStatus(reportID string) (GDAXReportResponse, error) {
|
||||
path := fmt.Sprintf("%s/%s", GDAX_REPORTS, reportID)
|
||||
resp := GDAXReportResponse{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+path, nil, &resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
|
||||
payload := []byte("")
|
||||
|
||||
if params != nil {
|
||||
payload, err = common.JSONEncode(params)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
if g.Verbose {
|
||||
log.Printf("Request JSON: %s\n", payload)
|
||||
}
|
||||
}
|
||||
|
||||
message := timestamp + method + path + string(payload)
|
||||
hmac := common.GetHMAC(common.HASH_SHA256, []byte(message), []byte(g.APISecret))
|
||||
headers := make(map[string]string)
|
||||
headers["CB-ACCESS-SIGN"] = common.Base64Encode([]byte(hmac))
|
||||
headers["CB-ACCESS-TIMESTAMP"] = timestamp
|
||||
headers["CB-ACCESS-KEY"] = g.APIKey
|
||||
headers["CB-ACCESS-PASSPHRASE"] = g.ClientID
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, GDAX_API_URL+path, headers, bytes.NewBuffer(payload))
|
||||
|
||||
if g.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
|
||||
}
|
||||
125
exchanges/gdax/gdaxwebsocket.go
Normal file
125
exchanges/gdax/gdaxwebsocket.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package gdax
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
const (
|
||||
GDAX_WEBSOCKET_URL = "wss://ws-feed.exchange.gdax.com"
|
||||
)
|
||||
|
||||
func (g *GDAX) WebsocketSubscribe(product string, conn *websocket.Conn) error {
|
||||
subscribe := GDAXWebsocketSubscribe{"subscribe", product}
|
||||
json, err := common.JSONEncode(subscribe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = conn.WriteMessage(websocket.TextMessage, json)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GDAX) WebsocketClient() {
|
||||
for g.Enabled && g.Websocket {
|
||||
var Dialer websocket.Dialer
|
||||
conn, _, err := Dialer.Dial(GDAX_WEBSOCKET_URL, http.Header{})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Error: %s\n", g.GetName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%s Connected to Websocket.\n", g.GetName())
|
||||
|
||||
currencies := []string{}
|
||||
for _, x := range g.EnabledPairs {
|
||||
currency := x[0:3] + "-" + x[3:]
|
||||
currencies = append(currencies, currency)
|
||||
}
|
||||
|
||||
for _, x := range currencies {
|
||||
err = g.WebsocketSubscribe(x, conn)
|
||||
if err != nil {
|
||||
log.Printf("%s Websocket subscription error: %s\n", g.GetName(), err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if g.Verbose {
|
||||
log.Printf("%s Subscribed to product messages.", g.GetName())
|
||||
}
|
||||
|
||||
for g.Enabled && g.Websocket {
|
||||
msgType, resp, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
|
||||
switch msgType {
|
||||
case websocket.TextMessage:
|
||||
type MsgType struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
msgType := MsgType{}
|
||||
err := common.JSONDecode(resp, &msgType)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch msgType.Type {
|
||||
case "error":
|
||||
log.Println(string(resp))
|
||||
break
|
||||
case "received":
|
||||
received := GDAXWebsocketReceived{}
|
||||
err := common.JSONDecode(resp, &received)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
case "open":
|
||||
open := GDAXWebsocketOpen{}
|
||||
err := common.JSONDecode(resp, &open)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
case "done":
|
||||
done := GDAXWebsocketDone{}
|
||||
err := common.JSONDecode(resp, &done)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
case "match":
|
||||
match := GDAXWebsocketMatch{}
|
||||
err := common.JSONDecode(resp, &match)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
case "change":
|
||||
change := GDAXWebsocketChange{}
|
||||
err := common.JSONDecode(resp, &change)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
conn.Close()
|
||||
log.Printf("%s Websocket client disconnected.", g.GetName())
|
||||
}
|
||||
}
|
||||
94
exchanges/gemini/gemini_types.go
Normal file
94
exchanges/gemini/gemini_types.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package gemini
|
||||
|
||||
type GeminiOrderbookEntry struct {
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
}
|
||||
|
||||
type GeminiOrderbook struct {
|
||||
Bids []GeminiOrderbookEntry `json:"bids"`
|
||||
Asks []GeminiOrderbookEntry `json:"asks"`
|
||||
}
|
||||
|
||||
type GeminiTrade struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
TID int64 `json:"tid"`
|
||||
Price float64 `json:"price"`
|
||||
Amount float64 `json:"amount"`
|
||||
Side string `json:"taker"`
|
||||
}
|
||||
|
||||
type GeminiOrder struct {
|
||||
OrderID int64 `json:"order_id"`
|
||||
ClientOrderID string `json:"client_order_id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Exchange string `json:"exchange"`
|
||||
Price float64 `json:"price,string"`
|
||||
AvgExecutionPrice float64 `json:"avg_execution_price,string"`
|
||||
Side string `json:"side"`
|
||||
Type string `json:"type"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
TimestampMS int64 `json:"timestampms"`
|
||||
IsLive bool `json:"is_live"`
|
||||
IsCancelled bool `json:"is_cancelled"`
|
||||
WasForced bool `json:"was_forced"`
|
||||
ExecutedAmount float64 `json:"executed_amount,string"`
|
||||
RemainingAmount float64 `json:"remaining_amount,string"`
|
||||
OriginalAmount float64 `json:"original_amount,string"`
|
||||
}
|
||||
|
||||
type GeminiOrderResult struct {
|
||||
Result bool `json:"result"`
|
||||
}
|
||||
|
||||
type GeminiTradeHistory struct {
|
||||
Price float64 `json:"price"`
|
||||
Amount float64 `json:"amount"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
TimestampMS int64 `json:"timestampms"`
|
||||
Type string `json:"type"`
|
||||
FeeCurrency string `json:"fee_currency"`
|
||||
FeeAmount float64 `json:"fee_amount"`
|
||||
TID int64 `json:"tid"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
ClientOrderID string `json:"client_order_id"`
|
||||
}
|
||||
|
||||
type GeminiBalance struct {
|
||||
Currency string `json:"currency"`
|
||||
Amount float64 `json:"amount"`
|
||||
Available float64 `json:"available"`
|
||||
}
|
||||
|
||||
type GeminiTicker struct {
|
||||
Ask float64 `json:"ask,string"`
|
||||
Bid float64 `json:"bid,string"`
|
||||
Last float64 `json:"last,string"`
|
||||
Volume struct {
|
||||
Currency float64
|
||||
USD float64
|
||||
Timestamp int64
|
||||
}
|
||||
}
|
||||
|
||||
type GeminiAuction struct {
|
||||
LastAuctionPrice float64 `json:"last_auction_price,string"`
|
||||
LastAuctionQuantity float64 `json:"last_auction_quantity,string"`
|
||||
LastHighestBidPrice float64 `json:"last_highest_bid_price,string"`
|
||||
LastLowestAskPrice float64 `json:"last_lowest_ask_price,string"`
|
||||
NextUpdateMS int64 `json:"next_update_ms"`
|
||||
NextAuctionMS int64 `json:"next_auction_ms"`
|
||||
LastAuctionEID int64 `json:"last_auction_eid"`
|
||||
}
|
||||
type GeminiAuctionHistory struct {
|
||||
AuctionID int64 `json:"auction_id"`
|
||||
AuctionPrice float64 `json:"auction_price,string"`
|
||||
AuctionQuantity float64 `json:"auction_quantity,string"`
|
||||
EID int64 `json:"eid"`
|
||||
HighestBidPrice float64 `json:"highest_bid_price,string"`
|
||||
LowestAskPrice float64 `json:"lowest_ask_price,string"`
|
||||
AuctionResult string `json:"auction_result"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
TimestampMS int64 `json:"timestampms"`
|
||||
EventType string `json:"event_type"`
|
||||
}
|
||||
97
exchanges/gemini/gemini_wrapper.go
Normal file
97
exchanges/gemini/gemini_wrapper.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package gemini
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (g *Gemini) Start() {
|
||||
go g.Run()
|
||||
}
|
||||
|
||||
func (g *Gemini) Run() {
|
||||
if g.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", g.GetName(), g.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", g.GetName(), len(g.EnabledPairs), g.EnabledPairs)
|
||||
}
|
||||
|
||||
exchangeProducts, err := g.GetSymbols()
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available symbols.\n", g.GetName())
|
||||
} else {
|
||||
log.Println(exchangeProducts)
|
||||
/*
|
||||
exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",")
|
||||
diff := common.StringSliceDifference(g.AvailablePairs, exchangeProducts)
|
||||
if len(diff) > 0 {
|
||||
exch, err := bot.config.GetExchangeConfig(g.Name)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Printf("%s Updating available pairs. Difference: %s.\n", g.Name, diff)
|
||||
exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",")
|
||||
bot.config.UpdateExchangeConfig(exch)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
for g.Enabled {
|
||||
for _, x := range g.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := g.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("Gemini %s Last %f Bid %f Ask %f Volume %f\n", currency, ticker.Last, ticker.Bid, ticker.Ask, ticker.Volume)
|
||||
//AddExchangeInfo(g.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * g.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Gemini exchange
|
||||
func (e *Gemini) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetBalances()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Amount
|
||||
exchangeCurrency.Hold = accountBalance[i].Available
|
||||
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(g.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := g.GetTicker(currency)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
tickerPrice.Ask = tick.Ask
|
||||
tickerPrice.Bid = tick.Bid
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Volume.USD
|
||||
ticker.ProcessTicker(g.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
288
exchanges/gemini/geminihttp.go
Normal file
288
exchanges/gemini/geminihttp.go
Normal file
@@ -0,0 +1,288 @@
|
||||
package gemini
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
GEMINI_API_URL = "https://api.gemini.com"
|
||||
GEMINI_API_VERSION = "1"
|
||||
|
||||
GEMINI_SYMBOLS = "symbols"
|
||||
GEMINI_TICKER = "pubticker"
|
||||
GEMINI_AUCTION = "auction"
|
||||
GEMINI_AUCTION_HISTORY = "history"
|
||||
GEMINI_ORDERBOOK = "book"
|
||||
GEMINI_TRADES = "trades"
|
||||
GEMINI_ORDERS = "orders"
|
||||
GEMINI_ORDER_NEW = "order/new"
|
||||
GEMINI_ORDER_CANCEL = "order/cancel"
|
||||
GEMINI_ORDER_CANCEL_SESSION = "order/cancel/session"
|
||||
GEMINI_ORDER_CANCEL_ALL = "order/cancel/all"
|
||||
GEMINI_ORDER_STATUS = "order/status"
|
||||
GEMINI_MYTRADES = "mytrades"
|
||||
GEMINI_BALANCES = "balances"
|
||||
GEMINI_HEARTBEAT = "heartbeat"
|
||||
)
|
||||
|
||||
type Gemini struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (g *Gemini) SetDefaults() {
|
||||
g.Name = "Gemini"
|
||||
g.Enabled = false
|
||||
g.Verbose = false
|
||||
g.Websocket = false
|
||||
g.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (g *Gemini) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
g.SetEnabled(false)
|
||||
} else {
|
||||
g.Enabled = true
|
||||
g.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
g.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
g.RESTPollingDelay = exch.RESTPollingDelay
|
||||
g.Verbose = exch.Verbose
|
||||
g.Websocket = exch.Websocket
|
||||
g.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
g.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
g.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Gemini) GetTicker(currency string) (GeminiTicker, error) {
|
||||
|
||||
type TickerResponse struct {
|
||||
Ask float64 `json:"ask,string"`
|
||||
Bid float64 `json:"bid,string"`
|
||||
Last float64 `json:"last,string"`
|
||||
Volume map[string]interface{}
|
||||
}
|
||||
|
||||
ticker := GeminiTicker{}
|
||||
resp := TickerResponse{}
|
||||
path := fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_TICKER, currency)
|
||||
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
if err != nil {
|
||||
return ticker, err
|
||||
}
|
||||
|
||||
ticker.Ask = resp.Ask
|
||||
ticker.Bid = resp.Bid
|
||||
ticker.Last = resp.Last
|
||||
|
||||
ticker.Volume.Currency, _ = strconv.ParseFloat(resp.Volume[currency[0:3]].(string), 64)
|
||||
ticker.Volume.USD, _ = strconv.ParseFloat(resp.Volume["USD"].(string), 64)
|
||||
|
||||
time, _ := resp.Volume["timestamp"].(float64)
|
||||
ticker.Volume.Timestamp = int64(time)
|
||||
|
||||
return ticker, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetSymbols() ([]string, error) {
|
||||
symbols := []string{}
|
||||
path := fmt.Sprintf("%s/v%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_SYMBOLS)
|
||||
err := common.SendHTTPGetRequest(path, true, &symbols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return symbols, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetAuction(currency string) (GeminiAuction, error) {
|
||||
path := fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_AUCTION, currency)
|
||||
auction := GeminiAuction{}
|
||||
err := common.SendHTTPGetRequest(path, true, &auction)
|
||||
if err != nil {
|
||||
return auction, err
|
||||
}
|
||||
return auction, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetAuctionHistory(currency string, params url.Values) ([]GeminiAuctionHistory, error) {
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_AUCTION, currency, GEMINI_AUCTION_HISTORY), params)
|
||||
auctionHist := []GeminiAuctionHistory{}
|
||||
err := common.SendHTTPGetRequest(path, true, &auctionHist)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return auctionHist, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetOrderbook(currency string, params url.Values) (GeminiOrderbook, error) {
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_ORDERBOOK, currency), params)
|
||||
orderbook := GeminiOrderbook{}
|
||||
err := common.SendHTTPGetRequest(path, true, &orderbook)
|
||||
if err != nil {
|
||||
return GeminiOrderbook{}, err
|
||||
}
|
||||
|
||||
return orderbook, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetTrades(currency string, params url.Values) ([]GeminiTrade, error) {
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_TRADES, currency), params)
|
||||
trades := []GeminiTrade{}
|
||||
err := common.SendHTTPGetRequest(path, true, &trades)
|
||||
if err != nil {
|
||||
return []GeminiTrade{}, err
|
||||
}
|
||||
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) NewOrder(symbol string, amount, price float64, side, orderType string) (int64, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["symbol"] = symbol
|
||||
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
request["price"] = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
request["side"] = side
|
||||
request["type"] = orderType
|
||||
|
||||
response := GeminiOrder{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDER_NEW, request, &response)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return response.OrderID, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) CancelOrder(OrderID int64) (GeminiOrder, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["order_id"] = OrderID
|
||||
|
||||
response := GeminiOrder{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDER_CANCEL, request, &response)
|
||||
if err != nil {
|
||||
return GeminiOrder{}, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) CancelOrders(sessions bool) ([]GeminiOrderResult, error) {
|
||||
response := []GeminiOrderResult{}
|
||||
path := GEMINI_ORDER_CANCEL_ALL
|
||||
if sessions {
|
||||
path = GEMINI_ORDER_CANCEL_SESSION
|
||||
}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", path, nil, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetOrderStatus(orderID int64) (GeminiOrder, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["order_id"] = orderID
|
||||
|
||||
response := GeminiOrder{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDER_STATUS, request, &response)
|
||||
if err != nil {
|
||||
return GeminiOrder{}, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetOrders() ([]GeminiOrder, error) {
|
||||
response := []GeminiOrder{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDERS, nil, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetTradeHistory(symbol string, timestamp int64) ([]GeminiTradeHistory, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["symbol"] = symbol
|
||||
request["timestamp"] = timestamp
|
||||
|
||||
response := []GeminiTradeHistory{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_MYTRADES, request, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) GetBalances() ([]GeminiBalance, error) {
|
||||
response := []GeminiBalance{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_BALANCES, nil, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) PostHeartbeat() (bool, error) {
|
||||
type Response struct {
|
||||
Result bool `json:"result"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_HEARTBEAT, nil, &response)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return response.Result, nil
|
||||
}
|
||||
|
||||
func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
|
||||
request := make(map[string]interface{})
|
||||
request["request"] = fmt.Sprintf("/v%s/%s", GEMINI_API_VERSION, path)
|
||||
request["nonce"] = time.Now().UnixNano()
|
||||
|
||||
if params != nil {
|
||||
for key, value := range params {
|
||||
request[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
PayloadJson, err := common.JSONEncode(request)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
if g.Verbose {
|
||||
log.Printf("Request JSON: %s\n", PayloadJson)
|
||||
}
|
||||
|
||||
PayloadBase64 := common.Base64Encode(PayloadJson)
|
||||
hmac := common.GetHMAC(common.HASH_SHA512_384, []byte(PayloadBase64), []byte(g.APISecret))
|
||||
headers := make(map[string]string)
|
||||
headers["X-GEMINI-APIKEY"] = g.APIKey
|
||||
headers["X-GEMINI-PAYLOAD"] = PayloadBase64
|
||||
headers["X-GEMINI-SIGNATURE"] = common.HexEncodeToString(hmac)
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, GEMINI_API_URL+path, headers, strings.NewReader(""))
|
||||
|
||||
if g.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
|
||||
}
|
||||
15
exchanges/huobi/huobi_types.go
Normal file
15
exchanges/huobi/huobi_types.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package huobi
|
||||
|
||||
type HuobiTicker struct {
|
||||
High float64
|
||||
Low float64
|
||||
Last float64
|
||||
Vol float64
|
||||
Buy float64
|
||||
Sell float64
|
||||
}
|
||||
|
||||
type HuobiTickerResponse struct {
|
||||
Time string
|
||||
Ticker HuobiTicker
|
||||
}
|
||||
80
exchanges/huobi/huobi_wrapper.go
Normal file
80
exchanges/huobi/huobi_wrapper.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package huobi
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (h *HUOBI) Start() {
|
||||
go h.Run()
|
||||
}
|
||||
|
||||
func (h *HUOBI) Run() {
|
||||
if h.Verbose {
|
||||
log.Printf("%s Websocket: %s (url: %s).\n", h.GetName(), common.IsEnabled(h.Websocket), HUOBI_SOCKETIO_ADDRESS)
|
||||
log.Printf("%s polling delay: %ds.\n", h.GetName(), h.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", h.GetName(), len(h.EnabledPairs), h.EnabledPairs)
|
||||
}
|
||||
|
||||
if h.Websocket {
|
||||
go h.WebsocketClient()
|
||||
}
|
||||
|
||||
for h.Enabled {
|
||||
for _, x := range h.EnabledPairs {
|
||||
currency := common.StringToLower(x[0:3])
|
||||
go func() {
|
||||
ticker, err := h.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
/*
|
||||
HuobiLastUSD, _ := ConvertCurrency(ticker.Last, "CNY", "USD")
|
||||
HuobiHighUSD, _ := ConvertCurrency(ticker.High, "CNY", "USD")
|
||||
HuobiLowUSD, _ := ConvertCurrency(ticker.Low, "CNY", "USD")
|
||||
log.Printf("Huobi %s: Last %f (%f) High %f (%f) Low %f (%f) Volume %f\n", currency, HuobiLastUSD, ticker.Last, HuobiHighUSD, ticker.High, HuobiLowUSD, ticker.Low, ticker.Volume)
|
||||
AddExchangeInfo(h.GetName(), common.StringToUpper(currency[0:3]), common.StringToUpper(currency[3:]), ticker.Last, ticker.Volume)
|
||||
AddExchangeInfo(h.GetName(), common.StringToUpper(currency[0:3]), "USD", HuobiLastUSD, ticker.Volume)
|
||||
*/
|
||||
log.Printf("Huobi %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * h.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(h.GetName(), common.StringToUpper(currency[0:3]), common.StringToUpper(currency[3:]))
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := h.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[3:])
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(h.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//TODO: retrieve HUOBI balance info
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the HUOBI exchange
|
||||
func (e *HUOBI) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
return response, nil
|
||||
}
|
||||
205
exchanges/huobi/huobihttp.go
Normal file
205
exchanges/huobi/huobihttp.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package huobi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
HUOBI_API_URL = "https://api.huobi.com/apiv2.php"
|
||||
HUOBI_API_VERSION = "2"
|
||||
)
|
||||
|
||||
type HUOBI struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (h *HUOBI) SetDefaults() {
|
||||
h.Name = "Huobi"
|
||||
h.Enabled = false
|
||||
h.Fee = 0
|
||||
h.Verbose = false
|
||||
h.Websocket = false
|
||||
h.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (h *HUOBI) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
h.SetEnabled(false)
|
||||
} else {
|
||||
h.Enabled = true
|
||||
h.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
h.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
h.RESTPollingDelay = exch.RESTPollingDelay
|
||||
h.Verbose = exch.Verbose
|
||||
h.Websocket = exch.Websocket
|
||||
h.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
h.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
h.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetFee() float64 {
|
||||
return h.Fee
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetTicker(symbol string) (HuobiTicker, error) {
|
||||
resp := HuobiTickerResponse{}
|
||||
path := fmt.Sprintf("http://api.huobi.com/staticmarket/ticker_%s_json.js", symbol)
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return HuobiTicker{}, err
|
||||
}
|
||||
return resp.Ticker, nil
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetOrderBook(symbol string) bool {
|
||||
path := fmt.Sprintf("http://api.huobi.com/staticmarket/depth_%s_json.js", symbol)
|
||||
err := common.SendHTTPGetRequest(path, true, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetAccountInfo() {
|
||||
err := h.SendAuthenticatedRequest("get_account_info", url.Values{})
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetOrders(coinType int) {
|
||||
values := url.Values{}
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
err := h.SendAuthenticatedRequest("get_orders", values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetOrderInfo(orderID, coinType int) {
|
||||
values := url.Values{}
|
||||
values.Set("id", strconv.Itoa(orderID))
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
err := h.SendAuthenticatedRequest("order_info", values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) Trade(orderType string, coinType int, price, amount float64) {
|
||||
values := url.Values{}
|
||||
if orderType != "buy" {
|
||||
orderType = "sell"
|
||||
}
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
err := h.SendAuthenticatedRequest(orderType, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) MarketTrade(orderType string, coinType int, price, amount float64) {
|
||||
values := url.Values{}
|
||||
if orderType != "buy_market" {
|
||||
orderType = "sell_market"
|
||||
}
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
err := h.SendAuthenticatedRequest(orderType, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) CancelOrder(orderID, coinType int) {
|
||||
values := url.Values{}
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
values.Set("id", strconv.Itoa(orderID))
|
||||
err := h.SendAuthenticatedRequest("cancel_order", values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) ModifyOrder(orderType string, coinType, orderID int, price, amount float64) {
|
||||
values := url.Values{}
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
values.Set("id", strconv.Itoa(orderID))
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
err := h.SendAuthenticatedRequest("modify_order", values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetNewDealOrders(coinType int) {
|
||||
values := url.Values{}
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
err := h.SendAuthenticatedRequest("get_new_deal_orders", values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetOrderIDByTradeID(coinType, orderID int) {
|
||||
values := url.Values{}
|
||||
values.Set("coin_type", strconv.Itoa(coinType))
|
||||
values.Set("trade_id", strconv.Itoa(orderID))
|
||||
err := h.SendAuthenticatedRequest("get_order_id_by_trade_id", values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) SendAuthenticatedRequest(method string, v url.Values) error {
|
||||
v.Set("access_key", h.APIKey)
|
||||
v.Set("created", strconv.FormatInt(time.Now().Unix(), 10))
|
||||
v.Set("method", method)
|
||||
hash := common.GetMD5([]byte(v.Encode() + "&secret_key=" + h.APISecret))
|
||||
v.Set("sign", common.StringToLower(common.HexEncodeToString(hash)))
|
||||
encoded := v.Encode()
|
||||
|
||||
if h.Verbose {
|
||||
log.Printf("Sending POST request to %s with params %s\n", HUOBI_API_URL, encoded)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", HUOBI_API_URL, headers, strings.NewReader(encoded))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
221
exchanges/huobi/huobiwebsocket.go
Normal file
221
exchanges/huobi/huobiwebsocket.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package huobi
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/socketio"
|
||||
)
|
||||
|
||||
const (
|
||||
HUOBI_SOCKETIO_ADDRESS = "https://hq.huobi.com:443"
|
||||
|
||||
//Service API
|
||||
HUOBI_SOCKET_REQ_SYMBOL_LIST = "reqSymbolList"
|
||||
HUOBI_SOCKET_REQ_SYMBOL_DETAIL = "reqSymbolDetail"
|
||||
HUOBI_SOCKET_REQ_SUBSCRIBE = "reqMsgSubscribe"
|
||||
HUOBI_SOCKET_REQ_UNSUBSCRIBE = "reqMsgUnsubscribe"
|
||||
|
||||
// Market data API
|
||||
HUOBI_SOCKET_MARKET_DETAIL = "marketDetail"
|
||||
HUOBI_SOCKET_TRADE_DETAIL = "tradeDetail"
|
||||
HUOBI_SOCKET_MARKET_DEPTH_TOP = "marketDepthTop"
|
||||
HUOBI_SOCKET_MARKET_DEPTH_TOP_SHORT = "marketDepthTopShort"
|
||||
HUOBI_SOCKET_MARKET_DEPTH = "marketDepth"
|
||||
HUOBI_SOCKET_MARKET_DEPTH_TOP_DIFF = "marketDepthTopDiff"
|
||||
HUOBI_SOCKET_MARKET_DEPTH_DIFF = "marketDepthDiff"
|
||||
HUOBI_SOCKET_MARKET_LAST_KLINE = "lastKLine"
|
||||
HUOBI_SOCKET_MARKET_LAST_TIMELINE = "lastTimeLine"
|
||||
HUOBI_SOCKET_MARKET_OVERVIEW = "marketOverview"
|
||||
HUOBI_SOCKET_MARKET_STATIC = "marketStatic"
|
||||
|
||||
// History data API
|
||||
HUOBI_SOCKET_REQ_TIMELINE = "reqTimeLine"
|
||||
HUOBI_SOCKET_REQ_KLINE = "reqKLine"
|
||||
HUOBI_SOCKET_REQ_DEPTH_TOP = "reqMarketDepthTop"
|
||||
HUOBI_SOCKET_REQ_DEPTH = "reqMarketDepth"
|
||||
HUOBI_SOCKET_REQ_TRADE_DETAIL_TOP = "reqTradeDetailTop"
|
||||
HUOBI_SOCKET_REQ_MARKET_DETAIL = "reqMarketDetail"
|
||||
)
|
||||
|
||||
var HuobiSocket *socketio.SocketIO
|
||||
|
||||
type HuobiDepth struct {
|
||||
SymbolID string `json:"symbolId"`
|
||||
Time float64 `json:"time"`
|
||||
Version float64 `json:"version"`
|
||||
BidName string `json:"bidName"`
|
||||
BidPrice []float64 `json:"bidPrice"`
|
||||
BidTotal []float64 `json:"bidTotal"`
|
||||
BidAmount []float64 `json:"bidAmount"`
|
||||
AskName string `json:"askName"`
|
||||
AskPrice []float64 `json:"askPrice"`
|
||||
AskTotal []float64 `json:"askTotal"`
|
||||
AskAmount []float64 `json:"askAmount"`
|
||||
}
|
||||
|
||||
type HuobiWebsocketTrade struct {
|
||||
Price []float64 `json:"price"`
|
||||
Level []float64 `json:"level"`
|
||||
Amount []float64 `json:"amount"`
|
||||
AccuAmount []float64 `json:"accuAmount"`
|
||||
}
|
||||
|
||||
type HuobiWebsocketTradeDetail struct {
|
||||
SymbolID string `json:"symbolId"`
|
||||
TradeID []int64 `json:"tradeId"`
|
||||
Price []float64 `json:"price"`
|
||||
Time []int64 `json:"time"`
|
||||
Amount []float64 `json:"amount"`
|
||||
TopBids []HuobiWebsocketTrade `json:"topBids"`
|
||||
TopAsks []HuobiWebsocketTrade `json:"topAsks"`
|
||||
}
|
||||
|
||||
type HuobiWebsocketMarketOverview struct {
|
||||
SymbolID string `json:"symbolId"`
|
||||
Last float64 `json:"priceNew"`
|
||||
Open float64 `json:"priceOpen"`
|
||||
High float64 `json:"priceHigh"`
|
||||
Low float64 `json:"priceLow"`
|
||||
Ask float64 `json:"priceAsk"`
|
||||
Bid float64 `json:"priceBid"`
|
||||
Volume float64 `json:"totalVolume"`
|
||||
TotalAmount float64 `json:"totalAmount"`
|
||||
}
|
||||
|
||||
type HuobiWebsocketLastTimeline struct {
|
||||
ID int64 `json:"_id"`
|
||||
SymbolID string `json:"symbolId"`
|
||||
Time int64 `json:"time"`
|
||||
LastPrice float64 `json:"priceLast"`
|
||||
Amount float64 `json:"amount"`
|
||||
Volume float64 `json:"volume"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
type HuobiResponse struct {
|
||||
Version int `json:"version"`
|
||||
MsgType string `json:"msgType"`
|
||||
RequestIndex int64 `json:"requestIndex"`
|
||||
RetCode int64 `json:"retCode"`
|
||||
RetMessage string `json:"retMsg"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
func (h *HUOBI) BuildHuobiWebsocketRequest(msgType string, requestIndex int64, symbolRequest []string) map[string]interface{} {
|
||||
request := map[string]interface{}{}
|
||||
request["version"] = 1
|
||||
request["msgType"] = msgType
|
||||
|
||||
if requestIndex != 0 {
|
||||
request["requestIndex"] = requestIndex
|
||||
}
|
||||
|
||||
if len(symbolRequest) != 0 {
|
||||
request["symbolIdList"] = symbolRequest
|
||||
}
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
func (h *HUOBI) BuildHuobiWebsocketRequestExtra(msgType string, requestIndex int64, symbolIdList interface{}) interface{} {
|
||||
request := map[string]interface{}{}
|
||||
request["version"] = 1
|
||||
request["msgType"] = msgType
|
||||
|
||||
if requestIndex != 0 {
|
||||
request["requestIndex"] = requestIndex
|
||||
}
|
||||
|
||||
request["symbolList"] = symbolIdList
|
||||
return request
|
||||
}
|
||||
|
||||
func (h *HUOBI) BuildHuobiWebsocketParamsList(objectName, currency, pushType, period, count, from, to, percentage string) interface{} {
|
||||
list := map[string]interface{}{}
|
||||
list["symbolId"] = currency
|
||||
list["pushType"] = pushType
|
||||
|
||||
if period != "" {
|
||||
list["period"] = period
|
||||
}
|
||||
if percentage != "" {
|
||||
list["percent"] = percentage
|
||||
}
|
||||
if count != "" {
|
||||
list["count"] = count
|
||||
}
|
||||
if from != "" {
|
||||
list["from"] = from
|
||||
}
|
||||
if to != "" {
|
||||
list["to"] = to
|
||||
}
|
||||
|
||||
listArray := []map[string]interface{}{}
|
||||
listArray = append(listArray, list)
|
||||
|
||||
listCompleted := make(map[string][]map[string]interface{})
|
||||
listCompleted[objectName] = listArray
|
||||
return listCompleted
|
||||
}
|
||||
|
||||
func (h *HUOBI) OnConnect(output chan socketio.Message) {
|
||||
if h.Verbose {
|
||||
log.Printf("%s Connected to Websocket.", h.GetName())
|
||||
}
|
||||
|
||||
for _, x := range h.EnabledPairs {
|
||||
currency := common.StringToLower(x)
|
||||
msg := h.BuildHuobiWebsocketRequestExtra(HUOBI_SOCKET_REQ_SUBSCRIBE, 100, h.BuildHuobiWebsocketParamsList(HUOBI_SOCKET_MARKET_OVERVIEW, currency, "pushLong", "", "", "", "", ""))
|
||||
result, err := common.JSONEncode(msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
output <- socketio.CreateMessageEvent("request", string(result), nil, HuobiSocket.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) OnDisconnect(output chan socketio.Message) {
|
||||
log.Printf("%s Disconnected from websocket server.. Reconnecting.\n", h.GetName())
|
||||
h.WebsocketClient()
|
||||
}
|
||||
|
||||
func (h *HUOBI) OnError() {
|
||||
log.Printf("%s Error with Websocket connection.. Reconnecting.\n", h.GetName())
|
||||
h.WebsocketClient()
|
||||
}
|
||||
|
||||
func (h *HUOBI) OnMessage(message []byte, output chan socketio.Message) {
|
||||
}
|
||||
|
||||
func (h *HUOBI) OnRequest(message []byte, output chan socketio.Message) {
|
||||
response := HuobiResponse{}
|
||||
err := common.JSONDecode(message, &response)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) WebsocketClient() {
|
||||
events := make(map[string]func(message []byte, output chan socketio.Message))
|
||||
events["request"] = h.OnRequest
|
||||
events["message"] = h.OnMessage
|
||||
|
||||
HuobiSocket = &socketio.SocketIO{
|
||||
Version: 0.9,
|
||||
OnConnect: h.OnConnect,
|
||||
OnEvent: events,
|
||||
OnError: h.OnError,
|
||||
OnDisconnect: h.OnDisconnect,
|
||||
}
|
||||
|
||||
for h.Enabled && h.Websocket {
|
||||
err := socketio.ConnectToSocket(HUOBI_SOCKETIO_ADDRESS, HuobiSocket)
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Err: %s\n", h.GetName(), err)
|
||||
continue
|
||||
}
|
||||
log.Printf("%s Disconnected from Websocket.\n", h.GetName())
|
||||
}
|
||||
}
|
||||
34
exchanges/itbit/itbit_types.go
Normal file
34
exchanges/itbit/itbit_types.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package itbit
|
||||
|
||||
type ItBitTicker struct {
|
||||
Pair string
|
||||
Bid float64 `json:",string"`
|
||||
BidAmt float64 `json:",string"`
|
||||
Ask float64 `json:",string"`
|
||||
AskAmt float64 `json:",string"`
|
||||
LastPrice float64 `json:",string"`
|
||||
LastAmt float64 `json:",string"`
|
||||
Volume24h float64 `json:",string"`
|
||||
VolumeToday float64 `json:",string"`
|
||||
High24h float64 `json:",string"`
|
||||
Low24h float64 `json:",string"`
|
||||
HighToday float64 `json:",string"`
|
||||
LowToday float64 `json:",string"`
|
||||
OpenToday float64 `json:",string"`
|
||||
VwapToday float64 `json:",string"`
|
||||
Vwap24h float64 `json:",string"`
|
||||
ServertimeUTC string
|
||||
}
|
||||
|
||||
type ItbitOrderbookEntry struct {
|
||||
Quantitiy float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
}
|
||||
|
||||
type ItBitOrderbookResponse struct {
|
||||
ServerTimeUTC string `json:"serverTimeUTC"`
|
||||
LastUpdatedTimeUTC string `json:"lastUpdatedTimeUTC"`
|
||||
Ticker string `json:"ticker"`
|
||||
Bids []ItbitOrderbookEntry `json:"bids"`
|
||||
Asks []ItbitOrderbookEntry `json:"asks"`
|
||||
}
|
||||
67
exchanges/itbit/itbit_wrapper.go
Normal file
67
exchanges/itbit/itbit_wrapper.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package itbit
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (i *ItBit) Start() {
|
||||
go i.Run()
|
||||
}
|
||||
func (i *ItBit) Run() {
|
||||
if i.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", i.GetName(), i.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", i.GetName(), len(i.EnabledPairs), i.EnabledPairs)
|
||||
}
|
||||
|
||||
for i.Enabled {
|
||||
for _, x := range i.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := i.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("ItBit %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(i.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * i.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(i.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := i.GetTicker(currency)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Ask = tick.Ask
|
||||
tickerPrice.Bid = tick.Bid
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
tickerPrice.Last = tick.LastPrice
|
||||
tickerPrice.High = tick.High24h
|
||||
tickerPrice.Low = tick.Low24h
|
||||
tickerPrice.Volume = tick.Volume24h
|
||||
ticker.ProcessTicker(i.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//TODO Get current holdings from ItBit
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the ItBit exchange
|
||||
func (e *ItBit) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
return response, nil
|
||||
}
|
||||
284
exchanges/itbit/itbithttp.go
Normal file
284
exchanges/itbit/itbithttp.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package itbit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
ITBIT_API_URL = "https://api.itbit.com/v1"
|
||||
ITBIT_API_VERSION = "1"
|
||||
)
|
||||
|
||||
type ItBit struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (i *ItBit) SetDefaults() {
|
||||
i.Name = "ITBIT"
|
||||
i.Enabled = false
|
||||
i.MakerFee = -0.10
|
||||
i.TakerFee = 0.50
|
||||
i.Verbose = false
|
||||
i.Websocket = false
|
||||
i.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (i *ItBit) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
i.SetEnabled(false)
|
||||
} else {
|
||||
i.Enabled = true
|
||||
i.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
i.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false)
|
||||
i.RESTPollingDelay = exch.RESTPollingDelay
|
||||
i.Verbose = exch.Verbose
|
||||
i.Websocket = exch.Websocket
|
||||
i.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
i.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
i.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetFee(maker bool) float64 {
|
||||
if maker {
|
||||
return i.MakerFee
|
||||
} else {
|
||||
return i.TakerFee
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetTicker(currency string) (ItBitTicker, error) {
|
||||
path := ITBIT_API_URL + "/markets/" + currency + "/ticker"
|
||||
var itbitTicker ItBitTicker
|
||||
err := common.SendHTTPGetRequest(path, true, &itbitTicker)
|
||||
if err != nil {
|
||||
return ItBitTicker{}, err
|
||||
}
|
||||
return itbitTicker, nil
|
||||
}
|
||||
|
||||
func (i *ItBit) GetOrderbook(currency string) (ItBitOrderbookResponse, error) {
|
||||
response := ItBitOrderbookResponse{}
|
||||
path := ITBIT_API_URL + "/markets/" + currency + "/order_book"
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return ItBitOrderbookResponse{}, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (i *ItBit) GetTradeHistory(currency, timestamp string) bool {
|
||||
req := "/trades?since=" + timestamp
|
||||
err := common.SendHTTPGetRequest(ITBIT_API_URL+"markets/"+currency+req, true, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *ItBit) GetWallets(params url.Values) {
|
||||
params.Set("userId", i.ClientID)
|
||||
path := "/wallets?" + params.Encode()
|
||||
|
||||
err := i.SendAuthenticatedHTTPRequest("GET", path, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) CreateWallet(walletName string) {
|
||||
path := "/wallets"
|
||||
params := make(map[string]interface{})
|
||||
params["userId"] = i.ClientID
|
||||
params["name"] = walletName
|
||||
|
||||
err := i.SendAuthenticatedHTTPRequest("POST", path, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetWallet(walletID string) {
|
||||
path := "/wallets/" + walletID
|
||||
err := i.SendAuthenticatedHTTPRequest("GET", path, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetWalletBalance(walletID, currency string) {
|
||||
path := "/wallets/ " + walletID + "/balances/" + currency
|
||||
err := i.SendAuthenticatedHTTPRequest("GET", path, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetWalletTrades(walletID string, params url.Values) {
|
||||
path := common.EncodeURLValues("/wallets/"+walletID+"/trades", params)
|
||||
err := i.SendAuthenticatedHTTPRequest("GET", path, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetWalletOrders(walletID string, params url.Values) {
|
||||
path := common.EncodeURLValues("/wallets/"+walletID+"/orders", params)
|
||||
err := i.SendAuthenticatedHTTPRequest("GET", path, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) PlaceWalletOrder(walletID, side, orderType, currency string, amount, price float64, instrument string, clientRef string) {
|
||||
path := "/wallets/" + walletID + "/orders"
|
||||
params := make(map[string]interface{})
|
||||
params["side"] = side
|
||||
params["type"] = orderType
|
||||
params["currency"] = currency
|
||||
params["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
params["price"] = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
params["instrument"] = instrument
|
||||
|
||||
if clientRef != "" {
|
||||
params["clientOrderIdentifier"] = clientRef
|
||||
}
|
||||
|
||||
err := i.SendAuthenticatedHTTPRequest("POST", path, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetWalletOrder(walletID, orderID string) {
|
||||
path := "/wallets/" + walletID + "/orders/" + orderID
|
||||
err := i.SendAuthenticatedHTTPRequest("GET", path, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) CancelWalletOrder(walletID, orderID string) {
|
||||
path := "/wallets/" + walletID + "/orders/" + orderID
|
||||
err := i.SendAuthenticatedHTTPRequest("DELETE", path, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) PlaceWithdrawalRequest(walletID, currency, address string, amount float64) {
|
||||
path := "/wallets/" + walletID + "/cryptocurrency_withdrawals"
|
||||
params := make(map[string]interface{})
|
||||
params["currency"] = currency
|
||||
params["amount"] = amount
|
||||
params["address"] = address
|
||||
|
||||
err := i.SendAuthenticatedHTTPRequest("POST", path, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetDepositAddress(walletID, currency string) {
|
||||
path := "/wallets/" + walletID + "/cryptocurrency_deposits"
|
||||
params := make(map[string]interface{})
|
||||
params["currency"] = currency
|
||||
|
||||
err := i.SendAuthenticatedHTTPRequest("POST", path, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) WalletTransfer(walletID, sourceWallet, destWallet string, amount float64, currency string) {
|
||||
path := "/wallets/" + walletID + "/wallet_transfers"
|
||||
params := make(map[string]interface{})
|
||||
params["sourceWalletId"] = sourceWallet
|
||||
params["destinationWalletId"] = destWallet
|
||||
params["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
params["currencyCode"] = currency
|
||||
|
||||
err := i.SendAuthenticatedHTTPRequest("POST", path, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) SendAuthenticatedHTTPRequest(method string, path string, params map[string]interface{}) (err error) {
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
|
||||
nonce, err := strconv.Atoi(timestamp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nonce -= 1
|
||||
request := make(map[string]interface{})
|
||||
url := ITBIT_API_URL + path
|
||||
|
||||
if params != nil {
|
||||
for key, value := range params {
|
||||
request[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
PayloadJson := []byte("")
|
||||
|
||||
if params != nil {
|
||||
PayloadJson, err = common.JSONEncode(request)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON Marshal request")
|
||||
}
|
||||
|
||||
if i.Verbose {
|
||||
log.Printf("Request JSON: %s\n", PayloadJson)
|
||||
}
|
||||
}
|
||||
|
||||
nonceStr := strconv.Itoa(nonce)
|
||||
message, err := common.JSONEncode([]string{method, url, string(PayloadJson), nonceStr, timestamp})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
hash := common.GetSHA256([]byte(nonceStr + string(message)))
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(url+string(hash)), []byte(i.APISecret))
|
||||
signature := common.Base64Encode(hmac)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Authorization"] = i.ClientID + ":" + signature
|
||||
headers["X-Auth-Timestamp"] = timestamp
|
||||
headers["X-Auth-Nonce"] = nonceStr
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, url, headers, bytes.NewBuffer([]byte(PayloadJson)))
|
||||
|
||||
if i.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
542
exchanges/kraken/kraken.go
Normal file
542
exchanges/kraken/kraken.go
Normal file
@@ -0,0 +1,542 @@
|
||||
package kraken
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
KRAKEN_API_URL = "https://api.kraken.com"
|
||||
KRAKEN_API_VERSION = "0"
|
||||
KRAKEN_SERVER_TIME = "Time"
|
||||
KRAKEN_ASSETS = "Assets"
|
||||
KRAKEN_ASSET_PAIRS = "AssetPairs"
|
||||
KRAKEN_TICKER = "Ticker"
|
||||
KRAKEN_OHLC = "OHLC"
|
||||
KRAKEN_DEPTH = "Depth"
|
||||
KRAKEN_TRADES = "Trades"
|
||||
KRAKEN_SPREAD = "Spread"
|
||||
KRAKEN_BALANCE = "Balance"
|
||||
KRAKEN_TRADE_BALANCE = "TradeBalance"
|
||||
KRAKEN_OPEN_ORDERS = "OpenOrders"
|
||||
KRAKEN_CLOSED_ORDERS = "ClosedOrders"
|
||||
KRAKEN_QUERY_ORDERS = "QueryOrders"
|
||||
KRAKEN_TRADES_HISTORY = "TradesHistory"
|
||||
KRAKEN_QUERY_TRADES = "QueryTrades"
|
||||
KRAKEN_OPEN_POSITIONS = "OpenPositions"
|
||||
KRAKEN_LEDGERS = "Ledgers"
|
||||
KRAKEN_QUERY_LEDGERS = "QueryLedgers"
|
||||
KRAKEN_TRADE_VOLUME = "TradeVolume"
|
||||
KRAKEN_ORDER_CANCEL = "CancelOrder"
|
||||
KRAKEN_ORDER_PLACE = "AddOrder"
|
||||
)
|
||||
|
||||
type Kraken struct {
|
||||
exchange.ExchangeBase
|
||||
CryptoFee, FiatFee float64
|
||||
Ticker map[string]KrakenTicker
|
||||
}
|
||||
|
||||
func (k *Kraken) SetDefaults() {
|
||||
k.Name = "Kraken"
|
||||
k.Enabled = false
|
||||
k.FiatFee = 0.35
|
||||
k.CryptoFee = 0.10
|
||||
k.Verbose = false
|
||||
k.Websocket = false
|
||||
k.RESTPollingDelay = 10
|
||||
k.Ticker = make(map[string]KrakenTicker)
|
||||
}
|
||||
|
||||
func (k *Kraken) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
k.SetEnabled(false)
|
||||
} else {
|
||||
k.Enabled = true
|
||||
k.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
k.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
k.RESTPollingDelay = exch.RESTPollingDelay
|
||||
k.Verbose = exch.Verbose
|
||||
k.Websocket = exch.Websocket
|
||||
k.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
k.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
k.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kraken) GetFee(cryptoTrade bool) float64 {
|
||||
if cryptoTrade {
|
||||
return k.CryptoFee
|
||||
} else {
|
||||
return k.FiatFee
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kraken) GetServerTime() error {
|
||||
var result interface{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_SERVER_TIME)
|
||||
err := common.SendHTTPGetRequest(path, true, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetAssets() error {
|
||||
var result interface{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_ASSETS)
|
||||
err := common.SendHTTPGetRequest(path, true, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetAssetPairs() (map[string]KrakenAssetPairs, error) {
|
||||
type Response struct {
|
||||
Result map[string]KrakenAssetPairs `json:"result"`
|
||||
Error []interface{} `json:"error"`
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_ASSET_PAIRS)
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Result, nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetTicker(symbol string) error {
|
||||
values := url.Values{}
|
||||
values.Set("pair", symbol)
|
||||
|
||||
type Response struct {
|
||||
Error []interface{} `json:"error"`
|
||||
Data map[string]KrakenTickerResponse `json:"result"`
|
||||
}
|
||||
|
||||
resp := Response{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s?%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_TICKER, values.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resp.Error) > 0 {
|
||||
return errors.New(fmt.Sprintf("Kraken error: %s", resp.Error))
|
||||
}
|
||||
|
||||
for x, y := range resp.Data {
|
||||
x = x[1:4] + x[5:]
|
||||
ticker := KrakenTicker{}
|
||||
ticker.Ask, _ = strconv.ParseFloat(y.Ask[0], 64)
|
||||
ticker.Bid, _ = strconv.ParseFloat(y.Bid[0], 64)
|
||||
ticker.Last, _ = strconv.ParseFloat(y.Last[0], 64)
|
||||
ticker.Volume, _ = strconv.ParseFloat(y.Volume[1], 64)
|
||||
ticker.VWAP, _ = strconv.ParseFloat(y.VWAP[1], 64)
|
||||
ticker.Trades = y.Trades[1]
|
||||
ticker.Low, _ = strconv.ParseFloat(y.Low[1], 64)
|
||||
ticker.High, _ = strconv.ParseFloat(y.High[1], 64)
|
||||
ticker.Open, _ = strconv.ParseFloat(y.Open, 64)
|
||||
k.Ticker[x] = ticker
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetOHLC(symbol string) error {
|
||||
values := url.Values{}
|
||||
values.Set("pair", symbol)
|
||||
|
||||
var result interface{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s?%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_OHLC, values.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetDepth(symbol string) error {
|
||||
values := url.Values{}
|
||||
values.Set("pair", symbol)
|
||||
|
||||
var result interface{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s?%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_DEPTH, values.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetTrades(symbol string) error {
|
||||
values := url.Values{}
|
||||
values.Set("pair", symbol)
|
||||
|
||||
var result interface{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s?%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_TRADES, values.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetSpread(symbol string) {
|
||||
values := url.Values{}
|
||||
values.Set("pair", symbol)
|
||||
|
||||
var result interface{}
|
||||
path := fmt.Sprintf("%s/%s/public/%s?%s", KRAKEN_API_URL, KRAKEN_API_VERSION, KRAKEN_SPREAD, values.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &result)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kraken) GetBalance() {
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_BALANCE, url.Values{})
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) GetTradeBalance(symbol, asset string) {
|
||||
values := url.Values{}
|
||||
|
||||
if len(symbol) > 0 {
|
||||
values.Set("aclass", symbol)
|
||||
}
|
||||
|
||||
if len(asset) > 0 {
|
||||
values.Set("asset", asset)
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_TRADE_BALANCE, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) GetOpenOrders(showTrades bool, userref int64) {
|
||||
values := url.Values{}
|
||||
|
||||
if showTrades {
|
||||
values.Set("trades", "true")
|
||||
}
|
||||
|
||||
if userref != 0 {
|
||||
values.Set("userref", strconv.FormatInt(userref, 10))
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_OPEN_ORDERS, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) GetClosedOrders(showTrades bool, userref, start, end, offset int64, closetime string) {
|
||||
values := url.Values{}
|
||||
|
||||
if showTrades {
|
||||
values.Set("trades", "true")
|
||||
}
|
||||
|
||||
if userref != 0 {
|
||||
values.Set("userref", strconv.FormatInt(userref, 10))
|
||||
}
|
||||
|
||||
if start != 0 {
|
||||
values.Set("start", strconv.FormatInt(start, 10))
|
||||
}
|
||||
|
||||
if end != 0 {
|
||||
values.Set("end", strconv.FormatInt(end, 10))
|
||||
}
|
||||
|
||||
if offset != 0 {
|
||||
values.Set("ofs", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
|
||||
if len(closetime) > 0 {
|
||||
values.Set("closetime", closetime)
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_CLOSED_ORDERS, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) QueryOrdersInfo(showTrades bool, userref, txid int64) {
|
||||
values := url.Values{}
|
||||
|
||||
if showTrades {
|
||||
values.Set("trades", "true")
|
||||
}
|
||||
|
||||
if userref != 0 {
|
||||
values.Set("userref", strconv.FormatInt(userref, 10))
|
||||
}
|
||||
|
||||
if txid != 0 {
|
||||
values.Set("txid", strconv.FormatInt(userref, 10))
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_QUERY_ORDERS, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) GetTradesHistory(tradeType string, showRelatedTrades bool, start, end, offset int64) {
|
||||
values := url.Values{}
|
||||
|
||||
if len(tradeType) > 0 {
|
||||
values.Set("aclass", tradeType)
|
||||
}
|
||||
|
||||
if showRelatedTrades {
|
||||
values.Set("trades", "true")
|
||||
}
|
||||
|
||||
if start != 0 {
|
||||
values.Set("start", strconv.FormatInt(start, 10))
|
||||
}
|
||||
|
||||
if end != 0 {
|
||||
values.Set("end", strconv.FormatInt(end, 10))
|
||||
}
|
||||
|
||||
if offset != 0 {
|
||||
values.Set("offset", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_TRADES_HISTORY, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) QueryTrades(txid int64, showRelatedTrades bool) {
|
||||
values := url.Values{}
|
||||
values.Set("txid", strconv.FormatInt(txid, 10))
|
||||
|
||||
if showRelatedTrades {
|
||||
values.Set("trades", "true")
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_QUERY_TRADES, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) OpenPositions(txid int64, showPL bool) {
|
||||
values := url.Values{}
|
||||
values.Set("txid", strconv.FormatInt(txid, 10))
|
||||
|
||||
if showPL {
|
||||
values.Set("docalcs", "true")
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_OPEN_POSITIONS, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) GetLedgers(symbol, asset, ledgerType string, start, end, offset int64) {
|
||||
values := url.Values{}
|
||||
|
||||
if len(symbol) > 0 {
|
||||
values.Set("aclass", symbol)
|
||||
}
|
||||
|
||||
if len(asset) > 0 {
|
||||
values.Set("asset", asset)
|
||||
}
|
||||
|
||||
if len(ledgerType) > 0 {
|
||||
values.Set("type", ledgerType)
|
||||
}
|
||||
|
||||
if start != 0 {
|
||||
values.Set("start", strconv.FormatInt(start, 10))
|
||||
}
|
||||
|
||||
if end != 0 {
|
||||
values.Set("end", strconv.FormatInt(end, 10))
|
||||
}
|
||||
|
||||
if offset != 0 {
|
||||
values.Set("offset", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_LEDGERS, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) QueryLedgers(id string) {
|
||||
values := url.Values{}
|
||||
values.Set("id", id)
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_QUERY_LEDGERS, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) GetTradeVolume(symbol string) {
|
||||
values := url.Values{}
|
||||
values.Set("pair", symbol)
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_TRADE_VOLUME, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) AddOrder(symbol, side, orderType string, price, price2, volume, leverage, position float64) {
|
||||
values := url.Values{}
|
||||
values.Set("pairs", symbol)
|
||||
values.Set("type", side)
|
||||
values.Set("ordertype", orderType)
|
||||
values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
values.Set("price2", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
values.Set("volume", strconv.FormatFloat(volume, 'f', -1, 64))
|
||||
values.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64))
|
||||
values.Set("position", strconv.FormatFloat(position, 'f', -1, 64))
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_ORDER_PLACE, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) CancelOrder(orderID int64) {
|
||||
values := url.Values{}
|
||||
values.Set("txid", strconv.FormatInt(orderID, 10))
|
||||
|
||||
result, err := k.SendAuthenticatedHTTPRequest(KRAKEN_ORDER_CANCEL, values)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
}
|
||||
|
||||
func (k *Kraken) SendAuthenticatedHTTPRequest(method string, values url.Values) (interface{}, error) {
|
||||
path := fmt.Sprintf("/%s/private/%s", KRAKEN_API_VERSION, method)
|
||||
values.Set("nonce", strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
secret, err := common.Base64Decode(k.APISecret)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shasum := common.GetSHA256([]byte(values.Get("nonce") + values.Encode()))
|
||||
signature := common.Base64Encode(common.GetHMAC(common.HASH_SHA512, append([]byte(path), shasum...), secret))
|
||||
|
||||
if k.Verbose {
|
||||
log.Printf("Sending POST request to %s, path: %s.", KRAKEN_API_URL, path)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["API-Key"] = k.APIKey
|
||||
headers["API-Sign"] = signature
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", KRAKEN_API_URL+path, headers, strings.NewReader(values.Encode()))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if k.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
44
exchanges/kraken/kraken_types.go
Normal file
44
exchanges/kraken/kraken_types.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package kraken
|
||||
|
||||
type KrakenAssetPairs struct {
|
||||
Altname string `json:"altname"`
|
||||
AclassBase string `json:"aclass_base"`
|
||||
Base string `json:"base"`
|
||||
AclassQuote string `json:"aclass_quote"`
|
||||
Quote string `json:"quote"`
|
||||
Lot string `json:"lot"`
|
||||
PairDecimals int `json:"pair_decimals"`
|
||||
LotDecimals int `json:"lot_decimals"`
|
||||
LotMultiplier int `json:"lot_multiplier"`
|
||||
LeverageBuy []int `json:"leverage_buy"`
|
||||
LeverageSell []int `json:"leverage_sell"`
|
||||
Fees [][]float64 `json:"fees"`
|
||||
FeesMaker [][]float64 `json:"fees_maker"`
|
||||
FeeVolumeCurrency string `json:"fee_volume_currency"`
|
||||
MarginCall int `json:"margin_call"`
|
||||
MarginStop int `json:"margin_stop"`
|
||||
}
|
||||
|
||||
type KrakenTicker struct {
|
||||
Ask float64
|
||||
Bid float64
|
||||
Last float64
|
||||
Volume float64
|
||||
VWAP float64
|
||||
Trades int64
|
||||
Low float64
|
||||
High float64
|
||||
Open float64
|
||||
}
|
||||
|
||||
type KrakenTickerResponse struct {
|
||||
Ask []string `json:"a"`
|
||||
Bid []string `json:"b"`
|
||||
Last []string `json:"c"`
|
||||
Volume []string `json:"v"`
|
||||
VWAP []string `json:"p"`
|
||||
Trades []int64 `json:"t"`
|
||||
Low []string `json:"l"`
|
||||
High []string `json:"h"`
|
||||
Open string `json:"o"`
|
||||
}
|
||||
82
exchanges/kraken/kraken_wrapper.go
Normal file
82
exchanges/kraken/kraken_wrapper.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package kraken
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (k *Kraken) Start() {
|
||||
go k.Run()
|
||||
}
|
||||
|
||||
func (k *Kraken) Run() {
|
||||
if k.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", k.GetName(), k.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", k.GetName(), len(k.EnabledPairs), k.EnabledPairs)
|
||||
}
|
||||
|
||||
assetPairs, err := k.GetAssetPairs()
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available symbols.\n", k.GetName())
|
||||
} else {
|
||||
log.Println(assetPairs)
|
||||
/*
|
||||
var exchangeProducts []string
|
||||
for _, v := range assetPairs {
|
||||
exchangeProducts = append(exchangeProducts, v.Altname)
|
||||
}
|
||||
diff := common.StringSliceDifference(k.AvailablePairs, exchangeProducts)
|
||||
if len(diff) > 0 {
|
||||
exch, err := bot.config.GetExchangeConfig(k.Name)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Printf("%s Updating available pairs. Difference: %s.\n", k.Name, diff)
|
||||
exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",")
|
||||
bot.config.UpdateExchangeConfig(exch)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
for k.Enabled {
|
||||
err := k.GetTicker(common.JoinStrings(k.EnabledPairs, ","))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
for _, x := range k.EnabledPairs {
|
||||
ticker := k.Ticker[x]
|
||||
log.Printf("Kraken %s Last %f High %f Low %f Volume %f\n", x, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(k.GetName(), x[0:3], x[3:], ticker.Last, ticker.Volume)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Second * k.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
//This will return the TickerPrice struct when tickers are completed here..
|
||||
func (k *Kraken) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
var tickerPrice ticker.TickerPrice
|
||||
/*
|
||||
ticker, err := i.GetTicker(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return tickerPrice
|
||||
}
|
||||
tickerPrice.Ask = ticker.Ask
|
||||
tickerPrice.Bid = ticker.Bid
|
||||
*/
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//TODO: Retrieve Kraken info
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Kraken exchange
|
||||
func (e *Kraken) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
return response, nil
|
||||
}
|
||||
100
exchanges/lakebtc/lakebtc_types.go
Normal file
100
exchanges/lakebtc/lakebtc_types.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package lakebtc
|
||||
|
||||
type LakeBTCTicker struct {
|
||||
Last float64
|
||||
Bid float64
|
||||
Ask float64
|
||||
High float64
|
||||
Low float64
|
||||
Volume float64
|
||||
}
|
||||
|
||||
type LakeBTCOrderbookStructure struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type LakeBTCOrderbook struct {
|
||||
Bids []LakeBTCOrderbookStructure `json:"bids"`
|
||||
Asks []LakeBTCOrderbookStructure `json:"asks"`
|
||||
}
|
||||
|
||||
/* Silly hack due to API returning null instead of strings */
|
||||
type LakeBTCTickerResponse struct {
|
||||
Last interface{}
|
||||
Bid interface{}
|
||||
Ask interface{}
|
||||
High interface{}
|
||||
Low interface{}
|
||||
Volume interface{}
|
||||
}
|
||||
|
||||
type LakeBTCTradeHistory struct {
|
||||
Date int64 `json:"data"`
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
TID int64 `json:"tid"`
|
||||
}
|
||||
|
||||
type LakeBTCAccountInfo struct {
|
||||
Balance map[string]string `json:"balance"`
|
||||
Locked map[string]string `json:"locked"`
|
||||
Profile struct {
|
||||
Email string `json:"email"`
|
||||
UID string `json:"uid"`
|
||||
BTCDepositAddress string `json:"btc_deposit_addres"`
|
||||
} `json:"profile"`
|
||||
}
|
||||
|
||||
type LakeBTCTrade struct {
|
||||
ID int64 `json:"id"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
type LakeBTCOpenOrders struct {
|
||||
ID int64 `json:"id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Symbol string `json:"symbol"`
|
||||
Type string `json:"type"`
|
||||
At int64 `json:"at"`
|
||||
}
|
||||
|
||||
type LakeBTCOrders struct {
|
||||
ID int64 `json:"id"`
|
||||
OriginalAmount float64 `json:"original_amount,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Symbol string `json:"symbol"`
|
||||
Type string `json:"type"`
|
||||
State string `json:"state"`
|
||||
At int64 `json:"at"`
|
||||
}
|
||||
|
||||
type LakeBTCAuthenticaltedTradeHistory struct {
|
||||
Type string `json:"type"`
|
||||
Symbol string `json:"symbol"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
At int64 `json:"at"`
|
||||
}
|
||||
type LakeBTCExternalAccounts struct {
|
||||
ID int64 `json:"id,string"`
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
Alias interface{} `json:"alias"`
|
||||
Currencies string `json:"currencies"`
|
||||
State string `json:"state"`
|
||||
UpdatedAt int64 `json:"updated_at,string"`
|
||||
}
|
||||
|
||||
type LakeBTCWithdraw struct {
|
||||
ID int64 `json:"id,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Currency string `json:"currency"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
State string `json:"state"`
|
||||
Source string `json:"source"`
|
||||
ExternalAccountID int64 `json:"external_account_id,string"`
|
||||
At int64 `json:"at"`
|
||||
}
|
||||
85
exchanges/lakebtc/lakebtc_wrapper.go
Normal file
85
exchanges/lakebtc/lakebtc_wrapper.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package lakebtc
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (l *LakeBTC) Start() {
|
||||
go l.Run()
|
||||
}
|
||||
func (l *LakeBTC) Run() {
|
||||
if l.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", l.GetName(), l.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", l.GetName(), len(l.EnabledPairs), l.EnabledPairs)
|
||||
}
|
||||
|
||||
for l.Enabled {
|
||||
for _, x := range l.EnabledPairs {
|
||||
ticker, err := l.GetTickerPrice(x)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
log.Printf("LakeBTC BTC %s: Last %f High %f Low %f Volume %f\n", x[3:], ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//AddExchangeInfo(l.GetName(), x[0:3], x[3:], ticker.Last, ticker.Volume)
|
||||
}
|
||||
time.Sleep(time.Second * l.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(l.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
tick, err := l.GetTicker()
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
}
|
||||
|
||||
result, ok := tick[currency]
|
||||
if !ok {
|
||||
return ticker.TickerPrice{}, err
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tickerPrice.Ask = result.Ask
|
||||
tickerPrice.Bid = result.Bid
|
||||
tickerPrice.Volume = result.Volume
|
||||
tickerPrice.High = result.High
|
||||
tickerPrice.Low = result.Low
|
||||
tickerPrice.Last = result.Last
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = currency[3:]
|
||||
ticker.ProcessTicker(l.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = l.GetName()
|
||||
accountInfo, err := l.GetAccountInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
for x, y := range accountInfo.Balance {
|
||||
for z, w := range accountInfo.Locked {
|
||||
if z == x {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||
exchangeCurrency.TotalValue, _ = strconv.ParseFloat(y, 64)
|
||||
exchangeCurrency.Hold, _ = strconv.ParseFloat(w, 64)
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
328
exchanges/lakebtc/lakebtchttp.go
Normal file
328
exchanges/lakebtc/lakebtchttp.go
Normal file
@@ -0,0 +1,328 @@
|
||||
package lakebtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
LAKEBTC_API_URL = "https://api.lakebtc.com/api_v2"
|
||||
LAKEBTC_API_VERSION = "2"
|
||||
LAKEBTC_TICKER = "ticker"
|
||||
LAKEBTC_ORDERBOOK = "bcorderbook"
|
||||
LAKEBTC_TRADES = "bctrades"
|
||||
LAKEBTC_GET_ACCOUNT_INFO = "getAccountInfo"
|
||||
LAKEBTC_BUY_ORDER = "buyOrder"
|
||||
LAKEBTC_SELL_ORDER = "sellOrder"
|
||||
LAKEBTC_OPEN_ORDERS = "openOrders"
|
||||
LAKEBTC_GET_ORDERS = "getOrders"
|
||||
LAKEBTC_CANCEL_ORDER = "cancelOrder"
|
||||
LAKEBTC_GET_TRADES = "getTrades"
|
||||
LAKEBTC_GET_EXTERNAL_ACCOUNTS = "getExternalAccounts"
|
||||
LAKEBTC_CREATE_WITHDRAW = "createWithdraw"
|
||||
)
|
||||
|
||||
type LakeBTC struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (l *LakeBTC) SetDefaults() {
|
||||
l.Name = "LakeBTC"
|
||||
l.Enabled = false
|
||||
l.TakerFee = 0.2
|
||||
l.MakerFee = 0.15
|
||||
l.Verbose = false
|
||||
l.Websocket = false
|
||||
l.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (l *LakeBTC) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
l.SetEnabled(false)
|
||||
} else {
|
||||
l.Enabled = true
|
||||
l.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
l.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
l.RESTPollingDelay = exch.RESTPollingDelay
|
||||
l.Verbose = exch.Verbose
|
||||
l.Websocket = exch.Websocket
|
||||
l.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
l.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
l.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetFee(maker bool) float64 {
|
||||
if maker {
|
||||
return l.MakerFee
|
||||
} else {
|
||||
return l.TakerFee
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetTicker() (map[string]LakeBTCTicker, error) {
|
||||
response := make(map[string]LakeBTCTickerResponse)
|
||||
path := fmt.Sprintf("%s/%s", LAKEBTC_API_URL, LAKEBTC_TICKER)
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]LakeBTCTicker)
|
||||
|
||||
var addresses []string
|
||||
for k, v := range response {
|
||||
var ticker LakeBTCTicker
|
||||
key := common.StringToUpper(k)
|
||||
if v.Ask != nil {
|
||||
ticker.Ask, _ = strconv.ParseFloat(v.Ask.(string), 64)
|
||||
}
|
||||
if v.Bid != nil {
|
||||
ticker.Bid, _ = strconv.ParseFloat(v.Bid.(string), 64)
|
||||
}
|
||||
if v.High != nil {
|
||||
ticker.High, _ = strconv.ParseFloat(v.High.(string), 64)
|
||||
}
|
||||
if v.Last != nil {
|
||||
ticker.Last, _ = strconv.ParseFloat(v.Last.(string), 64)
|
||||
}
|
||||
if v.Low != nil {
|
||||
ticker.Low, _ = strconv.ParseFloat(v.Low.(string), 64)
|
||||
}
|
||||
if v.Volume != nil {
|
||||
ticker.Volume, _ = strconv.ParseFloat(v.Volume.(string), 64)
|
||||
}
|
||||
result[key] = ticker
|
||||
addresses = append(addresses, key)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetOrderBook(currency string) (LakeBTCOrderbook, error) {
|
||||
type Response struct {
|
||||
Bids [][]string `json:"bids"`
|
||||
Asks [][]string `json:"asks"`
|
||||
}
|
||||
path := fmt.Sprintf("%s/%s?symbol=%s", LAKEBTC_API_URL, LAKEBTC_ORDERBOOK, common.StringToLower(currency))
|
||||
resp := Response{}
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
if err != nil {
|
||||
return LakeBTCOrderbook{}, err
|
||||
}
|
||||
orderbook := LakeBTCOrderbook{}
|
||||
|
||||
for _, x := range resp.Bids {
|
||||
price, err := strconv.ParseFloat(x[0], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat(x[1], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Bids = append(orderbook.Bids, LakeBTCOrderbookStructure{price, amount})
|
||||
}
|
||||
|
||||
for _, x := range resp.Asks {
|
||||
price, err := strconv.ParseFloat(x[0], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat(x[1], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Asks = append(orderbook.Asks, LakeBTCOrderbookStructure{price, amount})
|
||||
}
|
||||
return orderbook, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetTradeHistory(currency string) ([]LakeBTCTradeHistory, error) {
|
||||
path := fmt.Sprintf("%s/%s?symbol=%s", LAKEBTC_API_URL, LAKEBTC_TRADES, common.StringToLower(currency))
|
||||
resp := []LakeBTCTradeHistory{}
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetAccountInfo() (LakeBTCAccountInfo, error) {
|
||||
resp := LakeBTCAccountInfo{}
|
||||
err := l.SendAuthenticatedHTTPRequest(LAKEBTC_GET_ACCOUNT_INFO, "", &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) Trade(orderType int, amount, price float64, currency string) (LakeBTCTrade, error) {
|
||||
resp := LakeBTCTrade{}
|
||||
params := strconv.FormatFloat(price, 'f', -1, 64) + "," + strconv.FormatFloat(amount, 'f', -1, 64) + "," + currency
|
||||
err := errors.New("")
|
||||
|
||||
if orderType == 1 {
|
||||
err = l.SendAuthenticatedHTTPRequest(LAKEBTC_BUY_ORDER, params, &resp)
|
||||
} else {
|
||||
err = l.SendAuthenticatedHTTPRequest(LAKEBTC_SELL_ORDER, params, &resp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.Result != "order received" {
|
||||
return resp, fmt.Errorf("Unexpected result: %s", resp.Result)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetOpenOrders() ([]LakeBTCOpenOrders, error) {
|
||||
orders := []LakeBTCOpenOrders{}
|
||||
err := l.SendAuthenticatedHTTPRequest(LAKEBTC_OPEN_ORDERS, "", &orders)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetOrders(orders []int64) ([]LakeBTCOrders, error) {
|
||||
var ordersStr []string
|
||||
for _, x := range orders {
|
||||
ordersStr = append(ordersStr, strconv.FormatInt(x, 10))
|
||||
}
|
||||
|
||||
resp := []LakeBTCOrders{}
|
||||
err := l.SendAuthenticatedHTTPRequest(LAKEBTC_GET_ORDERS, common.JoinStrings(ordersStr, ","), &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) CancelOrder(orderID int64) error {
|
||||
type Response struct {
|
||||
Result bool `json:"Result"`
|
||||
}
|
||||
|
||||
resp := Response{}
|
||||
params := strconv.FormatInt(orderID, 10)
|
||||
err := l.SendAuthenticatedHTTPRequest(LAKEBTC_CANCEL_ORDER, params, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.Result != true {
|
||||
return errors.New("Unable to cancel order.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetTrades(timestamp int64) ([]LakeBTCAuthenticaltedTradeHistory, error) {
|
||||
params := ""
|
||||
if timestamp != 0 {
|
||||
params = strconv.FormatInt(timestamp, 10)
|
||||
}
|
||||
|
||||
trades := []LakeBTCAuthenticaltedTradeHistory{}
|
||||
err := l.SendAuthenticatedHTTPRequest(LAKEBTC_GET_TRADES, params, &trades)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
/* Only for BTC */
|
||||
func (l *LakeBTC) GetExternalAccounts() ([]LakeBTCExternalAccounts, error) {
|
||||
resp := []LakeBTCExternalAccounts{}
|
||||
err := l.SendAuthenticatedHTTPRequest(LAKEBTC_GET_EXTERNAL_ACCOUNTS, "", &resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
/* Only for BTC */
|
||||
func (l *LakeBTC) CreateWithdraw(amount float64, accountID int64) (LakeBTCWithdraw, error) {
|
||||
resp := LakeBTCWithdraw{}
|
||||
params := strconv.FormatFloat(amount, 'f', -1, 64) + ",btc," + strconv.FormatInt(accountID, 10)
|
||||
err := l.SendAuthenticatedHTTPRequest(LAKEBTC_CREATE_WITHDRAW, params, &resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s¶ms=%s", nonce, l.APIKey, method, params)
|
||||
hmac := common.GetHMAC(common.HASH_SHA1, []byte(req), []byte(l.APISecret))
|
||||
|
||||
if l.Verbose {
|
||||
log.Printf("Sending POST request to %s calling method %s with params %s\n", LAKEBTC_API_URL, method, req)
|
||||
}
|
||||
|
||||
postData := make(map[string]interface{})
|
||||
postData["method"] = method
|
||||
postData["id"] = 1
|
||||
postData["params"] = common.SplitStrings(params, ",")
|
||||
|
||||
data, err := common.JSONEncode(postData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Json-Rpc-Tonce"] = nonce
|
||||
headers["Authorization"] = "Basic " + common.Base64Encode([]byte(l.APIKey+":"+common.HexEncodeToString(hmac)))
|
||||
headers["Content-Type"] = "application/json-rpc"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", LAKEBTC_API_URL, headers, strings.NewReader(string(data)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if l.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
errResponse := ErrorResponse{}
|
||||
err = common.JSONDecode([]byte(resp), &errResponse)
|
||||
if err != nil {
|
||||
return errors.New("Unable to check response for error.")
|
||||
}
|
||||
|
||||
if errResponse.Error != "" {
|
||||
return errors.New(errResponse.Error)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
105
exchanges/liqui/liqui_types.go
Normal file
105
exchanges/liqui/liqui_types.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package liqui
|
||||
|
||||
type LiquiTicker struct {
|
||||
High float64
|
||||
Low float64
|
||||
Avg float64
|
||||
Vol float64
|
||||
Vol_cur float64
|
||||
Last float64
|
||||
Buy float64
|
||||
Sell float64
|
||||
Updated int64
|
||||
}
|
||||
|
||||
type LiquiOrderbook struct {
|
||||
Asks [][]float64 `json:"asks"`
|
||||
Bids [][]float64 `json:"bids"`
|
||||
}
|
||||
|
||||
type LiquiTrades struct {
|
||||
Type string `json:"type"`
|
||||
Price float64 `json:"bid"`
|
||||
Amount float64 `json:"amount"`
|
||||
TID int64 `json:"tid"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type LiquiResponse struct {
|
||||
Return interface{} `json:"return"`
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type LiquiPair struct {
|
||||
DecimalPlaces int `json:"decimal_places"`
|
||||
MinPrice float64 `json:"min_price"`
|
||||
MaxPrice float64 `json:"max_price"`
|
||||
MinAmount float64 `json:"min_amount"`
|
||||
Hidden int `json:"hidden"`
|
||||
Fee float64 `json:"fee"`
|
||||
}
|
||||
|
||||
type LiquiInfo struct {
|
||||
ServerTime int64 `json:"server_time"`
|
||||
Pairs map[string]LiquiPair `json:"pairs"`
|
||||
}
|
||||
|
||||
type LiquiAccountInfo struct {
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
Rights struct {
|
||||
Info bool `json:"info"`
|
||||
Trade bool `json:"trade"`
|
||||
Withdraw bool `json:"withdraw"`
|
||||
} `json:"rights"`
|
||||
ServerTime float64 `json:"server_time"`
|
||||
TransactionCount int `json:"transaction_count"`
|
||||
OpenOrders int `json:"open_orders"`
|
||||
}
|
||||
|
||||
type LiquiTrade struct {
|
||||
Received float64 `json:"received"`
|
||||
Remains float64 `json:"remains"`
|
||||
OrderID float64 `json:"order_id"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type LiquiActiveOrders struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
TimestampCreated float64 `json:"time_created"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type LiquiOrderInfo struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
StartAmount float64 `json:"start_amount"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
TimestampCreated float64 `json:"time_created"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type LiquiCancelOrder struct {
|
||||
OrderID float64 `json:"order_id"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type LiquiTradeHistory struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"type"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
OrderID float64 `json:"order_id"`
|
||||
MyOrder int `json:"is_your_order"`
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type LiquiWithdrawCoins struct {
|
||||
TID int64 `json:"tId"`
|
||||
AmountSent float64 `json:"amountSent"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
109
exchanges/liqui/liqui_wrapper.go
Normal file
109
exchanges/liqui/liqui_wrapper.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package liqui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (l *Liqui) Start() {
|
||||
go l.Run()
|
||||
}
|
||||
|
||||
func (l *Liqui) Run() {
|
||||
if l.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", l.GetName(), l.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", l.GetName(), len(l.EnabledPairs), l.EnabledPairs)
|
||||
}
|
||||
|
||||
var err error
|
||||
l.Info, err = l.GetInfo()
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to fetch info.\n", l.GetName())
|
||||
} else {
|
||||
exchangeProducts := l.GetAvailablePairs(true)
|
||||
log.Println(exchangeProducts)
|
||||
/*
|
||||
diff := common.StringSliceDifference(l.AvailablePairs, exchangeProducts)
|
||||
if len(diff) > 0 {
|
||||
exch, err := bot.config.GetExchangeConfig(l.Name)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Printf("%s Updating available pairs. Difference: %s.\n", l.Name, diff)
|
||||
exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",")
|
||||
bot.config.UpdateExchangeConfig(exch)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
pairs := []string{}
|
||||
for _, x := range l.EnabledPairs {
|
||||
currencies := common.SplitStrings(x, "_")
|
||||
x = common.StringToLower(currencies[0]) + "_" + common.StringToLower(currencies[1])
|
||||
pairs = append(pairs, x)
|
||||
}
|
||||
pairsString := common.JoinStrings(pairs, "-")
|
||||
|
||||
for l.Enabled {
|
||||
go func() {
|
||||
ticker, err := l.GetTicker(pairsString)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
for x, y := range ticker {
|
||||
//currencies := common.SplitStrings(x, "_")
|
||||
x = common.StringToUpper(x)
|
||||
log.Printf("Liqui %s: Last %f High %f Low %f Volume %f\n", x, y.Last, y.High, y.Low, y.Vol_cur)
|
||||
l.Ticker[x] = y
|
||||
//AddExchangeInfo(l.GetName(), common.StringToUpper(currencies[0]), common.StringToUpper(currencies[1]), y.Last, y.Vol_cur)
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Second * l.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Liqui) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, ok := l.Ticker[currency]
|
||||
if !ok {
|
||||
return tickerPrice, errors.New("Unable to get currency.")
|
||||
}
|
||||
tickerPrice.Ask = tick.Buy
|
||||
tickerPrice.Bid = tick.Sell
|
||||
currencies := common.SplitStrings(currency, "_")
|
||||
tickerPrice.FirstCurrency = currencies[0]
|
||||
tickerPrice.SecondCurrency = currencies[1]
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol_cur
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(l.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Liqui exchange
|
||||
func (e *Liqui) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetAccountInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
for x, y := range accountBalance.Funds {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||
exchangeCurrency.TotalValue = y
|
||||
exchangeCurrency.Hold = 0
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
297
exchanges/liqui/liquihttp.go
Normal file
297
exchanges/liqui/liquihttp.go
Normal file
@@ -0,0 +1,297 @@
|
||||
package liqui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
LIQUI_API_PUBLIC_URL = "https://api.Liqui.io/api"
|
||||
LIQUI_API_PRIVATE_URL = "https://api.Liqui.io/tapi"
|
||||
LIQUI_API_PUBLIC_VERSION = "3"
|
||||
LIQUI_API_PRIVATE_VERSION = "1"
|
||||
LIQUI_INFO = "info"
|
||||
LIQUI_TICKER = "ticker"
|
||||
LIQUI_DEPTH = "depth"
|
||||
LIQUI_TRADES = "trades"
|
||||
LIQUI_ACCOUNT_INFO = "getInfo"
|
||||
LIQUI_TRADE = "Trade"
|
||||
LIQUI_ACTIVE_ORDERS = "ActiveOrders"
|
||||
LIQUI_ORDER_INFO = "OrderInfo"
|
||||
LIQUI_CANCEL_ORDER = "CancelOrder"
|
||||
LIQUI_TRADE_HISTORY = "TradeHistory"
|
||||
LIQUI_WITHDRAW_COIN = "WithdrawCoin"
|
||||
)
|
||||
|
||||
type Liqui struct {
|
||||
exchange.ExchangeBase
|
||||
Ticker map[string]LiquiTicker
|
||||
Info LiquiInfo
|
||||
}
|
||||
|
||||
func (l *Liqui) SetDefaults() {
|
||||
l.Name = "Liqui"
|
||||
l.Enabled = false
|
||||
l.Fee = 0.25
|
||||
l.Verbose = false
|
||||
l.Websocket = false
|
||||
l.RESTPollingDelay = 10
|
||||
l.Ticker = make(map[string]LiquiTicker)
|
||||
}
|
||||
|
||||
func (l *Liqui) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
l.SetEnabled(false)
|
||||
} else {
|
||||
l.Enabled = true
|
||||
l.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
l.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
l.RESTPollingDelay = exch.RESTPollingDelay
|
||||
l.Verbose = exch.Verbose
|
||||
l.Websocket = exch.Websocket
|
||||
l.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
l.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
l.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Liqui) GetFee(currency string) (float64, error) {
|
||||
val, ok := l.Info.Pairs[common.StringToLower(currency)]
|
||||
if !ok {
|
||||
return 0, errors.New("Currency does not exist")
|
||||
}
|
||||
|
||||
return val.Fee, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetAvailablePairs(nonHidden bool) []string {
|
||||
var pairs []string
|
||||
for x, y := range l.Info.Pairs {
|
||||
if nonHidden && y.Hidden == 1 {
|
||||
continue
|
||||
}
|
||||
pairs = append(pairs, common.StringToUpper(x))
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
func (l *Liqui) GetInfo() (LiquiInfo, error) {
|
||||
req := fmt.Sprintf("%s/%s/%s/", LIQUI_API_PUBLIC_URL, LIQUI_API_PUBLIC_VERSION, LIQUI_INFO)
|
||||
resp := LiquiInfo{}
|
||||
err := common.SendHTTPGetRequest(req, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetTicker(symbol string) (map[string]LiquiTicker, error) {
|
||||
type Response struct {
|
||||
Data map[string]LiquiTicker
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", LIQUI_API_PUBLIC_URL, LIQUI_API_PUBLIC_VERSION, LIQUI_TICKER, symbol)
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Data, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetDepth(symbol string) (LiquiOrderbook, error) {
|
||||
type Response struct {
|
||||
Data map[string]LiquiOrderbook
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", LIQUI_API_PUBLIC_URL, LIQUI_API_PUBLIC_VERSION, LIQUI_DEPTH, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
if err != nil {
|
||||
return LiquiOrderbook{}, err
|
||||
}
|
||||
|
||||
depth := response.Data[symbol]
|
||||
return depth, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetTrades(symbol string) ([]LiquiTrades, error) {
|
||||
type Response struct {
|
||||
Data map[string][]LiquiTrades
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", LIQUI_API_PUBLIC_URL, LIQUI_API_PUBLIC_VERSION, LIQUI_TRADES, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
if err != nil {
|
||||
return []LiquiTrades{}, err
|
||||
}
|
||||
|
||||
trades := response.Data[symbol]
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetAccountInfo() (LiquiAccountInfo, error) {
|
||||
var result LiquiAccountInfo
|
||||
err := l.SendAuthenticatedHTTPRequest(LIQUI_ACCOUNT_INFO, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
//to-do: convert orderid to int64
|
||||
func (l *Liqui) Trade(pair, orderType string, amount, price float64) (float64, error) {
|
||||
req := url.Values{}
|
||||
req.Add("pair", pair)
|
||||
req.Add("type", orderType)
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("rate", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
|
||||
var result LiquiTrade
|
||||
err := l.SendAuthenticatedHTTPRequest(LIQUI_TRADE, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result.OrderID, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetActiveOrders(pair string) (map[string]LiquiActiveOrders, error) {
|
||||
req := url.Values{}
|
||||
req.Add("pair", pair)
|
||||
|
||||
var result map[string]LiquiActiveOrders
|
||||
err := l.SendAuthenticatedHTTPRequest(LIQUI_ACTIVE_ORDERS, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetOrderInfo(OrderID int64) (map[string]LiquiOrderInfo, error) {
|
||||
req := url.Values{}
|
||||
req.Add("order_id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
var result map[string]LiquiOrderInfo
|
||||
err := l.SendAuthenticatedHTTPRequest(LIQUI_ORDER_INFO, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) CancelOrder(OrderID int64) (bool, error) {
|
||||
req := url.Values{}
|
||||
req.Add("order_id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
var result LiquiCancelOrder
|
||||
err := l.SendAuthenticatedHTTPRequest(LIQUI_CANCEL_ORDER, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) GetTradeHistory(vals url.Values, pair string) (map[string]LiquiTradeHistory, error) {
|
||||
if pair != "" {
|
||||
vals.Add("pair", pair)
|
||||
}
|
||||
|
||||
var result map[string]LiquiTradeHistory
|
||||
err := l.SendAuthenticatedHTTPRequest(LIQUI_TRADE_HISTORY, vals, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// API mentions that this isn't active now, but will be soon - you must provide the first 8 characters of the key
|
||||
// in your ticket to support.
|
||||
func (l *Liqui) WithdrawCoins(coin string, amount float64, address string) (LiquiWithdrawCoins, error) {
|
||||
req := url.Values{}
|
||||
req.Add("coinName", coin)
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("address", address)
|
||||
|
||||
var result LiquiWithdrawCoins
|
||||
err := l.SendAuthenticatedHTTPRequest(LIQUI_WITHDRAW_COIN, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *Liqui) SendAuthenticatedHTTPRequest(method string, values url.Values, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
values.Set("nonce", nonce)
|
||||
values.Set("method", method)
|
||||
|
||||
encoded := values.Encode()
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(encoded), []byte(l.APISecret))
|
||||
|
||||
if l.Verbose {
|
||||
log.Printf("Sending POST request to %s calling method %s with params %s\n", LIQUI_API_PRIVATE_URL, method, encoded)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Key"] = l.APIKey
|
||||
headers["Sign"] = common.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", LIQUI_API_PRIVATE_URL, headers, strings.NewReader(encoded))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response := LiquiResponse{}
|
||||
err = common.JSONDecode([]byte(resp), &response)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Success != 1 {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
|
||||
jsonEncoded, err := common.JSONEncode(response.Return)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = common.JSONDecode(jsonEncoded, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
84
exchanges/localbitcoins/localbitcoins_types.go
Normal file
84
exchanges/localbitcoins/localbitcoins_types.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package localbitcoins
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type LocalBitcoinsTicker struct {
|
||||
Avg12h float64 `json:"avg_12h,string"`
|
||||
Avg1h float64 `json:"avg_1h,string"`
|
||||
Avg24h float64 `json:"avg_24h,string"`
|
||||
Rates struct {
|
||||
Last float64 `json:"last,string"`
|
||||
} `json:"rates"`
|
||||
VolumeBTC float64 `json:"volume_btc,string"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsTrade struct {
|
||||
TID int64 `json:"tid"`
|
||||
Date int64 `json:"date"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsOrderbookStructure struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type LocalBitcoinsOrderbook struct {
|
||||
Bids []LocalBitcoinsOrderbookStructure `json:"bids"`
|
||||
Asks []LocalBitcoinsOrderbookStructure `json:"asks"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsAccountInfo struct {
|
||||
Username string `json:"username"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
AgeText string `json:"age_text"`
|
||||
TradingPartners int `json:"trading_partners_count"`
|
||||
FeedbacksUnconfirmed int `json:"feedbacks_unconfirmed_count"`
|
||||
TradeVolumeText string `json:"trade_volume_text"`
|
||||
HasCommonTrades bool `json:"has_common_trades"`
|
||||
HasFeedback bool `json:"has_feedback"`
|
||||
ConfirmedTradesText string `json:"confirmed_trade_count_text"`
|
||||
BlockedCount int `json:"blocked_count"`
|
||||
FeedbackScore int `json:"feedback_score"`
|
||||
FeedbackCount int `json:"feedback_count"`
|
||||
URL string `json:"url"`
|
||||
TrustedCount int `json:"trusted_count"`
|
||||
IdentityVerifiedAt time.Time `json:"identify_verified_at"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsBalance struct {
|
||||
Balance float64 `json:"balance,string"`
|
||||
Sendable float64 `json:"Sendable,string"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsWalletTransaction struct {
|
||||
TXID string `json:"txid"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Description string `json:"description"`
|
||||
TXType int `json:"tx_type"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsWalletAddressList struct {
|
||||
Address string `json:"address"`
|
||||
Received float64 `json:"received,string"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsWalletInfo struct {
|
||||
Message string `json:"message"`
|
||||
Total LocalBitcoinsBalance `json:"total"`
|
||||
SentTransactions30d []LocalBitcoinsWalletTransaction `json:"sent_transactions_30d"`
|
||||
ReceivedTransactions30d []LocalBitcoinsWalletTransaction `json:"received_transactions_30d"`
|
||||
ReceivingAddressCount int `json:"receiving_address_count"`
|
||||
ReceivingAddressList []LocalBitcoinsWalletAddressList `json:"receiving_address_list"`
|
||||
}
|
||||
|
||||
type LocalBitcoinsWalletBalanceInfo struct {
|
||||
Message string `json:"message"`
|
||||
Total LocalBitcoinsBalance `json:"total"`
|
||||
ReceivingAddressCount int `json:"receiving_address_count"` // always 1
|
||||
ReceivingAddressList []LocalBitcoinsWalletAddressList `json:"receiving_address_list"`
|
||||
}
|
||||
74
exchanges/localbitcoins/localbitcoins_wrapper.go
Normal file
74
exchanges/localbitcoins/localbitcoins_wrapper.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package localbitcoins
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (l *LocalBitcoins) Start() {
|
||||
go l.Run()
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) Run() {
|
||||
if l.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", l.GetName(), l.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", l.GetName(), len(l.EnabledPairs), l.EnabledPairs)
|
||||
}
|
||||
|
||||
for l.Enabled {
|
||||
for _, x := range l.EnabledPairs {
|
||||
currency := x[3:]
|
||||
ticker, err := l.GetTickerPrice("BTC" + currency)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("LocalBitcoins BTC %s: Last %f Volume %f\n", currency, ticker.Last, ticker.Volume)
|
||||
//AddExchangeInfo(l.GetName(), x[0:3], x[3:], ticker.Last, ticker.Volume)
|
||||
}
|
||||
time.Sleep(time.Second * l.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(l.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
tick, err := l.GetTicker()
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
for key, value := range tick {
|
||||
tickerPrice.Last = value.Rates.Last
|
||||
tickerPrice.FirstCurrency = currency[0:3]
|
||||
tickerPrice.SecondCurrency = key
|
||||
tickerPrice.Volume = value.VolumeBTC
|
||||
ticker.ProcessTicker(l.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
}
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the LocalBitcoins exchange
|
||||
func (e *LocalBitcoins) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetWalletBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = "BTC"
|
||||
exchangeCurrency.TotalValue = accountBalance.Total.Balance
|
||||
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
return response, nil
|
||||
}
|
||||
299
exchanges/localbitcoins/localbitcoinshttp.go
Normal file
299
exchanges/localbitcoins/localbitcoinshttp.go
Normal file
@@ -0,0 +1,299 @@
|
||||
package localbitcoins
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
LOCALBITCOINS_API_URL = "https://localbitcoins.com"
|
||||
LOCALBITCOINS_API_TICKER = "/bitcoinaverage/ticker-all-currencies/"
|
||||
LOCALBITCOINS_API_BITCOINCHARTS = "/bitcoincharts/"
|
||||
LOCALBITCOINS_API_PINCODE = "pincode/"
|
||||
LOCALBITCOINS_API_WALLET = "wallet/"
|
||||
LOCALBITCOINS_API_MYSELF = "myself/"
|
||||
LOCALBITCOINS_API_WALLET_BALANCE = "wallet-balance/"
|
||||
LOCALBITCOINS_API_WALLET_SEND = "wallet-send/"
|
||||
LOCALBITCOINS_API_WALLET_SEND_PIN = "wallet-send-pin/"
|
||||
LOCALBITCOINS_API_WALLET_ADDRESS = "wallet-addr/"
|
||||
)
|
||||
|
||||
type LocalBitcoins struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) SetDefaults() {
|
||||
l.Name = "LocalBitcoins"
|
||||
l.Enabled = false
|
||||
l.Verbose = false
|
||||
l.Verbose = false
|
||||
l.Websocket = false
|
||||
l.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
l.SetEnabled(false)
|
||||
} else {
|
||||
l.Enabled = true
|
||||
l.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
l.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
l.RESTPollingDelay = exch.RESTPollingDelay
|
||||
l.Verbose = exch.Verbose
|
||||
l.Websocket = exch.Websocket
|
||||
l.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
l.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
l.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetFee(maker bool) float64 {
|
||||
if maker {
|
||||
return l.MakerFee
|
||||
} else {
|
||||
return l.TakerFee
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetTicker() (map[string]LocalBitcoinsTicker, error) {
|
||||
result := make(map[string]LocalBitcoinsTicker)
|
||||
err := common.SendHTTPGetRequest(LOCALBITCOINS_API_URL+LOCALBITCOINS_API_TICKER, true, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetTrades(currency string, values url.Values) ([]LocalBitcoinsTrade, error) {
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/%s/trades.json", LOCALBITCOINS_API_URL+LOCALBITCOINS_API_BITCOINCHARTS, currency), values)
|
||||
result := []LocalBitcoinsTrade{}
|
||||
err := common.SendHTTPGetRequest(path, true, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetOrderbook(currency string) (LocalBitcoinsOrderbook, error) {
|
||||
type response struct {
|
||||
Bids [][]string `json:"bids"`
|
||||
Asks [][]string `json:"asks"`
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s/orderbook.json", LOCALBITCOINS_API_URL+LOCALBITCOINS_API_BITCOINCHARTS, currency)
|
||||
resp := response{}
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return LocalBitcoinsOrderbook{}, err
|
||||
}
|
||||
|
||||
orderbook := LocalBitcoinsOrderbook{}
|
||||
|
||||
for _, x := range resp.Bids {
|
||||
price, err := strconv.ParseFloat(x[0], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat(x[1], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Bids = append(orderbook.Bids, LocalBitcoinsOrderbookStructure{price, amount})
|
||||
}
|
||||
|
||||
for _, x := range resp.Asks {
|
||||
price, err := strconv.ParseFloat(x[0], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
amount, err := strconv.ParseFloat(x[1], 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Asks = append(orderbook.Asks, LocalBitcoinsOrderbookStructure{price, amount})
|
||||
}
|
||||
|
||||
return orderbook, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetAccountInfo(username string, self bool) (LocalBitcoinsAccountInfo, error) {
|
||||
type response struct {
|
||||
Data LocalBitcoinsAccountInfo `json:"data"`
|
||||
}
|
||||
resp := response{}
|
||||
|
||||
if self {
|
||||
err := l.SendAuthenticatedHTTPRequest("GET", LOCALBITCOINS_API_MYSELF, nil, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp.Data, err
|
||||
}
|
||||
} else {
|
||||
path := fmt.Sprintf("%s/api/account_info/%s/", LOCALBITCOINS_API_URL, username)
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp.Data, err
|
||||
}
|
||||
}
|
||||
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) CheckPincode(pin int) (bool, error) {
|
||||
type response struct {
|
||||
Data struct {
|
||||
PinOK bool `json:"pincode_ok"`
|
||||
} `json:"data"`
|
||||
}
|
||||
resp := response{}
|
||||
values := url.Values{}
|
||||
values.Set("pincode", strconv.Itoa(pin))
|
||||
err := l.SendAuthenticatedHTTPRequest("POST", LOCALBITCOINS_API_PINCODE, values, &resp)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !resp.Data.PinOK {
|
||||
return false, errors.New("Pin invalid.")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetWalletInfo() (LocalBitcoinsWalletInfo, error) {
|
||||
type response struct {
|
||||
Data LocalBitcoinsWalletInfo `json:"data"`
|
||||
}
|
||||
resp := response{}
|
||||
err := l.SendAuthenticatedHTTPRequest("GET", LOCALBITCOINS_API_WALLET, nil, &resp)
|
||||
|
||||
if err != nil {
|
||||
return LocalBitcoinsWalletInfo{}, err
|
||||
}
|
||||
|
||||
if resp.Data.Message != "OK" {
|
||||
return LocalBitcoinsWalletInfo{}, errors.New("Unable to fetch wallet info.")
|
||||
}
|
||||
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetWalletBalance() (LocalBitcoinsWalletBalanceInfo, error) {
|
||||
type response struct {
|
||||
Data LocalBitcoinsWalletBalanceInfo `json:"data"`
|
||||
}
|
||||
resp := response{}
|
||||
err := l.SendAuthenticatedHTTPRequest("GET", LOCALBITCOINS_API_WALLET_BALANCE, nil, &resp)
|
||||
|
||||
if err != nil {
|
||||
return LocalBitcoinsWalletBalanceInfo{}, err
|
||||
}
|
||||
|
||||
if resp.Data.Message != "OK" {
|
||||
return LocalBitcoinsWalletBalanceInfo{}, errors.New("Unable to fetch wallet balance.")
|
||||
}
|
||||
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int) (bool, error) {
|
||||
values := url.Values{}
|
||||
values.Set("address", address)
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
path := LOCALBITCOINS_API_WALLET_SEND
|
||||
|
||||
if pin > 0 {
|
||||
values.Set("pincode", strconv.Itoa(pin))
|
||||
path = LOCALBITCOINS_API_WALLET_SEND_PIN
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Data struct {
|
||||
Message string `json:"message"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
err := l.SendAuthenticatedHTTPRequest("POST", path, values, &resp)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if resp.Data.Message != "Money is being sent" {
|
||||
return false, errors.New("Unable to send Bitcoins.")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetWalletAddress() (string, error) {
|
||||
type response struct {
|
||||
Data struct {
|
||||
Message string `json:"message"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
}
|
||||
resp := response{}
|
||||
err := l.SendAuthenticatedHTTPRequest("POST", LOCALBITCOINS_API_WALLET_ADDRESS, nil, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.Data.Message != "OK!" {
|
||||
return "", errors.New("Unable to fetch wallet address.")
|
||||
}
|
||||
|
||||
return resp.Data.Address, nil
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, values url.Values, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
payload := ""
|
||||
path = "/api/" + path
|
||||
|
||||
if len(values) > 0 {
|
||||
payload = values.Encode()
|
||||
}
|
||||
|
||||
message := string(nonce) + l.APIKey + path + payload
|
||||
hmac := common.GetHMAC(common.HASH_SHA256, []byte(message), []byte(l.APISecret))
|
||||
headers := make(map[string]string)
|
||||
headers["Apiauth-Key"] = l.APIKey
|
||||
headers["Apiauth-Nonce"] = string(nonce)
|
||||
headers["Apiauth-Signature"] = common.StringToUpper(common.HexEncodeToString(hmac))
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, LOCALBITCOINS_API_URL+path, headers, bytes.NewBuffer([]byte(payload)))
|
||||
|
||||
if l.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
|
||||
}
|
||||
391
exchanges/okcoin/okcoin_types.go
Normal file
391
exchanges/okcoin/okcoin_types.go
Normal 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"`
|
||||
}
|
||||
102
exchanges/okcoin/okcoin_wrapper.go
Normal file
102
exchanges/okcoin/okcoin_wrapper.go
Normal 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
|
||||
}
|
||||
988
exchanges/okcoin/okcoinhttp.go
Normal file
988
exchanges/okcoin/okcoinhttp.go
Normal 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",
|
||||
}
|
||||
}
|
||||
558
exchanges/okcoin/okcoinwebsocket.go
Normal file
558
exchanges/okcoin/okcoinwebsocket.go
Normal 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",
|
||||
}
|
||||
}
|
||||
214
exchanges/poloniex/poloniex_types.go
Normal file
214
exchanges/poloniex/poloniex_types.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package poloniex
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type PoloniexTicker struct {
|
||||
Last float64 `json:"last,string"`
|
||||
LowestAsk float64 `json:"lowestAsk,string"`
|
||||
HighestBid float64 `json:"highestBid,string"`
|
||||
PercentChange float64 `json:"percentChange,string"`
|
||||
BaseVolume float64 `json:"baseVolume,string"`
|
||||
QuoteVolume float64 `json:"quoteVolume,string"`
|
||||
IsFrozen int `json:"isFrozen,string"`
|
||||
High24Hr float64 `json:"high24hr,string"`
|
||||
Low24Hr float64 `json:"low24hr,string"`
|
||||
}
|
||||
|
||||
type PoloniexOrderbook struct {
|
||||
Asks [][]interface{} `json:"asks"`
|
||||
Bids [][]interface{} `json:"bids"`
|
||||
IsFrozen string `json:"isFrozen"`
|
||||
}
|
||||
|
||||
type PoloniexTradeHistory struct {
|
||||
GlobalTradeID int64 `json:"globalTradeID"`
|
||||
TradeID int64 `json:"tradeID"`
|
||||
Date string `json:"date"`
|
||||
Type string `json:"type"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
}
|
||||
|
||||
type PoloniexChartData struct {
|
||||
Date int `json:"date"`
|
||||
High float64 `json:"high"`
|
||||
Low float64 `json:"low"`
|
||||
Open float64 `json:"open"`
|
||||
Close float64 `json:"close"`
|
||||
Volume float64 `json:"volume"`
|
||||
QuoteVolume float64 `json:"quoteVolume"`
|
||||
WeightedAverage float64 `json:"weightedAverage"`
|
||||
}
|
||||
|
||||
type PoloniexCurrencies struct {
|
||||
Name string `json:"name"`
|
||||
MaxDailyWithdrawal string `json:"maxDailyWithdrawal"`
|
||||
TxFee float64 `json:"txFee,string"`
|
||||
MinConfirmations int `json:"minConf"`
|
||||
DepositAddresses interface{} `json:"depositAddress"`
|
||||
Disabled int `json:"disabled"`
|
||||
Delisted int `json:"delisted"`
|
||||
Frozen int `json:"frozen"`
|
||||
}
|
||||
|
||||
type PoloniexLoanOrder struct {
|
||||
Rate float64 `json:"rate,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
RangeMin int `json:"rangeMin"`
|
||||
RangeMax int `json:"rangeMax"`
|
||||
}
|
||||
|
||||
type PoloniexLoanOrders struct {
|
||||
Offers []PoloniexLoanOrder `json:"offers"`
|
||||
Demands []PoloniexLoanOrder `json:"demands"`
|
||||
}
|
||||
|
||||
type PoloniexBalance struct {
|
||||
Currency map[string]float64
|
||||
}
|
||||
|
||||
type PoloniexCompleteBalance struct {
|
||||
Available float64
|
||||
OnOrders float64
|
||||
BTCValue float64
|
||||
}
|
||||
|
||||
type PoloniexDepositAddresses struct {
|
||||
Addresses map[string]string
|
||||
}
|
||||
|
||||
type PoloniexDepositsWithdrawals struct {
|
||||
Deposits []struct {
|
||||
Currency string `json:"currency"`
|
||||
Address string `json:"address"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
TransactionID string `json:"txid"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Status string `json:"string"`
|
||||
} `json:"deposits"`
|
||||
Withdrawals []struct {
|
||||
WithdrawalNumber int64 `json:"withdrawalNumber"`
|
||||
Currency string `json:"currency"`
|
||||
Address string `json:"address"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
TransactionID string `json:"txid"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Status string `json:"string"`
|
||||
IPAddress string `json:"ipAddress"`
|
||||
} `json:"withdrawals"`
|
||||
}
|
||||
|
||||
type PoloniexOrder struct {
|
||||
OrderNumber int64 `json:"orderNumber,string"`
|
||||
Type string `json:"type"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
Date string `json:"date"`
|
||||
Margin float64 `json:"margin"`
|
||||
}
|
||||
|
||||
type PoloniexOpenOrdersResponseAll struct {
|
||||
Data map[string][]PoloniexOrder
|
||||
}
|
||||
|
||||
type PoloniexOpenOrdersResponse struct {
|
||||
Data []PoloniexOrder
|
||||
}
|
||||
|
||||
type PoloniexAuthentictedTradeHistory struct {
|
||||
GlobalTradeID int64 `json:"globalTradeID"`
|
||||
TradeID int64 `json:"tradeID,string"`
|
||||
Date string `json:"date"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
OrderNumber int64 `json:"orderNumber,string"`
|
||||
Type string `json:"type"`
|
||||
Category string `json:"category"`
|
||||
}
|
||||
|
||||
type PoloniexAuthenticatedTradeHistoryAll struct {
|
||||
Data map[string][]PoloniexAuthentictedTradeHistory
|
||||
}
|
||||
|
||||
type PoloniexAuthenticatedTradeHistoryResponse struct {
|
||||
Data []PoloniexAuthentictedTradeHistory
|
||||
}
|
||||
|
||||
type PoloniexResultingTrades struct {
|
||||
Amount float64 `json:"amount,string"`
|
||||
Date string `json:"date"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
TradeID int64 `json:"tradeID,string"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type PoloniexOrderResponse struct {
|
||||
OrderNumber int64 `json:"orderNumber,string"`
|
||||
Trades []PoloniexResultingTrades `json:"resultingTrades"`
|
||||
}
|
||||
|
||||
type PoloniexGenericResponse struct {
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type PoloniexMoveOrderResponse struct {
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
OrderNumber int64 `json:"orderNumber,string"`
|
||||
Trades map[string][]PoloniexResultingTrades `json:"resultingTrades"`
|
||||
}
|
||||
|
||||
type PoloniexWithdraw struct {
|
||||
Response string `json:"response"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type PoloniexFee struct {
|
||||
MakerFee float64 `json:"makerFee,string"`
|
||||
TakerFee float64 `json:"takerFee,string"`
|
||||
ThirtyDayVolume float64 `json:"thirtyDayVolume,string"`
|
||||
NextTier float64 `json:"nextTier,string"`
|
||||
}
|
||||
|
||||
type PoloniexMargin struct {
|
||||
TotalValue float64 `json:"totalValue,string"`
|
||||
ProfitLoss float64 `json:"pl,string"`
|
||||
LendingFees float64 `json:"lendingFees,string"`
|
||||
NetValue float64 `json:"netValue,string"`
|
||||
BorrowedValue float64 `json:"totalBorrowedValue,string"`
|
||||
CurrentMargin float64 `json:"currentMargin,string"`
|
||||
}
|
||||
|
||||
type PoloniexMarginPosition struct {
|
||||
Amount float64 `json:"amount,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
BasePrice float64 `json:"basePrice,string"`
|
||||
LiquidiationPrice float64 `json:"liquidiationPrice"`
|
||||
ProfitLoss float64 `json:"pl,string"`
|
||||
LendingFees float64 `json:"lendingFees,string"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type PoloniexLoanOffer struct {
|
||||
ID int64 `json:"id"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Duration int `json:"duration"`
|
||||
AutoRenew bool `json:"autoRenew,int"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
|
||||
type PoloniexActiveLoans struct {
|
||||
Provided []PoloniexLoanOffer `json:"provided"`
|
||||
Used []PoloniexLoanOffer `json:"used"`
|
||||
}
|
||||
86
exchanges/poloniex/poloniex_wrapper.go
Normal file
86
exchanges/poloniex/poloniex_wrapper.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package poloniex
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func (p *Poloniex) Start() {
|
||||
go p.Run()
|
||||
}
|
||||
|
||||
func (p *Poloniex) Run() {
|
||||
if p.Verbose {
|
||||
log.Printf("%s Websocket: %s (url: %s).\n", p.GetName(), common.IsEnabled(p.Websocket), POLONIEX_WEBSOCKET_ADDRESS)
|
||||
log.Printf("%s polling delay: %ds.\n", p.GetName(), p.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", p.GetName(), len(p.EnabledPairs), p.EnabledPairs)
|
||||
}
|
||||
|
||||
if p.Websocket {
|
||||
go p.WebsocketClient()
|
||||
}
|
||||
|
||||
for p.Enabled {
|
||||
for _, x := range p.EnabledPairs {
|
||||
currency := x
|
||||
go func() {
|
||||
ticker, err := p.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("Poloniex %s Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
//currencyPair := common.SplitStrings(currency, "_")
|
||||
//AddExchangeInfo(p.GetName(), currencyPair[0], currencyPair[1], ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * p.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetTickerPrice(currency string) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(p.GetName(), currency[0:3], currency[3:])
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := p.GetTicker()
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
currencyPair := common.SplitStrings(currency, "_")
|
||||
tickerPrice.FirstCurrency = currencyPair[0]
|
||||
tickerPrice.SecondCurrency = currencyPair[1]
|
||||
tickerPrice.Ask = tick[currency].Last
|
||||
tickerPrice.Bid = tick[currency].HighestBid
|
||||
tickerPrice.High = tick[currency].HighestBid
|
||||
tickerPrice.Last = tick[currency].Last
|
||||
tickerPrice.Low = tick[currency].LowestAsk
|
||||
tickerPrice.Volume = tick[currency].BaseVolume
|
||||
ticker.ProcessTicker(p.GetName(), tickerPrice.FirstCurrency, tickerPrice.SecondCurrency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Poloniex exchange
|
||||
func (e *Poloniex) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetBalances()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
currencies := e.AvailablePairs
|
||||
for i := 0; i < len(currencies); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = currencies[i]
|
||||
exchangeCurrency.TotalValue = accountBalance.Currency[currencies[i]]
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
743
exchanges/poloniex/poloniexhttp.go
Normal file
743
exchanges/poloniex/poloniexhttp.go
Normal file
@@ -0,0 +1,743 @@
|
||||
package poloniex
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
const (
|
||||
POLONIEX_API_URL = "https://poloniex.com"
|
||||
POLONIEX_API_TRADING_ENDPOINT = "tradingApi"
|
||||
POLONIEX_API_VERSION = "1"
|
||||
POLONIEX_BALANCES = "returnBalances"
|
||||
POLONIEX_BALANCES_COMPLETE = "returnCompleteBalances"
|
||||
POLONIEX_DEPOSIT_ADDRESSES = "returnDepositAddresses"
|
||||
POLONIEX_GENERATE_NEW_ADDRESS = "generateNewAddress"
|
||||
POLONIEX_DEPOSITS_WITHDRAWALS = "returnDepositsWithdrawals"
|
||||
POLONIEX_ORDERS = "returnOpenOrders"
|
||||
POLONIEX_TRADE_HISTORY = "returnTradeHistory"
|
||||
POLONIEX_ORDER_BUY = "buy"
|
||||
POLONIEX_ORDER_SELL = "sell"
|
||||
POLONIEX_ORDER_CANCEL = "cancelOrder"
|
||||
POLONIEX_ORDER_MOVE = "moveOrder"
|
||||
POLONIEX_WITHDRAW = "withdraw"
|
||||
POLONIEX_FEE_INFO = "returnFeeInfo"
|
||||
POLONIEX_AVAILABLE_BALANCES = "returnAvailableAccountBalances"
|
||||
POLONIEX_TRADABLE_BALANCES = "returnTradableBalances"
|
||||
POLONIEX_TRANSFER_BALANCE = "transferBalance"
|
||||
POLONIEX_MARGIN_ACCOUNT_SUMMARY = "returnMarginAccountSummary"
|
||||
POLONIEX_MARGIN_BUY = "marginBuy"
|
||||
POLONIEX_MARGIN_SELL = "marginSell"
|
||||
POLONIEX_MARGIN_POSITION = "getMarginPosition"
|
||||
POLONIEX_MARGIN_POSITION_CLOSE = "closeMarginPosition"
|
||||
POLONIEX_CREATE_LOAN_OFFER = "createLoanOffer"
|
||||
POLONIEX_CANCEL_LOAN_OFFER = "cancelLoanOffer"
|
||||
POLONIEX_OPEN_LOAN_OFFERS = "returnOpenLoanOffers"
|
||||
POLONIEX_ACTIVE_LOANS = "returnActiveLoans"
|
||||
POLONIEX_AUTO_RENEW = "toggleAutoRenew"
|
||||
)
|
||||
|
||||
type Poloniex struct {
|
||||
exchange.ExchangeBase
|
||||
}
|
||||
|
||||
func (p *Poloniex) SetDefaults() {
|
||||
p.Name = "Poloniex"
|
||||
p.Enabled = false
|
||||
p.Fee = 0
|
||||
p.Verbose = false
|
||||
p.Websocket = false
|
||||
p.RESTPollingDelay = 10
|
||||
}
|
||||
|
||||
func (p *Poloniex) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
p.SetEnabled(false)
|
||||
} else {
|
||||
p.Enabled = true
|
||||
p.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
p.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
p.RESTPollingDelay = exch.RESTPollingDelay
|
||||
p.Verbose = exch.Verbose
|
||||
p.Websocket = exch.Websocket
|
||||
p.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
p.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
p.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetFee() float64 {
|
||||
return p.Fee
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetTicker() (map[string]PoloniexTicker, error) {
|
||||
type response struct {
|
||||
Data map[string]PoloniexTicker
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
path := fmt.Sprintf("%s/public?command=returnTicker", POLONIEX_API_URL)
|
||||
err := common.SendHTTPGetRequest(path, true, &resp.Data)
|
||||
|
||||
if err != nil {
|
||||
return resp.Data, err
|
||||
}
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetVolume() (interface{}, error) {
|
||||
var resp interface{}
|
||||
path := fmt.Sprintf("%s/public?command=return24hVolume", POLONIEX_API_URL)
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
//TO-DO: add support for individual pair depth fetching
|
||||
func (p *Poloniex) GetOrderbook(currencyPair string, depth int) (map[string]PoloniexOrderbook, error) {
|
||||
type Response struct {
|
||||
Data map[string]PoloniexOrderbook
|
||||
}
|
||||
|
||||
vals := url.Values{}
|
||||
vals.Set("currencyPair", currencyPair)
|
||||
|
||||
if depth != 0 {
|
||||
vals.Set("depth", strconv.Itoa(depth))
|
||||
}
|
||||
|
||||
resp := Response{}
|
||||
path := fmt.Sprintf("%s/public?command=returnOrderBook&%s", POLONIEX_API_URL, vals.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &resp.Data)
|
||||
|
||||
if err != nil {
|
||||
return resp.Data, err
|
||||
}
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetTradeHistory(currencyPair, start, end string) ([]PoloniexTradeHistory, error) {
|
||||
vals := url.Values{}
|
||||
vals.Set("currencyPair", currencyPair)
|
||||
|
||||
if start != "" {
|
||||
vals.Set("start", start)
|
||||
}
|
||||
|
||||
if end != "" {
|
||||
vals.Set("end", end)
|
||||
}
|
||||
|
||||
resp := []PoloniexTradeHistory{}
|
||||
path := fmt.Sprintf("%s/public?command=returnTradeHistory&%s", POLONIEX_API_URL, vals.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetChartData(currencyPair, start, end, period string) ([]PoloniexChartData, error) {
|
||||
vals := url.Values{}
|
||||
vals.Set("currencyPair", currencyPair)
|
||||
|
||||
if start != "" {
|
||||
vals.Set("start", start)
|
||||
}
|
||||
|
||||
if end != "" {
|
||||
vals.Set("end", end)
|
||||
}
|
||||
|
||||
if period != "" {
|
||||
vals.Set("period", period)
|
||||
}
|
||||
|
||||
resp := []PoloniexChartData{}
|
||||
path := fmt.Sprintf("%s/public?command=returnChartData&%s", POLONIEX_API_URL, vals.Encode())
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetCurrencies() (map[string]PoloniexCurrencies, error) {
|
||||
type Response struct {
|
||||
Data map[string]PoloniexCurrencies
|
||||
}
|
||||
resp := Response{}
|
||||
path := fmt.Sprintf("%s/public?command=returnCurrencies", POLONIEX_API_URL)
|
||||
err := common.SendHTTPGetRequest(path, true, &resp.Data)
|
||||
|
||||
if err != nil {
|
||||
return resp.Data, err
|
||||
}
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetLoanOrders(currency string) (PoloniexLoanOrders, error) {
|
||||
resp := PoloniexLoanOrders{}
|
||||
path := fmt.Sprintf("%s/public?command=returnLoanOrders¤cy=%s", POLONIEX_API_URL, currency)
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetBalances() (PoloniexBalance, error) {
|
||||
var result interface{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_BALANCES, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return PoloniexBalance{}, err
|
||||
}
|
||||
|
||||
data := result.(map[string]interface{})
|
||||
balance := PoloniexBalance{}
|
||||
balance.Currency = make(map[string]float64)
|
||||
|
||||
for x, y := range data {
|
||||
balance.Currency[x], _ = strconv.ParseFloat(y.(string), 64)
|
||||
}
|
||||
|
||||
return balance, nil
|
||||
}
|
||||
|
||||
type PoloniexCompleteBalances struct {
|
||||
Currency map[string]PoloniexCompleteBalance
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetCompleteBalances() (PoloniexCompleteBalances, error) {
|
||||
var result interface{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_BALANCES_COMPLETE, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return PoloniexCompleteBalances{}, err
|
||||
}
|
||||
|
||||
data := result.(map[string]interface{})
|
||||
balance := PoloniexCompleteBalances{}
|
||||
balance.Currency = make(map[string]PoloniexCompleteBalance)
|
||||
|
||||
for x, y := range data {
|
||||
dataVals := y.(map[string]interface{})
|
||||
balancesData := PoloniexCompleteBalance{}
|
||||
balancesData.Available, _ = strconv.ParseFloat(dataVals["available"].(string), 64)
|
||||
balancesData.OnOrders, _ = strconv.ParseFloat(dataVals["onOrders"].(string), 64)
|
||||
balancesData.BTCValue, _ = strconv.ParseFloat(dataVals["btcValue"].(string), 64)
|
||||
balance.Currency[x] = balancesData
|
||||
}
|
||||
|
||||
return balance, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetDepositAddresses() (PoloniexDepositAddresses, error) {
|
||||
var result interface{}
|
||||
addresses := PoloniexDepositAddresses{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_DEPOSIT_ADDRESSES, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return addresses, err
|
||||
}
|
||||
|
||||
addresses.Addresses = make(map[string]string)
|
||||
data := result.(map[string]interface{})
|
||||
for x, y := range data {
|
||||
addresses.Addresses[x] = y.(string)
|
||||
}
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GenerateNewAddress(currency string) (string, error) {
|
||||
type Response struct {
|
||||
Success int
|
||||
Error string
|
||||
Response string
|
||||
}
|
||||
resp := Response{}
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_GENERATE_NEW_ADDRESS, values, &resp)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.Error != "" {
|
||||
return "", errors.New(resp.Error)
|
||||
}
|
||||
|
||||
return resp.Response, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetDepositsWithdrawals(start, end string) (PoloniexDepositsWithdrawals, error) {
|
||||
resp := PoloniexDepositsWithdrawals{}
|
||||
values := url.Values{}
|
||||
|
||||
if start != "" {
|
||||
values.Set("start", start)
|
||||
} else {
|
||||
values.Set("start", "0")
|
||||
}
|
||||
|
||||
if end != "" {
|
||||
values.Set("end", end)
|
||||
} else {
|
||||
values.Set("end", strconv.FormatInt(time.Now().Unix(), 10))
|
||||
}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_DEPOSITS_WITHDRAWALS, values, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetOpenOrders(currency string) (interface{}, error) {
|
||||
values := url.Values{}
|
||||
|
||||
if currency != "" {
|
||||
values.Set("currencyPair", currency)
|
||||
result := PoloniexOpenOrdersResponse{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_ORDERS, values, &result.Data)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
} else {
|
||||
values.Set("currencyPair", "all")
|
||||
result := PoloniexOpenOrdersResponseAll{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_ORDERS, values, &result.Data)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetAuthenticatedTradeHistory(currency, start, end string) (interface{}, error) {
|
||||
values := url.Values{}
|
||||
|
||||
if start != "" {
|
||||
values.Set("start", start)
|
||||
}
|
||||
|
||||
if end != "" {
|
||||
values.Set("end", end)
|
||||
}
|
||||
|
||||
if currency != "" && currency != "all" {
|
||||
values.Set("currencyPair", currency)
|
||||
result := PoloniexAuthenticatedTradeHistoryResponse{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_TRADE_HISTORY, values, &result.Data)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
} else {
|
||||
values.Set("currencyPair", "all")
|
||||
result := PoloniexAuthenticatedTradeHistoryAll{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_TRADE_HISTORY, values, &result.Data)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poloniex) PlaceOrder(currency string, rate, amount float64, immediate, fillOrKill, buy bool) (PoloniexOrderResponse, error) {
|
||||
result := PoloniexOrderResponse{}
|
||||
values := url.Values{}
|
||||
|
||||
var orderType string
|
||||
if buy {
|
||||
orderType = POLONIEX_ORDER_BUY
|
||||
} else {
|
||||
orderType = POLONIEX_ORDER_SELL
|
||||
}
|
||||
|
||||
values.Set("currencyPair", currency)
|
||||
values.Set("rate", strconv.FormatFloat(rate, 'f', -1, 64))
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
if immediate {
|
||||
values.Set("immediateOrCancel", "1")
|
||||
}
|
||||
|
||||
if fillOrKill {
|
||||
values.Set("fillOrKill", "1")
|
||||
}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", orderType, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) CancelOrder(orderID int64) (bool, error) {
|
||||
result := PoloniexGenericResponse{}
|
||||
values := url.Values{}
|
||||
values.Set("orderNumber", strconv.FormatInt(orderID, 10))
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_ORDER_CANCEL, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if result.Success != 1 {
|
||||
return false, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) MoveOrder(orderID int64, rate, amount float64) (PoloniexMoveOrderResponse, error) {
|
||||
result := PoloniexMoveOrderResponse{}
|
||||
values := url.Values{}
|
||||
values.Set("orderNumber", strconv.FormatInt(orderID, 10))
|
||||
values.Set("rate", strconv.FormatFloat(rate, 'f', -1, 64))
|
||||
|
||||
if amount != 0 {
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_ORDER_MOVE, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if result.Success != 1 {
|
||||
return result, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) Withdraw(currency, address string, amount float64) (bool, error) {
|
||||
result := PoloniexWithdraw{}
|
||||
values := url.Values{}
|
||||
|
||||
values.Set("currency", currency)
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
values.Set("address", address)
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_WITHDRAW, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if result.Error != "" {
|
||||
return false, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetFeeInfo() (PoloniexFee, error) {
|
||||
result := PoloniexFee{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_FEE_INFO, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetTradableBalances() (map[string]map[string]float64, error) {
|
||||
type Response struct {
|
||||
Data map[string]map[string]interface{}
|
||||
}
|
||||
result := Response{}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_TRADABLE_BALANCES, url.Values{}, &result.Data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
balances := make(map[string]map[string]float64)
|
||||
|
||||
for x, y := range result.Data {
|
||||
balances[x] = make(map[string]float64)
|
||||
for z, w := range y {
|
||||
balances[x][z], _ = strconv.ParseFloat(w.(string), 64)
|
||||
}
|
||||
}
|
||||
|
||||
return balances, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) TransferBalance(currency, from, to string, amount float64) (bool, error) {
|
||||
values := url.Values{}
|
||||
result := PoloniexGenericResponse{}
|
||||
|
||||
values.Set("currency", currency)
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
values.Set("fromAccount", from)
|
||||
values.Set("toAccount", to)
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_TRANSFER_BALANCE, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if result.Error != "" && result.Success != 1 {
|
||||
return false, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetMarginAccountSummary() (PoloniexMargin, error) {
|
||||
result := PoloniexMargin{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_MARGIN_ACCOUNT_SUMMARY, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) PlaceMarginOrder(currency string, rate, amount, lendingRate float64, buy bool) (PoloniexOrderResponse, error) {
|
||||
result := PoloniexOrderResponse{}
|
||||
values := url.Values{}
|
||||
|
||||
var orderType string
|
||||
if buy {
|
||||
orderType = POLONIEX_MARGIN_BUY
|
||||
} else {
|
||||
orderType = POLONIEX_MARGIN_SELL
|
||||
}
|
||||
|
||||
values.Set("currencyPair", currency)
|
||||
values.Set("rate", strconv.FormatFloat(rate, 'f', -1, 64))
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
if lendingRate != 0 {
|
||||
values.Set("lendingRate", strconv.FormatFloat(lendingRate, 'f', -1, 64))
|
||||
}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", orderType, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetMarginPosition(currency string) (interface{}, error) {
|
||||
values := url.Values{}
|
||||
|
||||
if currency != "" && currency != "all" {
|
||||
values.Set("currencyPair", currency)
|
||||
result := PoloniexMarginPosition{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_MARGIN_POSITION, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
} else {
|
||||
values.Set("currencyPair", "all")
|
||||
|
||||
type Response struct {
|
||||
Data map[string]PoloniexMarginPosition
|
||||
}
|
||||
|
||||
result := Response{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_MARGIN_POSITION, values, &result.Data)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poloniex) CloseMarginPosition(currency string) (bool, error) {
|
||||
values := url.Values{}
|
||||
values.Set("currencyPair", currency)
|
||||
result := PoloniexGenericResponse{}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_MARGIN_POSITION_CLOSE, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if result.Success == 0 {
|
||||
return false, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) CreateLoanOffer(currency string, amount, rate float64, duration int, autoRenew bool) (int64, error) {
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
values.Set("duration", strconv.Itoa(duration))
|
||||
|
||||
if autoRenew {
|
||||
values.Set("autoRenew", "1")
|
||||
} else {
|
||||
values.Set("autoRenew", "0")
|
||||
}
|
||||
|
||||
values.Set("lendingRate", strconv.FormatFloat(rate, 'f', -1, 64))
|
||||
|
||||
type Response struct {
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
OrderID int64 `json:"orderID"`
|
||||
}
|
||||
|
||||
result := Response{}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_CREATE_LOAN_OFFER, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if result.Success == 0 {
|
||||
return 0, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return result.OrderID, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) CancelLoanOffer(orderNumber int64) (bool, error) {
|
||||
result := PoloniexGenericResponse{}
|
||||
values := url.Values{}
|
||||
values.Set("orderID", strconv.FormatInt(orderNumber, 10))
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_CANCEL_LOAN_OFFER, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if result.Success == 0 {
|
||||
return false, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetOpenLoanOffers() (map[string][]PoloniexLoanOffer, error) {
|
||||
type Response struct {
|
||||
Data map[string][]PoloniexLoanOffer
|
||||
}
|
||||
result := Response{}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_OPEN_LOAN_OFFERS, url.Values{}, &result.Data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.Data == nil {
|
||||
return nil, errors.New("There are no open loan offers.")
|
||||
}
|
||||
|
||||
return result.Data, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetActiveLoans() (PoloniexActiveLoans, error) {
|
||||
result := PoloniexActiveLoans{}
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_ACTIVE_LOANS, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) ToggleAutoRenew(orderNumber int64) (bool, error) {
|
||||
values := url.Values{}
|
||||
values.Set("orderNumber", strconv.FormatInt(orderNumber, 10))
|
||||
result := PoloniexGenericResponse{}
|
||||
|
||||
err := p.SendAuthenticatedHTTPRequest("POST", POLONIEX_AUTO_RENEW, values, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if result.Success == 0 {
|
||||
return false, errors.New(result.Error)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, result interface{}) error {
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
headers["Key"] = p.APIKey
|
||||
|
||||
nonce := time.Now().UnixNano()
|
||||
nonceStr := strconv.FormatInt(nonce, 10)
|
||||
|
||||
values.Set("nonce", nonceStr)
|
||||
values.Set("command", endpoint)
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(values.Encode()), []byte(p.APISecret))
|
||||
headers["Sign"] = common.HexEncodeToString(hmac)
|
||||
|
||||
path := fmt.Sprintf("%s/%s", POLONIEX_API_URL, POLONIEX_API_TRADING_ENDPOINT)
|
||||
resp, err := common.SendHTTPRequest(method, path, headers, bytes.NewBufferString(values.Encode()))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
182
exchanges/poloniex/poloniexwebsocket.go
Normal file
182
exchanges/poloniex/poloniexwebsocket.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package poloniex
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/beatgammit/turnpike"
|
||||
)
|
||||
|
||||
const (
|
||||
POLONIEX_WEBSOCKET_ADDRESS = "wss://api.poloniex.com"
|
||||
POLONIEX_WEBSOCKET_REALM = "realm1"
|
||||
POLONIEX_WEBSOCKET_TICKER = "ticker"
|
||||
POLONIEX_WEBSOCKET_TROLLBOX = "trollbox"
|
||||
)
|
||||
|
||||
type PoloniexWebsocketTicker struct {
|
||||
CurrencyPair string
|
||||
Last float64
|
||||
LowestAsk float64
|
||||
HighestBid float64
|
||||
PercentChange float64
|
||||
BaseVolume float64
|
||||
QuoteVolume float64
|
||||
IsFrozen bool
|
||||
High float64
|
||||
Low float64
|
||||
}
|
||||
|
||||
func PoloniexOnTicker(args []interface{}, kwargs map[string]interface{}) {
|
||||
ticker := PoloniexWebsocketTicker{}
|
||||
ticker.CurrencyPair = args[0].(string)
|
||||
ticker.Last, _ = strconv.ParseFloat(args[1].(string), 64)
|
||||
ticker.LowestAsk, _ = strconv.ParseFloat(args[2].(string), 64)
|
||||
ticker.HighestBid, _ = strconv.ParseFloat(args[3].(string), 64)
|
||||
ticker.PercentChange, _ = strconv.ParseFloat(args[4].(string), 64)
|
||||
ticker.BaseVolume, _ = strconv.ParseFloat(args[5].(string), 64)
|
||||
ticker.QuoteVolume, _ = strconv.ParseFloat(args[6].(string), 64)
|
||||
|
||||
if args[7].(float64) != 0 {
|
||||
ticker.IsFrozen = true
|
||||
} else {
|
||||
ticker.IsFrozen = false
|
||||
}
|
||||
|
||||
ticker.High, _ = strconv.ParseFloat(args[8].(string), 64)
|
||||
ticker.Low, _ = strconv.ParseFloat(args[9].(string), 64)
|
||||
}
|
||||
|
||||
type PoloniexWebsocketTrollboxMessage struct {
|
||||
MessageNumber float64
|
||||
Username string
|
||||
Message string
|
||||
Reputation float64
|
||||
}
|
||||
|
||||
func PoloniexOnTrollbox(args []interface{}, kwargs map[string]interface{}) {
|
||||
message := PoloniexWebsocketTrollboxMessage{}
|
||||
message.MessageNumber, _ = args[1].(float64)
|
||||
message.Username = args[2].(string)
|
||||
message.Message = args[3].(string)
|
||||
if len(args) == 5 {
|
||||
message.Reputation = args[4].(float64)
|
||||
}
|
||||
}
|
||||
|
||||
func PoloniexOnDepthOrTrade(args []interface{}, kwargs map[string]interface{}) {
|
||||
for x := range args {
|
||||
data := args[x].(map[string]interface{})
|
||||
msgData := data["data"].(map[string]interface{})
|
||||
msgType := data["type"].(string)
|
||||
|
||||
switch msgType {
|
||||
case "orderBookModify":
|
||||
{
|
||||
type PoloniexWebsocketOrderbookModify struct {
|
||||
Type string
|
||||
Rate float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
orderModify := PoloniexWebsocketOrderbookModify{}
|
||||
orderModify.Type = msgData["type"].(string)
|
||||
|
||||
rateStr := msgData["rate"].(string)
|
||||
orderModify.Rate, _ = strconv.ParseFloat(rateStr, 64)
|
||||
|
||||
amountStr := msgData["amount"].(string)
|
||||
orderModify.Amount, _ = strconv.ParseFloat(amountStr, 64)
|
||||
}
|
||||
case "orderBookRemove":
|
||||
{
|
||||
type PoloniexWebsocketOrderbookRemove struct {
|
||||
Type string
|
||||
Rate float64
|
||||
}
|
||||
|
||||
orderRemoval := PoloniexWebsocketOrderbookRemove{}
|
||||
orderRemoval.Type = msgData["type"].(string)
|
||||
|
||||
rateStr := msgData["rate"].(string)
|
||||
orderRemoval.Rate, _ = strconv.ParseFloat(rateStr, 64)
|
||||
}
|
||||
case "newTrade":
|
||||
{
|
||||
type PoloniexWebsocketNewTrade struct {
|
||||
Type string
|
||||
TradeID int64
|
||||
Rate float64
|
||||
Amount float64
|
||||
Date string
|
||||
Total float64
|
||||
}
|
||||
|
||||
trade := PoloniexWebsocketNewTrade{}
|
||||
trade.Type = msgData["type"].(string)
|
||||
|
||||
tradeIDstr := msgData["tradeID"].(string)
|
||||
trade.TradeID, _ = strconv.ParseInt(tradeIDstr, 10, 64)
|
||||
|
||||
rateStr := msgData["rate"].(string)
|
||||
trade.Rate, _ = strconv.ParseFloat(rateStr, 64)
|
||||
|
||||
amountStr := msgData["amount"].(string)
|
||||
trade.Amount, _ = strconv.ParseFloat(amountStr, 64)
|
||||
|
||||
totalStr := msgData["total"].(string)
|
||||
trade.Rate, _ = strconv.ParseFloat(totalStr, 64)
|
||||
|
||||
trade.Date = msgData["date"].(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poloniex) WebsocketClient() {
|
||||
for p.Enabled && p.Websocket {
|
||||
c, err := turnpike.NewWebsocketClient(turnpike.JSON, POLONIEX_WEBSOCKET_ADDRESS, nil)
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Error: %s\n", p.GetName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Verbose {
|
||||
log.Printf("%s Connected to Websocket.\n", p.GetName())
|
||||
}
|
||||
|
||||
_, err = c.JoinRealm(POLONIEX_WEBSOCKET_REALM, nil)
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to join realm. Error: %s\n", p.GetName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Verbose {
|
||||
log.Printf("%s Joined Websocket realm.\n", p.GetName())
|
||||
}
|
||||
|
||||
c.ReceiveDone = make(chan bool)
|
||||
|
||||
if err := c.Subscribe(POLONIEX_WEBSOCKET_TICKER, PoloniexOnTicker); err != nil {
|
||||
log.Printf("%s Error subscribing to ticker channel: %s\n", p.GetName(), err)
|
||||
}
|
||||
|
||||
if err := c.Subscribe(POLONIEX_WEBSOCKET_TROLLBOX, PoloniexOnTrollbox); err != nil {
|
||||
log.Printf("%s Error subscribing to trollbox channel: %s\n", p.GetName(), err)
|
||||
}
|
||||
|
||||
for x := range p.EnabledPairs {
|
||||
currency := p.EnabledPairs[x]
|
||||
if err := c.Subscribe(currency, PoloniexOnDepthOrTrade); err != nil {
|
||||
log.Printf("%s Error subscribing to %s channel: %s\n", p.GetName(), currency, err)
|
||||
}
|
||||
}
|
||||
|
||||
if p.Verbose {
|
||||
log.Printf("%s Subscribed to websocket channels.\n", p.GetName())
|
||||
}
|
||||
|
||||
<-c.ReceiveDone
|
||||
log.Printf("%s Websocket client disconnected.\n", p.GetName())
|
||||
}
|
||||
}
|
||||
140
exchanges/ticker/ticker.go
Normal file
140
exchanges/ticker/ticker.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package ticker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTickerForExchangeNotFound = "Ticker for exchange does not exist."
|
||||
ErrPrimaryCurrencyNotFound = "Error primary currency for ticker not found."
|
||||
ErrSecondaryCurrencyNotFound = "Error secondary currency for ticker not found."
|
||||
|
||||
Tickers []Ticker
|
||||
)
|
||||
|
||||
type TickerPrice struct {
|
||||
FirstCurrency string `json:"FirstCurrency"`
|
||||
SecondCurrency string `json:"SecondCurrency"`
|
||||
CurrencyPair string `json:"CurrencyPair"`
|
||||
Last float64 `json:"Last"`
|
||||
High float64 `json:"High"`
|
||||
Low float64 `json:"Low"`
|
||||
Bid float64 `json:"Bid"`
|
||||
Ask float64 `json:"Ask"`
|
||||
Volume float64 `json:"Volume"`
|
||||
PriceATH float64 `json:"PriceATH"`
|
||||
}
|
||||
|
||||
type Ticker struct {
|
||||
Price map[string]map[string]TickerPrice
|
||||
ExchangeName string
|
||||
}
|
||||
|
||||
func (t *Ticker) PriceToString(firstCurrency, secondCurrency, priceType string) string {
|
||||
switch priceType {
|
||||
case "last":
|
||||
return strconv.FormatFloat(t.Price[firstCurrency][secondCurrency].Last, 'f', -1, 64)
|
||||
case "high":
|
||||
return strconv.FormatFloat(t.Price[firstCurrency][secondCurrency].High, 'f', -1, 64)
|
||||
case "low":
|
||||
return strconv.FormatFloat(t.Price[firstCurrency][secondCurrency].Low, 'f', -1, 64)
|
||||
case "bid":
|
||||
return strconv.FormatFloat(t.Price[firstCurrency][secondCurrency].Bid, 'f', -1, 64)
|
||||
case "ask":
|
||||
return strconv.FormatFloat(t.Price[firstCurrency][secondCurrency].Ask, 'f', -1, 64)
|
||||
case "volume":
|
||||
return strconv.FormatFloat(t.Price[firstCurrency][secondCurrency].Volume, 'f', -1, 64)
|
||||
case "ath":
|
||||
return strconv.FormatFloat(t.Price[firstCurrency][secondCurrency].PriceATH, 'f', -1, 64)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func GetTicker(exchange, firstCurrency, secondCurrency string) (TickerPrice, error) {
|
||||
ticker, err := GetTickerByExchange(exchange)
|
||||
if err != nil {
|
||||
return TickerPrice{}, err
|
||||
}
|
||||
|
||||
if !FirstCurrencyExists(exchange, firstCurrency) {
|
||||
return TickerPrice{}, errors.New(ErrPrimaryCurrencyNotFound)
|
||||
}
|
||||
|
||||
if !SecondCurrencyExists(exchange, firstCurrency, secondCurrency) {
|
||||
return TickerPrice{}, errors.New(ErrSecondaryCurrencyNotFound)
|
||||
}
|
||||
|
||||
return ticker.Price[firstCurrency][secondCurrency], nil
|
||||
}
|
||||
|
||||
func GetTickerByExchange(exchange string) (*Ticker, error) {
|
||||
for _, y := range Tickers {
|
||||
if y.ExchangeName == exchange {
|
||||
return &y, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New(ErrTickerForExchangeNotFound)
|
||||
}
|
||||
|
||||
func FirstCurrencyExists(exchange, currency string) bool {
|
||||
for _, y := range Tickers {
|
||||
if y.ExchangeName == exchange {
|
||||
if _, ok := y.Price[currency]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func SecondCurrencyExists(exchange, primary, secondary string) bool {
|
||||
for _, y := range Tickers {
|
||||
if y.ExchangeName == exchange {
|
||||
if _, ok := y.Price[primary]; ok {
|
||||
if _, ok := y.Price[primary][secondary]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateNewTicker(exchangeName string, firstCurrency, secondCurrency string, tickerNew TickerPrice) Ticker {
|
||||
ticker := Ticker{}
|
||||
ticker.ExchangeName = exchangeName
|
||||
ticker.Price = make(map[string]map[string]TickerPrice)
|
||||
sMap := make(map[string]TickerPrice)
|
||||
sMap[secondCurrency] = tickerNew
|
||||
ticker.Price[firstCurrency] = sMap
|
||||
return ticker
|
||||
}
|
||||
|
||||
func ProcessTicker(exchangeName string, firstCurrency, secondCurrency string, tickerNew TickerPrice) {
|
||||
tickerNew.CurrencyPair = tickerNew.FirstCurrency + tickerNew.SecondCurrency
|
||||
|
||||
if len(Tickers) == 0 {
|
||||
CreateNewTicker(exchangeName, firstCurrency, secondCurrency, tickerNew)
|
||||
return
|
||||
} else {
|
||||
ticker, err := GetTickerByExchange(exchangeName)
|
||||
if err != nil {
|
||||
CreateNewTicker(exchangeName, firstCurrency, secondCurrency, tickerNew)
|
||||
return
|
||||
}
|
||||
|
||||
if FirstCurrencyExists(exchangeName, firstCurrency) {
|
||||
if !SecondCurrencyExists(exchangeName, firstCurrency, secondCurrency) {
|
||||
second := ticker.Price[firstCurrency]
|
||||
second[secondCurrency] = tickerNew
|
||||
ticker.Price[firstCurrency] = second
|
||||
return
|
||||
}
|
||||
}
|
||||
sMap := make(map[string]TickerPrice)
|
||||
sMap[secondCurrency] = tickerNew
|
||||
ticker.Price[firstCurrency] = sMap
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user