diff --git a/README.md b/README.md index c5377e46..73e35519 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ A cryptocurrency trading bot supporting multiple exchanges written in Golang. | Exchange | REST API | Streaming API | FIX API | |----------|------|-----------|-----| +| Alphapoint | Yes | No | NA | | ANXPRO | Yes | No | NA | | Bitfinex | Yes | NA | NA | | Bitstamp | Yes | Yes | NA | @@ -14,6 +15,7 @@ A cryptocurrency trading bot supporting multiple exchanges written in Golang. | BTCMarkets | Yes | NA | NA | | Coinbase | Yes | Yes | No| | Cryptsy | Yes | Yes | NA| +| DWVX | Yes | No | NA | | Huobi | Yes | Yes |No | ItBit | Yes | NA | NA | | Kraken | Yes | NA | NA diff --git a/alphapointhttp.go b/alphapointhttp.go index 6d1634aa..cc926d2a 100644 --- a/alphapointhttp.go +++ b/alphapointhttp.go @@ -124,13 +124,22 @@ type AlphapointProducts struct { 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 { + ProductPairs []struct { ProductPairName string `json:"productPairName"` ProductPairCode int `json:"productPairCode"` TradeCount int `json:"tradeCount"` @@ -141,18 +150,30 @@ type AlphapointAccountInfo struct { } type AlphapointOrder struct { - ServerOrderID int64 `json:"ServerOrderId"` - AccountID int `json:"AccountId"` - Price float64 `json:"price"` - QuantityTotal float64 `json:"QtyTotal"` - QuantityRemaining float64 `json:"QtyRemaining"` - ReceiveTime int64 `json:"ReceiveTime"` - Side int `json:"side"` + 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 { - Instrument string `json:"ins"` - Orders []AlphapointOrder `json:"openOrders"` + 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"` } func (a *Alphapoint) SetDefaults() { @@ -276,12 +297,13 @@ func (a *Alphapoint) CreateAccount(firstName, lastName, email, phone, password s return nil } -func (a *Alphapoint) GetUserInfo() error { - err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_USERINFO, map[string]interface{}{}, nil) +func (a *Alphapoint) GetUserInfo() (AlphapointUserInfo, error) { + response := AlphapointUserInfo{} + err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_USERINFO, map[string]interface{}{}, &response) if err != nil { - return err + return AlphapointUserInfo{}, err } - return nil + return response, nil } func (a *Alphapoint) GetAccountInfo() (AlphapointAccountInfo, error) { @@ -313,8 +335,9 @@ func (a *Alphapoint) GetAccountTrades(symbol string, startIndex, count int) (Alp return response, nil } -func (a *Alphapoint) GetDepositAddresses() error { +func (a *Alphapoint) GetDepositAddresses() ([]AlphapointDepositAddresses, error) { type Response struct { + Addresses []AlphapointDepositAddresses IsAccepted bool `json:"isAccepted"` RejectReason string `json:"rejectReason"` } @@ -322,12 +345,12 @@ func (a *Alphapoint) GetDepositAddresses() error { response := Response{} err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_DEPOSIT_ADDRESSES, map[string]interface{}{}, &response) if err != nil { - return err + return nil, err } if !response.IsAccepted { - return errors.New(response.RejectReason) + return nil, errors.New(response.RejectReason) } - return nil + return response.Addresses, nil } func (a *Alphapoint) WithdrawCoins(symbol, product string, amount float64, address string) error { @@ -358,15 +381,15 @@ func (a *Alphapoint) CreateOrder(symbol, side string, orderType int, quantity, p request := make(map[string]interface{}) request["ins"] = symbol request["side"] = side - request["orderType"] = "0" + request["orderType"] = orderType request["qty"] = strconv.FormatFloat(quantity, 'f', 8, 64) request["px"] = strconv.FormatFloat(price, 'f', 2, 64) type Response struct { - IsAccepted bool `json:"isAccepted"` - RejectReason string `json:"rejectReason"` - DateTimeUTC int64 `json:"dateTimeUtc"` - OrderID int64 `json:"serverOrderId,string"` + ServerOrderID int64 `json:"serverOrderId"` + DateTimeUTC float64 `json:"dateTimeUtc"` + IsAccepted bool `json:"isAccepted"` + RejectReason string `json:"rejectReason"` } response := Response{} @@ -378,7 +401,7 @@ func (a *Alphapoint) CreateOrder(symbol, side string, orderType int, quantity, p if !response.IsAccepted { return 0, errors.New(response.RejectReason) } - return response.OrderID, nil + return response.ServerOrderID, nil } func (a *Alphapoint) ModifyOrder(symbol string, OrderID, action int64) (int64, error) { @@ -388,10 +411,11 @@ func (a *Alphapoint) ModifyOrder(symbol string, OrderID, action int64) (int64, e request["modifyAction"] = action type Response struct { - IsAccepted bool `json:"isAccepted"` - RejectReason string `json:"rejectReason"` - DateTimeUTC int64 `json:"dateTimeUtc"` - OrderID int64 `json:"serverOrderId,string"` + ModifyOrderID int64 `json:"modifyOrderId"` + ServerOrderID int64 `json:"serverOrderId"` + DateTimeUTC float64 `json:"dateTimeUtc"` + IsAccepted bool `json:"isAccepted"` + RejectReason string `json:"rejectReason"` } response := Response{} @@ -403,7 +427,7 @@ func (a *Alphapoint) ModifyOrder(symbol string, OrderID, action int64) (int64, e if !response.IsAccepted { return 0, errors.New(response.RejectReason) } - return response.OrderID, nil + return response.ModifyOrderID, nil } func (a *Alphapoint) CancelOrder(symbol string, OrderID int64) (int64, error) { @@ -412,10 +436,11 @@ func (a *Alphapoint) CancelOrder(symbol string, OrderID int64) (int64, error) { request["serverOrderId"] = OrderID type Response struct { - IsAccepted bool `json:"isAccepted"` - RejectReason string `json:"rejectReason"` - DateTimeUTC int64 `json:"dateTimeUtc"` - OrderID int64 `json:"serverOrderId,string"` + CancelOrderID int64 `json:"cancelOrderId"` + ServerOrderID int64 `json:"serverOrderId"` + DateTimeUTC float64 `json:"dateTimeUtc"` + IsAccepted bool `json:"isAccepted"` + RejectReason string `json:"rejectReason"` } response := Response{} @@ -427,7 +452,7 @@ func (a *Alphapoint) CancelOrder(symbol string, OrderID int64) (int64, error) { if !response.IsAccepted { return 0, errors.New(response.RejectReason) } - return response.OrderID, nil + return response.CancelOrderID, nil } func (a *Alphapoint) CancelAllOrders(symbol string) error { @@ -451,38 +476,32 @@ func (a *Alphapoint) CancelAllOrders(symbol string) error { return nil } -func (a *Alphapoint) GetOrders() (AlphapointOrderInfo, error) { - type Response struct { - Orders AlphapointOrderInfo `json:"openOrdersInfo"` - IsAccepted bool `json:"isAccepted"` - RejectReason string `json:"rejectReason"` - } - - response := Response{} +func (a *Alphapoint) GetOrders() ([]AlphapointOpenOrders, error) { + response := AlphapointOrderInfo{} err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_OPEN_ORDERS, map[string]interface{}{}, &response) if err != nil { - return AlphapointOrderInfo{}, err + return nil, err } if !response.IsAccepted { - return AlphapointOrderInfo{}, errors.New(response.RejectReason) + return nil, errors.New(response.RejectReason) } - return response.Orders, nil + 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,string"` + 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', 8, 64) - request["px"] = strconv.FormatFloat(price, 'f', 2, 64) + 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) @@ -524,9 +543,10 @@ func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[ headers := make(map[string]string) headers["Content-Type"] = "application/json" data["apiKey"] = a.APIKey - nonce := strconv.FormatInt(time.Now().Unix(), 10) + nonce := time.Now().UnixNano() + nonceStr := strconv.FormatInt(nonce, 10) data["apiNonce"] = nonce - hmac := GetHMAC(HASH_SHA256, []byte(nonce+a.UserID+a.APIKey), []byte(a.APISecret)) + hmac := GetHMAC(HASH_SHA256, []byte(nonceStr+a.UserID+a.APIKey), []byte(a.APISecret)) data["apiSig"] = StringToUpper(HexEncodeToString(hmac)) path = fmt.Sprintf("%s/ajax/v%s/%s", a.APIUrl, ALPHAPOINT_API_VERSION, path) PayloadJson, err := JSONEncode(data) diff --git a/config_example.json b/config_example.json index 8ccb658c..4eb806ed 100644 --- a/config_example.json +++ b/config_example.json @@ -116,6 +116,20 @@ "EnabledPairs": "BTCUSD,LTCUSD,DASHBTC,DOGEBTC", "BaseCurrencies": "USD" }, + { + "Name": "DWVX", + "Enabled": true, + "Verbose": false, + "Websocket": false, + "RESTPollingDelay": 10, + "AuthenticatedAPISupport": false, + "APIKey": "Key", + "APISecret": "Secret", + "ClientID": "ClientID", + "AvailablePairs": "BTCAUD", + "EnabledPairs": "BTCAUD", + "BaseCurrencies": "AUD" + }, { "Name": "Huobi", "Enabled": true, diff --git a/dwvxhttp.go b/dwvxhttp.go new file mode 100644 index 00000000..281c5904 --- /dev/null +++ b/dwvxhttp.go @@ -0,0 +1,176 @@ +package main + +import ( + "log" + "time" +) + +const ( + DWVX_API_URL = "https://api.dwvx.com.au:8400" +) + +type DWVX struct { + Name string + Enabled bool + Verbose bool + Websocket bool + RESTPollingDelay time.Duration + AuthenticatedAPISupport bool + ClientID, APIKey, APISecret string + Ticker AlphapointTicker + TakerFee, MakerFee float64 + BaseCurrencies []string + AvailablePairs []string + EnabledPairs []string + API Alphapoint + DepositAddresses map[string]string +} + +func (d *DWVX) SetDefaults() { + d.Name = "DWVX" + d.API.APIUrl = DWVX_API_URL + d.Enabled = true + d.Verbose = false + d.Websocket = false + d.RESTPollingDelay = 10 + d.DepositAddresses = make(map[string]string) +} + +func (d *DWVX) GetName() string { + return d.Name +} + +func (d *DWVX) SetEnabled(enabled bool) { + d.Enabled = enabled +} + +func (d *DWVX) IsEnabled() bool { + return d.Enabled +} + +func (d *DWVX) SetAPIKeys(userID, apiKey, apiSecret string) { + d.API.APIKey = apiKey + d.API.APISecret = apiSecret + d.API.UserID = userID +} + +func (d *DWVX) Run() { + if d.Verbose { + log.Printf("%s polling delay: %ds.\n", d.GetName(), d.RESTPollingDelay) + log.Printf("%s %d currencies enabled: %s.\n", d.GetName(), len(d.EnabledPairs), d.EnabledPairs) + } + + products, err := d.GetProductPairs() + if err != nil { + log.Printf("%s Failed to get available symbols.\n", d.GetName()) + } else { + availProducts := []string{} + for _, x := range products.ProductPairs { + availProducts = append(availProducts, x.Name) + } + + diff := StringSliceDifference(d.AvailablePairs, availProducts) + if len(diff) > 0 { + exch, err := GetExchangeConfig(d.Name) + if err != nil { + log.Println(err) + } else { + log.Printf("%s Updating available pairs. Difference: %s.\n", d.Name, diff) + exch.AvailablePairs = JoinStrings(availProducts, ",") + UpdateExchangeConfig(exch) + } + } + } + + for d.Enabled { + for _, x := range d.EnabledPairs { + currency := x + go func() { + ticker, err := d.GetTicker(currency) + if err != nil { + log.Println(err) + return + } + log.Printf("DWVX %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Total24HrQtyTraded) + AddExchangeInfo(d.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume) + }() + } + time.Sleep(time.Second * d.RESTPollingDelay) + } +} + +func (d *DWVX) GetTicker(symbol string) (AlphapointTicker, error) { + return d.API.GetTicker(symbol) +} + +func (d *DWVX) GetTrades(symbol string, startIndex, count int) (AlphapointTrades, error) { + return d.API.GetTrades(symbol, startIndex, count) +} + +func (d *DWVX) GetTradesByDate(symbol string, startDate, endDate int64) (AlphapointTradesByDate, error) { + return d.API.GetTradesByDate(symbol, startDate, endDate) +} + +func (d *DWVX) GetOrderbook(symbol string) (AlphapointOrderbook, error) { + return d.API.GetOrderbook(symbol) +} + +func (d *DWVX) GetProductPairs() (AlphapointProductPairs, error) { + return d.API.GetProductPairs() +} + +func (d *DWVX) GetProducts() (AlphapointProducts, error) { + return d.API.GetProducts() +} + +func (d *DWVX) GetUserInfo() (AlphapointUserInfo, error) { + return d.API.GetUserInfo() +} + +func (d *DWVX) GetAccountTrades(symbol string, startIndex, count int) (AlphapointTrades, error) { + return d.API.GetAccountTrades(symbol, startIndex, count) +} +func (d *DWVX) GetAccountInfo() (AlphapointAccountInfo, error) { + return d.API.GetAccountInfo() +} + +func (d *DWVX) GetDepositAddresses() error { + result, err := d.API.GetDepositAddresses() + if err != nil { + return err + } + for _, x := range result { + if x.DepositAddress != "" { + d.DepositAddresses[x.Name] = x.DepositAddress + } + } + return nil +} + +func (d *DWVX) WithdrawCoins(symbol, product string, amount float64, address string) error { + return d.API.WithdrawCoins(symbol, product, amount, address) +} + +func (d *DWVX) CreateOrder(symbol, side string, orderType int, quantity, price float64) (int64, error) { + return d.API.CreateOrder(symbol, side, orderType, quantity, price) +} + +func (d *DWVX) ModifyOrder(symbol string, OrderID, action int64) (int64, error) { + return d.API.ModifyOrder(symbol, OrderID, action) +} + +func (d *DWVX) CancelOrder(symbol string, orderID int64) (int64, error) { + return d.API.CancelOrder(symbol, orderID) +} + +func (d *DWVX) CancelAllOrders(symbol string) error { + return d.API.CancelAllOrders(symbol) +} + +func (d *DWVX) GetOrders() ([]AlphapointOpenOrders, error) { + return d.API.GetOrders() +} + +func (d *DWVX) GetOrderFee(symbol, side string, amount, price float64) (float64, error) { + return d.API.GetOrderFee(symbol, side, amount, price) +} diff --git a/events.go b/events.go index fb99f9c3..ab1319ea 100644 --- a/events.go +++ b/events.go @@ -114,6 +114,7 @@ func (e *Event) CheckCondition() bool { condition := SplitStrings(e.Condition, ",") targetPrice, _ := strconv.ParseFloat(condition[1], 64) + /* to-do: add event handling for all currencies and fiat currencies */ if bot.exchange.bitfinex.GetName() == e.Exchange { lastPrice = bot.exchange.bitfinex.GetTicker("btcusd").Last } else if bot.exchange.bitstamp.GetName() == e.Exchange { @@ -122,6 +123,13 @@ func (e *Event) CheckCondition() bool { lastPrice = bot.exchange.coinbase.GetTicker("BTC-USD").Price } else if bot.exchange.cryptsy.GetName() == e.Exchange { lastPrice = bot.exchange.cryptsy.Market["BTCUSD"].LastTrade.Price + } else if bot.exchange.dwvx.GetName() == e.Exchange { + result, err := bot.exchange.dwvx.GetTicker("BTCAUD") + if err != nil { + lastPrice = 0 + } else { + lastPrice = result.Last + } } else if bot.exchange.lakebtc.GetName() == e.Exchange { lastPrice = bot.exchange.lakebtc.GetTicker().CNY.Last } else if bot.exchange.btcchina.GetName() == e.Exchange { @@ -249,6 +257,7 @@ func IsValidExchange(Exchange string) bool { bot.exchange.btcmarkets.GetName() == Exchange && bot.exchange.btcmarkets.IsEnabled() || bot.exchange.coinbase.GetName() == Exchange && bot.exchange.coinbase.IsEnabled() || bot.exchange.cryptsy.GetName() == Exchange && bot.exchange.cryptsy.IsEnabled() || + bot.exchange.dwvx.GetName() == Exchange && bot.exchange.dwvx.IsEnabled() || bot.exchange.huobi.GetName() == Exchange && bot.exchange.huobi.IsEnabled() || bot.exchange.itbit.GetName() == Exchange && bot.exchange.itbit.IsEnabled() || bot.exchange.kraken.GetName() == Exchange && bot.exchange.kraken.IsEnabled() || diff --git a/main.go b/main.go index 1edc50fc..894fe06f 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ type Exchange struct { btcmarkets BTCMarkets coinbase Coinbase cryptsy Cryptsy + dwvx DWVX okcoinChina OKCoin okcoinIntl OKCoin itbit ItBit @@ -99,6 +100,7 @@ func main() { bot.exchange.btcmarkets.SetDefaults() bot.exchange.coinbase.SetDefaults() bot.exchange.cryptsy.SetDefaults() + bot.exchange.dwvx.SetDefaults() bot.exchange.okcoinChina.SetURL(OKCOIN_API_URL_CHINA) bot.exchange.okcoinChina.SetDefaults() bot.exchange.okcoinIntl.SetURL(OKCOIN_API_URL) @@ -232,6 +234,20 @@ func main() { bot.exchange.cryptsy.EnabledPairs = SplitStrings(exch.EnabledPairs, ",") go bot.exchange.cryptsy.Run() } + } else if bot.exchange.dwvx.GetName() == exch.Name { + if !exch.Enabled { + bot.exchange.dwvx.SetEnabled(false) + } else { + bot.exchange.dwvx.AuthenticatedAPISupport = exch.AuthenticatedAPISupport + bot.exchange.dwvx.SetAPIKeys(exch.ClientID, exch.APIKey, exch.APISecret) + bot.exchange.dwvx.RESTPollingDelay = exch.RESTPollingDelay + bot.exchange.dwvx.Verbose = exch.Verbose + bot.exchange.dwvx.Websocket = exch.Websocket + bot.exchange.dwvx.BaseCurrencies = SplitStrings(exch.BaseCurrencies, ",") + bot.exchange.dwvx.AvailablePairs = SplitStrings(exch.AvailablePairs, ",") + bot.exchange.dwvx.EnabledPairs = SplitStrings(exch.EnabledPairs, ",") + go bot.exchange.dwvx.Run() + } } else if bot.exchange.okcoinChina.GetName() == exch.Name { if !exch.Enabled { bot.exchange.okcoinChina.SetEnabled(false)