From 92abe3bd9584eec7c6fb02fc718932b6a262c15f Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Tue, 24 Feb 2015 17:53:56 +1100 Subject: [PATCH] Added static/dynamic fee values/methods for all exchanges. --- bitfinexhttp.go | 85 +++++++++++++++++++++++++++++++++++++++++-------- bitstamphttp.go | 49 ++++++++++++++++++++-------- btcchinahttp.go | 6 ++++ btcehttp.go | 6 ++++ btcmarkets.go | 6 ++++ huobihttp.go | 6 ++++ itbithttp.go | 11 +++++++ lakebtchttp.go | 11 +++++++ main.go | 2 ++ okcoinhttp.go | 13 ++++++++ 10 files changed, 169 insertions(+), 26 deletions(-) diff --git a/bitfinexhttp.go b/bitfinexhttp.go index 26766e08..3220aef3 100644 --- a/bitfinexhttp.go +++ b/bitfinexhttp.go @@ -34,6 +34,7 @@ const ( BITFINEX_CLAIM_POSITION = "position/claim" BITFINEX_HISTORY = "history" BITFINEX_TRADE_HISTORY = "mytrades" + BITFINEX_ACCOUNT_INFO = "account_infos" ) type BitfinexStats struct { @@ -56,6 +57,12 @@ type BookStructure struct { Price, Amount, Timestamp string } +type BitfinexFee struct { + Currency string + TakerFees float64 + MakerFees float64 +} + type BitfinexOrderbook struct { Bids []BookStructure Asks []BookStructure @@ -80,6 +87,7 @@ type Bitfinex struct { Orderbook BitfinexOrderbook Trades []TradeStructure SymbolsDetails []SymbolsDetails + Fees []BitfinexFee } func (b *Bitfinex) SetDefaults() { @@ -104,7 +112,52 @@ func (b *Bitfinex) SetAPIKeys(apiKey, apiSecret string) { b.APISecret = apiSecret } -func (b *Bitfinex) SendAuthenticatedHTTPRequest(path string, params map[string]interface{}) (err error) { +func (b *Bitfinex) GetFee(maker bool, symbol string) (float64, error) { + for _, i := range b.Fees { + if symbol == i.Currency { + if maker { + return i.MakerFees, nil + } else { + return i.TakerFees, nil + } + } + } + return 0, errors.New("Unable to find specified currency.") +} + +func (b *Bitfinex) GetAccountFeeInfo() (bool, error) { + type Fee struct { + Pairs string `json:"pairs"` + MakerFees string `json:"maker_fees"` + TakerFees string `json:"taker_fees"` + } + + type Response struct { + Data []map[string][]Fee + } + + var resp Response + err := b.SendAuthenticatedHTTPRequest(BITFINEX_ACCOUNT_INFO, nil, &resp.Data) + + if err != nil { + return false, err + } + + Fees := []BitfinexFee{} + + for _, i := range resp.Data[0]["fees"] { + var bfxFee BitfinexFee + bfxFee.Currency = i.Pairs + bfxFee.MakerFees, _ = strconv.ParseFloat(i.MakerFees, 64) + bfxFee.TakerFees, _ = strconv.ParseFloat(i.TakerFees, 64) + Fees = append(Fees, bfxFee) + } + + b.Fees = Fees + return true, nil +} + +func (b *Bitfinex) SendAuthenticatedHTTPRequest(path string, params map[string]interface{}, result interface{}) (err error) { request := make(map[string]interface{}) request["request"] = "/v1/" + path request["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10) @@ -147,7 +200,13 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(path string, params map[string]i } contents, _ := ioutil.ReadAll(resp.Body) - log.Printf("Recieved raw: %s\n", string(contents)) + log.Printf("Recieved raw: \n%s\n", string(contents)) + err = json.Unmarshal(contents, &result) + + if err != nil { + return errors.New("Unable to JSON response.") + } + resp.Body.Close() return nil } @@ -212,7 +271,7 @@ func (b *Bitfinex) NewDeposit(Symbol, Method, Wallet string) { request["method"] = Method request["wallet_name"] = Wallet - err := b.SendAuthenticatedHTTPRequest(BITFINEX_DEPOSIT, request) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_DEPOSIT, request, nil) if err != nil { fmt.Println(err) @@ -235,7 +294,7 @@ func (b *Bitfinex) NewOrder(Symbol string, Amount float64, Price float64, Buy bo //request["is_hidden"] - currently not implemented request["type"] = Type - err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_NEW, request) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_NEW, request, nil) if err != nil { fmt.Println(err) @@ -246,7 +305,7 @@ func (b *Bitfinex) CancelOrder(OrderID int) { request := make(map[string]interface{}) request["order_id"] = OrderID - err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_CANCEL, request) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_CANCEL, request, nil) if err != nil { fmt.Println(err) @@ -257,7 +316,7 @@ func (b *Bitfinex) CancelMultiplateOrders(OrderIDs []int) { request := make(map[string]interface{}) request["order_ids"] = OrderIDs - err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_CANCEL, request) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_CANCEL, request, nil) if err != nil { fmt.Println(err) @@ -265,7 +324,7 @@ func (b *Bitfinex) CancelMultiplateOrders(OrderIDs []int) { } func (b *Bitfinex) CancelAllOrders() { - err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_CANCEL_ALL, nil) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_CANCEL_ALL, nil, nil) if err != nil { fmt.Println(err) @@ -281,7 +340,7 @@ func (b *Bitfinex) GetOrderStatus(OrderID int) { request := make(map[string]interface{}) request["order_id"] = OrderID - err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_STATUS, request) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDER_STATUS, request, nil) if err != nil { fmt.Println(err) @@ -289,7 +348,7 @@ func (b *Bitfinex) GetOrderStatus(OrderID int) { } func (b *Bitfinex) GetActiveOrders() { - err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDERS, nil) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_ORDERS, nil, nil) if err != nil { fmt.Println(err) @@ -297,7 +356,7 @@ func (b *Bitfinex) GetActiveOrders() { } func (b *Bitfinex) GetActivePositions() { - err := b.SendAuthenticatedHTTPRequest(BITFINEX_POSITIONS, nil) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_POSITIONS, nil, nil) if err != nil { fmt.Println(err) @@ -308,7 +367,7 @@ func (b *Bitfinex) ClaimPosition(PositionID int) { request := make(map[string]interface{}) request["position_id"] = PositionID - err := b.SendAuthenticatedHTTPRequest(BITFINEX_CLAIM_POSITION, nil) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_CLAIM_POSITION, nil, nil) if err != nil { fmt.Println(err) @@ -329,7 +388,7 @@ func (b *Bitfinex) GetBalanceHistory(symbol string, timeSince time.Time, timeUnt request["wallet"] = wallet } - err := b.SendAuthenticatedHTTPRequest(BITFINEX_HISTORY, request) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_HISTORY, request, nil) if err != nil { fmt.Println(err) @@ -345,7 +404,7 @@ func (b *Bitfinex) GetTradeHistory(symbol string, timestamp time.Time, limit int request["limit_trades"] = limit } - err := b.SendAuthenticatedHTTPRequest(BITFINEX_TRADE_HISTORY, nil) + err := b.SendAuthenticatedHTTPRequest(BITFINEX_TRADE_HISTORY, nil, nil) if err != nil { fmt.Println(err) diff --git a/bitstamphttp.go b/bitstamphttp.go index 6f647eee..4e66fae1 100644 --- a/bitstamphttp.go +++ b/bitstamphttp.go @@ -7,6 +7,7 @@ import ( "fmt" "log" "encoding/hex" + "encoding/json" "crypto/hmac" "crypto/sha256" "strings" @@ -43,6 +44,8 @@ type Bitstamp struct { Orderbook Orderbook ConversionRate ConversionRate Transactions []Transactions + Balance BitstampAccountBalance + TakerFee, MakerFee float64 } type BitstampTicker struct { @@ -55,6 +58,16 @@ type BitstampTicker struct { Ask float64 `json:",string"` } +type BitstampAccountBalance struct { + BTCReserved float64 `json:"usd_balance,string"` + Fee float64 `json:",string"` + BTCAvailable float64 `json:"btc_balance,string"` + USDReserved float64 `json:"usd_reserved,string"` + BTCBalance float64 `json:"btc_balance,string"` + USDBalance float64 `json:"usd_balance,string"` + USDAvailable float64 `json:"usd_available,string"` +} + type Orderbook struct { Timestamp string Bids [][]string @@ -88,6 +101,10 @@ func (b *Bitstamp) IsEnabled() (bool) { return b.Enabled } +func (b *Bitstamp) GetFee() (float64) { + return b.Balance.Fee +} + func (b *Bitstamp) SetAPIKeys(clientID, apiKey, apiSecret string) { b.ClientID = clientID b.APIKey = apiKey @@ -133,7 +150,7 @@ func (b *Bitstamp) GetEURUSDConversionRate() { } func (b *Bitstamp) GetBalance() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BALANCE, url.Values{}) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BALANCE, url.Values{}, &b.Balance) if err != nil { fmt.Println(err) @@ -147,7 +164,7 @@ func (b *Bitstamp) GetUserTransactions(offset, limit, sort int64) { req.Add("limit", strconv.FormatInt(limit, 10)) req.Add("sort", strconv.FormatInt(sort, 10)) - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_USER_TRANSACTIONS, req) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_USER_TRANSACTIONS, req, nil) if err != nil { fmt.Println(err) @@ -158,7 +175,7 @@ func (b *Bitstamp) CancelOrder(OrderID int64) { var req = url.Values{} req.Add("id", strconv.FormatInt(OrderID, 10)) - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ORDER, req) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ORDER, req, nil) if err != nil { fmt.Println(err) @@ -166,7 +183,7 @@ func (b *Bitstamp) CancelOrder(OrderID int64) { } func (b *Bitstamp) GetOpenOrders() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_OPEN_ORDERS, url.Values{}) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_OPEN_ORDERS, url.Values{}, nil) if err != nil { fmt.Println(err) @@ -185,7 +202,7 @@ func (b *Bitstamp) PlaceOrder(price float64, amount float64, Type int) { log.Printf("Placing %s order at price %f for %f amount.\n", orderType, price, amount) - err := b.SendAuthenticatedHTTPRequest(orderType, req) + err := b.SendAuthenticatedHTTPRequest(orderType, req, nil) if err != nil { fmt.Println(err) @@ -193,7 +210,7 @@ func (b *Bitstamp) PlaceOrder(price float64, amount float64, Type int) { } func (b *Bitstamp) GetWithdrawalRequests() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_WITHDRAWAL_REQUESTS, url.Values{}) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_WITHDRAWAL_REQUESTS, url.Values{}, nil) if err != nil { fmt.Println(err) @@ -205,7 +222,7 @@ func (b *Bitstamp) BitcoinWithdrawal(amount float64, address string) { req.Add("amount", strconv.FormatFloat(amount, 'f', 8, 64)) req.Add("address", address) - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_WITHDRAWAL, req) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_WITHDRAWAL, req, nil) if err != nil { fmt.Println(err) @@ -213,7 +230,7 @@ func (b *Bitstamp) BitcoinWithdrawal(amount float64, address string) { } func (b *Bitstamp) BitcoinDepositAddress() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_DEPOSIT, url.Values{}) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_DEPOSIT, url.Values{}, nil) if err != nil { fmt.Println(err) @@ -221,7 +238,7 @@ func (b *Bitstamp) BitcoinDepositAddress() { } func (b *Bitstamp) UnconfirmedBitcoin() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_UNCONFIRMED_BITCOIN, url.Values{}) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_UNCONFIRMED_BITCOIN, url.Values{}, nil) if err != nil { fmt.Println(err) @@ -234,7 +251,7 @@ func (b *Bitstamp) RippleWithdrawal(amount float64, address, currency string) { req.Add("address", address) req.Add("currency", currency) - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_WITHDRAWAL, req) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_WITHDRAWAL, req, nil) if err != nil { fmt.Println(err) @@ -242,21 +259,20 @@ func (b *Bitstamp) RippleWithdrawal(amount float64, address, currency string) { } func (b *Bitstamp) RippleDepositAddress() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_DESPOIT, url.Values{}) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_DESPOIT, url.Values{}, nil) if err != nil { fmt.Println(err) } } -func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, values url.Values) (err error) { +func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, values url.Values, result interface{}) (err error) { nonce := strconv.FormatInt(time.Now().UnixNano(), 10) values.Set("key", b.APIKey) values.Set("nonce", nonce) hmac := hmac.New(sha256.New, []byte(b.APISecret)) hmac.Write([]byte(nonce + b.ClientID + b.APIKey)) values.Set("signature", strings.ToUpper(hex.EncodeToString(hmac.Sum(nil)))) - reqBody := strings.NewReader(values.Encode()) path = BITSTAMP_API_URL + path @@ -278,6 +294,13 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, values url.Values) contents, _ := ioutil.ReadAll(resp.Body) fmt.Printf("Recieved raw: %s\n", string(contents)) + + err = json.Unmarshal(contents, &result) + + if err != nil { + return errors.New("Unable to JSON response.") + } + resp.Body.Close() return nil } \ No newline at end of file diff --git a/btcchinahttp.go b/btcchinahttp.go index 05602dae..73cfbcf6 100644 --- a/btcchinahttp.go +++ b/btcchinahttp.go @@ -25,6 +25,7 @@ type BTCChina struct { Name string Enabled bool APISecret, APIKey string + Fee float64 } type BTCChinaTicker struct { @@ -43,6 +44,7 @@ type BTCChinaTicker struct { func (b *BTCChina) SetDefaults() { b.Name = "BTC China" b.Enabled = true + b.Fee = 0 } func (b *BTCChina) GetName() (string) { @@ -62,6 +64,10 @@ func (b *BTCChina) SetAPIKeys(apiKey, apiSecret string) { b.APISecret = apiSecret } +func (b *BTCChina) GetFee() (float64) { + return b.Fee +} + func (b *BTCChina) GetTicker(symbol string) (BTCChinaTicker) { type Response struct { Ticker BTCChinaTicker diff --git a/btcehttp.go b/btcehttp.go index 8c83e3d8..0d9a3ee2 100644 --- a/btcehttp.go +++ b/btcehttp.go @@ -28,6 +28,7 @@ type BTCE struct { Name string Enabled bool APIKey, APISecret string + Fee float64 } type BTCeTicker struct { @@ -46,6 +47,7 @@ type BTCeTicker struct { func (b *BTCE) SetDefaults() { b.Name = "BTCE" b.Enabled = true + b.Fee = 0.2 } func (b *BTCE) GetName() (string) { @@ -65,6 +67,10 @@ func (b *BTCE) SetAPIKeys(apiKey, apiSecret string) { b.APISecret = apiSecret } +func (b *BTCE) GetFee() (float64) { + return b.Fee +} + func (b *BTCE) GetTicker(symbol string) (BTCeTicker) { type Response struct { Ticker BTCeTicker diff --git a/btcmarkets.go b/btcmarkets.go index a04b02e1..c46c3491 100644 --- a/btcmarkets.go +++ b/btcmarkets.go @@ -21,6 +21,7 @@ const ( type BTCMarkets struct { Name string Enabled bool + Fee float64 APIKey, APISecret string } @@ -36,6 +37,7 @@ type BTCMarketsTicker struct { func (b *BTCMarkets) SetDefaults() { b.Name = "BTC Markets" b.Enabled = true + b.Fee = 0.85 } func (b *BTCMarkets) GetName() (string) { @@ -55,6 +57,10 @@ func (b *BTCMarkets) SetAPIKeys(apiKey, apiSecret string) { b.APISecret = apiSecret } +func (b *BTCMarkets) GetFee() (float64) { + return b.Fee +} + func (b *BTCMarkets) GetTicker(symbol string) (BTCMarketsTicker) { ticker := BTCMarketsTicker{} path := fmt.Sprintf("/market/%s/AUD/tick", symbol) diff --git a/huobihttp.go b/huobihttp.go index 92966e09..8ea9a626 100644 --- a/huobihttp.go +++ b/huobihttp.go @@ -21,6 +21,7 @@ type HUOBI struct { Name string Enabled bool AccessKey, SecretKey string + Fee float64 } type HuobiTicker struct { @@ -40,6 +41,7 @@ type HuobiTickerResponse struct { func (h *HUOBI) SetDefaults() { h.Name = "Huobi" h.Enabled = true + h.Fee = 0 } func (h *HUOBI) GetName() (string) { @@ -59,6 +61,10 @@ func (h *HUOBI) SetAPIKeys(apiKey, apiSecret string) { h.SecretKey = apiSecret } +func (h *HUOBI) GetFee() (float64) { + return h.Fee +} + func (h *HUOBI) GetTicker(symbol string) (HuobiTicker) { resp := HuobiTickerResponse{} path := fmt.Sprintf("http://market.huobi.com/staticmarket/ticker_%s_json.js", symbol) diff --git a/itbithttp.go b/itbithttp.go index 62753242..78f6497c 100644 --- a/itbithttp.go +++ b/itbithttp.go @@ -24,6 +24,7 @@ type ItBit struct { Name string Enabled bool ClientKey, APISecret, UserID string + MakerFee, TakerFee float64 } type ItBitTicker struct { @@ -49,6 +50,8 @@ type ItBitTicker struct { func (i *ItBit) SetDefaults() { i.Name = "ITBIT" i.Enabled = true + i.MakerFee = -0.10 + i.TakerFee = 0.50 } func (i *ItBit) GetName() (string) { @@ -68,6 +71,14 @@ func (i *ItBit) SetAPIKeys(apiKey, apiSecret string) { i.APISecret = apiSecret } +func (i *ItBit) GetFee(maker bool) (float64) { + if maker { + return i.MakerFee + } else { + return i.TakerFee + } +} + func (i *ItBit) GetTicker(currency string) (ItBitTicker) { path := ITBIT_API_URL + "/markets/" + currency + "/ticker" var itbitTicker ItBitTicker diff --git a/lakebtchttp.go b/lakebtchttp.go index 4d51384a..b95c7785 100644 --- a/lakebtchttp.go +++ b/lakebtchttp.go @@ -33,6 +33,7 @@ type LakeBTC struct { Name string Enabled bool Email, APISecret string + TakerFee, MakerFee float64 } type LakeBTCTicker struct { @@ -51,6 +52,8 @@ type LakeBTCTickerResponse struct { func (l *LakeBTC) SetDefaults() { l.Name = "LakeBTC" l.Enabled = true + l.TakerFee = 0.2 + l.MakerFee = 0.15 } func (l *LakeBTC) GetName() (string) { @@ -70,6 +73,14 @@ func (l *LakeBTC) SetAPIKeys(apiKey, apiSecret string) { l.APISecret = apiSecret } +func (l *LakeBTC) GetFee(maker bool) (float64) { + if (maker) { + return l.MakerFee + } else { + return l.TakerFee + } +} + func (l *LakeBTC) GetTicker() (LakeBTCTickerResponse) { response := LakeBTCTickerResponse{} err := SendHTTPRequest(LAKEBTC_API_URL + LAKEBTC_TICKER, true, &response) diff --git a/main.go b/main.go index 23ecc737..750c8bfd 100644 --- a/main.go +++ b/main.go @@ -77,6 +77,7 @@ func main() { } else { log.Printf("%s enabled.\n", exch.Name) exchange.bitstamp.SetAPIKeys(exch.ClientID, exch.APIKey, exch.APISecret) + exchange.bitstamp.GetBalance() } } else if exchange.bitfinex.GetName() == exch.Name { if !exch.Enabled { @@ -85,6 +86,7 @@ func main() { } else { log.Printf("%s enabled.\n", exch.Name) exchange.bitfinex.SetAPIKeys(exch.APIKey, exch.APISecret) + exchange.bitfinex.GetAccountFeeInfo() } } else if exchange.btce.GetName() == exch.Name { if !exch.Enabled { diff --git a/okcoinhttp.go b/okcoinhttp.go index 3fa08de3..393b9881 100644 --- a/okcoinhttp.go +++ b/okcoinhttp.go @@ -21,6 +21,7 @@ type OKCoin struct { Name string Enabled bool APIUrl, PartnerID, SecretKey string + TakerFee, MakerFee float64 } type OKCoinTicker struct { @@ -82,6 +83,18 @@ func (o *OKCoin) SetAPIKeys(apiKey, apiSecret string) { o.SecretKey = apiSecret } +func (o *OKCoin) GetFee(maker bool) (float64) { + if (o.APIUrl == OKCOIN_API_URL) { + if maker { + return o.MakerFee + } else { + return o.TakerFee + } + } + // Chinese exchange does not have any trading fees + return 0 +} + func (o *OKCoin) GetTicker(symbol string) (OKCoinTicker) { resp := OKCoinTickerResponse{} path := fmt.Sprintf("ticker.do?symbol=%s&ok=1", symbol)