Package exchanges

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

View File

@@ -0,0 +1,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
}

View 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
}

View 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
}

View 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())
}
}