mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -2,4 +2,7 @@ config.json
|
||||
config.dat
|
||||
node_modules
|
||||
lib
|
||||
.vscode
|
||||
.vscode
|
||||
|
||||
testdata/dump
|
||||
testdata/writefiletest
|
||||
10
.travis.yml
10
.travis.yml
@@ -1,14 +1,14 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
- tip
|
||||
go:
|
||||
- 1.8.x
|
||||
#- master
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
|
||||
script:
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||
- ./testdata/test.sh
|
||||
|
||||
install:
|
||||
- go get github.com/gorilla/websocket
|
||||
@@ -18,4 +18,4 @@ install:
|
||||
- go get github.com/gorilla/mux
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
@@ -7,3 +7,7 @@ Cornel - cornelk
|
||||
Łukasz Kurowski - crackcomm
|
||||
Adrian Gallagher - thrasher-
|
||||
Manuel Kreutz - 140am
|
||||
libsora.so - if1live
|
||||
Tong - tongxiaofeng
|
||||
Jamie Cheng - starit
|
||||
Jake - snipesjr
|
||||
55
README.md
55
README.md
@@ -1,8 +1,10 @@
|
||||
## Cryptocurrency trading bot written in Golang
|
||||
# Cryptocurrency trading bot written in Golang
|
||||
|
||||
[](https://travis-ci.org/thrasher-/gocryptotrader)
|
||||
[](https://github.com/thrasher-/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-/gocryptotrader)
|
||||
[](http://codecov.io/github/thrasher-/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-/gocryptotrader)
|
||||
|
||||
A cryptocurrency trading bot supporting multiple exchanges written in Golang.
|
||||
|
||||
@@ -10,7 +12,7 @@ A cryptocurrency trading bot supporting multiple exchanges written in Golang.
|
||||
|
||||
## Community
|
||||
|
||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader](https://gocryptotrader.herokuapp.com/)
|
||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://gocryptotrader.herokuapp.com/)
|
||||
|
||||
## Exchange Support Table
|
||||
|
||||
@@ -20,8 +22,8 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader]
|
||||
| ANXPRO | Yes | No | NA |
|
||||
| Bitfinex | Yes | Yes | NA |
|
||||
| Bitstamp | Yes | Yes | NA |
|
||||
| Bittrex | Yes | No | NA |
|
||||
| BTCC | Yes | Yes | No |
|
||||
| BTCE | Yes | NA | NA |
|
||||
| BTCMarkets | Yes | NA | NA |
|
||||
| COINUT | Yes | No | NA |
|
||||
| GDAX(Coinbase) | Yes | Yes | No|
|
||||
@@ -34,25 +36,29 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader]
|
||||
| LocalBitcoins | Yes | NA | NA |
|
||||
| OKCoin (both) | Yes | Yes | No |
|
||||
| Poloniex | Yes | Yes | NA |
|
||||
| WEX | Yes | NA | NA |
|
||||
|
||||
We are aiming to support the top 20 highest volume exchanges based off the [CoinMarketCap exchange data](https://coinmarketcap.com/exchanges/volume/24-hour/).
|
||||
|
||||
** NA means not applicable as the Exchange does not support the feature.
|
||||
|
||||
## Current Features
|
||||
|
||||
+ Support for all Exchange fiat and digital currencies, with the ability to individually toggle them on/off.
|
||||
+ AES encrypted config file.
|
||||
+ REST API support for all exchanges.
|
||||
+ Websocket support for applicable exchanges.
|
||||
+ Ability to turn off/on certain exchanges.
|
||||
+ Ability to adjust manual polling timer for exchanges.
|
||||
+ SMS notification support via SMS Gateway.
|
||||
+ Packages for handling currency pairs, ticker/orderbook fetching and currency conversion.
|
||||
+ Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking.
|
||||
+ Basic event trigger system.
|
||||
+ WebGUI.
|
||||
|
||||
## Planned Features
|
||||
+ WebGUI.
|
||||
+ FIX support.
|
||||
+ Expanding event trigger system.
|
||||
+ TALib.
|
||||
+ Trade history summary generation for tax purposes.
|
||||
+ ZMQ Hub for manging different gocryptotrader instances.
|
||||
|
||||
Planned features can be found on our [community Trello page](https://trello.com/b/ZAhMhpOy/gocryptotrader).
|
||||
|
||||
## Contribution
|
||||
|
||||
@@ -60,18 +66,29 @@ Please feel free to submit any pull requests or suggest any desired features to
|
||||
|
||||
When submitting a PR, please abide by our coding guidelines:
|
||||
|
||||
* Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
* Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
* Code must adhere to our [coding style](https://github.com/thrasher-/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
* Pull requests need to be based on and opened against the `master` branch.
|
||||
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
+ Code must adhere to our [coding style](https://github.com/thrasher-/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
+ Pull requests need to be based on and opened against the `master` branch.
|
||||
|
||||
## Compiling instructions
|
||||
Download Go from https://golang.org/dl/
|
||||
Using a terminal, type go get github.com/thrasher-/gocryptotrader
|
||||
Change directory to the package directory, then type go install.
|
||||
Copy config_example.dat to config.dat.
|
||||
Make any neccessary changes to the config file.
|
||||
Run the application!
|
||||
|
||||
Download and install Go from [Go Downloads](https://golang.org/dl/)
|
||||
|
||||
```
|
||||
go get github.com/thrasher-/gocryptotrader
|
||||
cd $GOPATH/src/github.com/thrasher-/gocryptotrader
|
||||
go install
|
||||
cp $GOPATH/src/github.com/thrasher-/gocryptotrader/config_example.dat $GOPATH/bin/config.dat
|
||||
```
|
||||
|
||||
Make any neccessary changes to the config file.
|
||||
Run the application!
|
||||
|
||||
## Donations
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to: 1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB
|
||||
|
||||
## Binaries
|
||||
|
||||
Binaries will be published once the codebase reaches a stable condition.
|
||||
|
||||
133
common/common.go
133
common/common.go
@@ -1,7 +1,6 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
//"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
@@ -21,56 +20,63 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Const declarations for common.go operations
|
||||
const (
|
||||
HASH_SHA1 = iota
|
||||
HASH_SHA256
|
||||
HASH_SHA512
|
||||
HASH_SHA512_384
|
||||
SATOSHIS_PER_BTC = 100000000
|
||||
SATOSHIS_PER_LTC = 100000000
|
||||
WEI_PER_ETHER = 1000000000000000000
|
||||
HashSHA1 = iota
|
||||
HashSHA256
|
||||
HashSHA512
|
||||
HashSHA512_384
|
||||
SatoshisPerBTC = 100000000
|
||||
SatoshisPerLTC = 100000000
|
||||
WeiPerEther = 1000000000000000000
|
||||
)
|
||||
|
||||
// GetMD5 returns a MD5 hash of a byte array
|
||||
func GetMD5(input []byte) []byte {
|
||||
hash := md5.New()
|
||||
hash.Write(input)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
// GetSHA512 returns a SHA512 hash of a byte array
|
||||
func GetSHA512(input []byte) []byte {
|
||||
sha := sha512.New()
|
||||
sha.Write(input)
|
||||
return sha.Sum(nil)
|
||||
}
|
||||
|
||||
// GetSHA256 returns a SHA256 hash of a byte array
|
||||
func GetSHA256(input []byte) []byte {
|
||||
sha := sha256.New()
|
||||
sha.Write(input)
|
||||
return sha.Sum(nil)
|
||||
}
|
||||
|
||||
// GetHMAC returns a keyed-hash message authentication code using the desired
|
||||
// hashtype
|
||||
func GetHMAC(hashType int, input, key []byte) []byte {
|
||||
var hash func() hash.Hash
|
||||
|
||||
switch hashType {
|
||||
case HASH_SHA1:
|
||||
case HashSHA1:
|
||||
{
|
||||
hash = sha1.New
|
||||
}
|
||||
case HASH_SHA256:
|
||||
case HashSHA256:
|
||||
{
|
||||
hash = sha256.New
|
||||
}
|
||||
case HASH_SHA512:
|
||||
case HashSHA512:
|
||||
{
|
||||
hash = sha512.New
|
||||
}
|
||||
case HASH_SHA512_384:
|
||||
case HashSHA512_384:
|
||||
{
|
||||
hash = sha512.New384
|
||||
}
|
||||
@@ -81,10 +87,12 @@ func GetHMAC(hashType int, input, key []byte) []byte {
|
||||
return hmac.Sum(nil)
|
||||
}
|
||||
|
||||
// HexEncodeToString takes in a hexadecimal byte array and returns a string
|
||||
func HexEncodeToString(input []byte) string {
|
||||
return hex.EncodeToString(input)
|
||||
}
|
||||
|
||||
// Base64Decode takes in a Base64 string and returns a byte array and an error
|
||||
func Base64Decode(input string) ([]byte, error) {
|
||||
result, err := base64.StdEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
@@ -93,10 +101,13 @@ func Base64Decode(input string) ([]byte, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Base64Encode takes in a byte array then returns an encoded base64 string
|
||||
func Base64Encode(input []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(input)
|
||||
}
|
||||
|
||||
// StringSliceDifference concatenates slices together based on its index and
|
||||
// returns an individual string array
|
||||
func StringSliceDifference(slice1 []string, slice2 []string) []string {
|
||||
var diff []string
|
||||
for i := 0; i < 2; i++ {
|
||||
@@ -119,35 +130,51 @@ func StringSliceDifference(slice1 []string, slice2 []string) []string {
|
||||
return diff
|
||||
}
|
||||
|
||||
// StringContains checks a substring if it contains your input then returns a
|
||||
// bool
|
||||
func StringContains(input, substring string) bool {
|
||||
return strings.Contains(input, substring)
|
||||
}
|
||||
|
||||
// DataContains checks the substring array with an input and returns a bool
|
||||
func DataContains(haystack []string, needle string) bool {
|
||||
data := strings.Join(haystack, ",")
|
||||
return strings.Contains(data, needle)
|
||||
}
|
||||
|
||||
func JoinStrings(input []string, seperator string) string {
|
||||
return strings.Join(input, seperator)
|
||||
// JoinStrings joins an array together with the required separator and returns
|
||||
// it as a string
|
||||
func JoinStrings(input []string, separator string) string {
|
||||
return strings.Join(input, separator)
|
||||
}
|
||||
|
||||
func SplitStrings(input, seperator string) []string {
|
||||
return strings.Split(input, seperator)
|
||||
// SplitStrings splits blocks of strings from string into a string array using
|
||||
// a separator ie "," or "_"
|
||||
func SplitStrings(input, separator string) []string {
|
||||
return strings.Split(input, separator)
|
||||
}
|
||||
|
||||
// TrimString trims unwanted prefixes or postfixes
|
||||
func TrimString(input, cutset string) string {
|
||||
return strings.Trim(input, cutset)
|
||||
}
|
||||
|
||||
// ReplaceString replaces a string with another
|
||||
func ReplaceString(input, old, new string, n int) string {
|
||||
return strings.Replace(input, old, new, n)
|
||||
}
|
||||
|
||||
// StringToUpper changes strings to uppercase
|
||||
func StringToUpper(input string) string {
|
||||
return strings.ToUpper(input)
|
||||
}
|
||||
|
||||
// StringToLower changes strings to lowercase
|
||||
func StringToLower(input string) string {
|
||||
return strings.ToLower(input)
|
||||
}
|
||||
|
||||
// RoundFloat rounds your floating point number to the desired decimal place
|
||||
func RoundFloat(x float64, prec int) float64 {
|
||||
var rounder float64
|
||||
pow := math.Pow(10, float64(prec))
|
||||
@@ -157,7 +184,7 @@ func RoundFloat(x float64, prec int) float64 {
|
||||
x = .5
|
||||
if frac < 0.0 {
|
||||
x = -.5
|
||||
intermed -= 1
|
||||
intermed--
|
||||
}
|
||||
if frac >= x {
|
||||
rounder = math.Ceil(intermed)
|
||||
@@ -168,14 +195,32 @@ func RoundFloat(x float64, prec int) float64 {
|
||||
return rounder / pow
|
||||
}
|
||||
|
||||
// IsEnabled takes in a boolean param and returns a string if it is enabled
|
||||
// or disabled
|
||||
func IsEnabled(isEnabled bool) string {
|
||||
if isEnabled {
|
||||
return "Enabled"
|
||||
} else {
|
||||
return "Disabled"
|
||||
}
|
||||
return "Disabled"
|
||||
}
|
||||
|
||||
// IsValidCryptoAddress validates your cryptocurrency address string using the
|
||||
// regexp package // Validation issues occurring because "3" is contained in
|
||||
// litecoin and Bitcoin addresses - non-fatal
|
||||
func IsValidCryptoAddress(address, crypto string) (bool, error) {
|
||||
switch StringToLower(crypto) {
|
||||
case "btc":
|
||||
return regexp.MatchString("^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$", address)
|
||||
case "ltc":
|
||||
return regexp.MatchString("^[L3M][a-km-zA-HJ-NP-Z1-9]{25,34}$", address)
|
||||
case "eth":
|
||||
return regexp.MatchString("^0x[a-km-z0-9]{40}$", address)
|
||||
default:
|
||||
return false, errors.New("Invalid crypto currency")
|
||||
}
|
||||
}
|
||||
|
||||
// YesOrNo returns a boolean variable to check if input is "y" or "yes"
|
||||
func YesOrNo(input string) bool {
|
||||
if StringToLower(input) == "y" || StringToLower(input) == "yes" {
|
||||
return true
|
||||
@@ -183,31 +228,40 @@ func YesOrNo(input string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// CalculateAmountWithFee returns a calculated fee included amount on fee
|
||||
func CalculateAmountWithFee(amount, fee float64) float64 {
|
||||
return amount + CalculateFee(amount, fee)
|
||||
}
|
||||
|
||||
// CalculateFee returns a simple fee on amount
|
||||
func CalculateFee(amount, fee float64) float64 {
|
||||
return amount * (fee / 100)
|
||||
}
|
||||
|
||||
// CalculatePercentageGainOrLoss returns the percentage rise over a certain
|
||||
// period
|
||||
func CalculatePercentageGainOrLoss(priceNow, priceThen float64) float64 {
|
||||
return (priceNow - priceThen) / priceThen * 100
|
||||
}
|
||||
|
||||
// CalculatePercentageDifference returns the percentage of difference between
|
||||
// multiple time periods
|
||||
func CalculatePercentageDifference(amount, secondAmount float64) float64 {
|
||||
return (amount - secondAmount) / ((amount + secondAmount) / 2) * 100
|
||||
}
|
||||
|
||||
// CalculateNetProfit returns net profit
|
||||
func CalculateNetProfit(amount, priceThen, priceNow, costs float64) float64 {
|
||||
return (priceNow * amount) - (priceThen * amount) - costs
|
||||
}
|
||||
|
||||
// SendHTTPRequest sends a request using the http package and returns a response
|
||||
// as a string and an error
|
||||
func SendHTTPRequest(method, path string, headers map[string]string, body io.Reader) (string, error) {
|
||||
result := strings.ToUpper(method)
|
||||
|
||||
if result != "POST" && result != "GET" && result != "DELETE" {
|
||||
return "", errors.New("Invalid HTTP method specified.")
|
||||
return "", errors.New("invalid HTTP method specified")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, path, body)
|
||||
@@ -237,20 +291,22 @@ func SendHTTPRequest(method, path string, headers map[string]string, body io.Rea
|
||||
return string(contents), nil
|
||||
}
|
||||
|
||||
func SendHTTPGetRequest(url string, jsonDecode bool, result interface{}) (err error) {
|
||||
// SendHTTPGetRequest sends a simple get request using a url string & JSON
|
||||
// decodes the response into a struct pointer you have supplied. Returns an error
|
||||
// on failure.
|
||||
func SendHTTPGetRequest(url string, jsonDecode bool, result interface{}) error {
|
||||
res, err := http.Get(url)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
log.Printf("HTTP status code: %d\n", res.StatusCode)
|
||||
return errors.New("Status code was not 200.")
|
||||
log.Printf("URL: %s\n", url)
|
||||
return errors.New("status code was not 200")
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadAll(res.Body)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -270,14 +326,18 @@ func SendHTTPGetRequest(url string, jsonDecode bool, result interface{}) (err er
|
||||
return nil
|
||||
}
|
||||
|
||||
// JSONEncode encodes structure data into JSON
|
||||
func JSONEncode(v interface{}) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
// JSONDecode decodes JSON data into a structure
|
||||
func JSONDecode(data []byte, to interface{}) error {
|
||||
return json.Unmarshal(data, to)
|
||||
}
|
||||
|
||||
// EncodeURLValues concatenates url values onto a url string and returns a
|
||||
// string
|
||||
func EncodeURLValues(url string, values url.Values) string {
|
||||
path := url
|
||||
if len(values) > 0 {
|
||||
@@ -286,6 +346,7 @@ func EncodeURLValues(url string, values url.Values) string {
|
||||
return path
|
||||
}
|
||||
|
||||
// ExtractHost returns the hostname out of a string
|
||||
func ExtractHost(address string) string {
|
||||
host := SplitStrings(address, ":")[0]
|
||||
if host == "" {
|
||||
@@ -294,13 +355,23 @@ func ExtractHost(address string) string {
|
||||
return host
|
||||
}
|
||||
|
||||
// ExtractPort returns the port name out of a string
|
||||
func ExtractPort(host string) int {
|
||||
portStr := SplitStrings(host, ":")[1]
|
||||
port, _ := strconv.Atoi(portStr)
|
||||
return port
|
||||
}
|
||||
|
||||
// OutputCSV dumps data into a file as comma-separated values
|
||||
func OutputCSV(path string, data [][]string) error {
|
||||
_, err := ReadFile(path)
|
||||
if err != nil {
|
||||
errTwo := WriteFile(path, nil)
|
||||
if errTwo != nil {
|
||||
return errTwo
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -313,14 +384,17 @@ func OutputCSV(path string, data [][]string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
defer writer.Flush()
|
||||
writer.Flush()
|
||||
file.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnixTimestampToTime returns time.time
|
||||
func UnixTimestampToTime(timeint64 int64) time.Time {
|
||||
return time.Unix(timeint64, 0)
|
||||
}
|
||||
|
||||
// UnixTimestampStrToTime returns a time.time and an error
|
||||
func UnixTimestampStrToTime(timeStr string) (time.Time, error) {
|
||||
i, err := strconv.ParseInt(timeStr, 10, 64)
|
||||
if err != nil {
|
||||
@@ -330,6 +404,7 @@ func UnixTimestampStrToTime(timeStr string) (time.Time, error) {
|
||||
return time.Unix(i, 0), nil
|
||||
}
|
||||
|
||||
// ReadFile reads a file and returns read data as byte array.
|
||||
func ReadFile(path string) ([]byte, error) {
|
||||
file, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
@@ -338,6 +413,7 @@ func ReadFile(path string) ([]byte, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// WriteFile writes selected data to a file and returns an error
|
||||
func WriteFile(file string, data []byte) error {
|
||||
err := ioutil.WriteFile(file, data, 0644)
|
||||
if err != nil {
|
||||
@@ -346,7 +422,12 @@ func WriteFile(file string, data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetURIPath returns the path of a URL given a URL
|
||||
// RemoveFile removes a file
|
||||
func RemoveFile(file string) error {
|
||||
return os.Remove(file)
|
||||
}
|
||||
|
||||
// GetURIPath returns the path of a URL given a URI
|
||||
func GetURIPath(uri string) string {
|
||||
urip, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,7 +3,9 @@ package common
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -13,13 +15,63 @@ func TestIsEnabled(t *testing.T) {
|
||||
expected := "Enabled"
|
||||
actual := IsEnabled(true)
|
||||
if actual != expected {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected %s. Actual %s", expected, actual),
|
||||
)
|
||||
}
|
||||
|
||||
expected = "Disabled"
|
||||
actual = IsEnabled(false)
|
||||
if actual != expected {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected %s. Actual %s", expected, actual),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidCryptoAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
b, err := IsValidCryptoAddress("1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "bTC")
|
||||
if err != nil && !b {
|
||||
t.Errorf("Test Failed - Common IsValidCryptoAddress error: %s", err)
|
||||
}
|
||||
b, err = IsValidCryptoAddress("0Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "btc")
|
||||
if err == nil && b {
|
||||
t.Error("Test Failed - Common IsValidCryptoAddress error")
|
||||
}
|
||||
b, err = IsValidCryptoAddress("1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "lTc")
|
||||
if err == nil && b {
|
||||
t.Error("Test Failed - Common IsValidCryptoAddress error")
|
||||
}
|
||||
b, err = IsValidCryptoAddress("3CDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj", "ltc")
|
||||
if err != nil && !b {
|
||||
t.Errorf("Test Failed - Common IsValidCryptoAddress error: %s", err)
|
||||
}
|
||||
b, err = IsValidCryptoAddress("NCDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj", "lTc")
|
||||
if err == nil && b {
|
||||
t.Error("Test Failed - Common IsValidCryptoAddress error")
|
||||
}
|
||||
b, err = IsValidCryptoAddress(
|
||||
"0xb794f5ea0ba39494ce839613fffba74279579268",
|
||||
"eth",
|
||||
)
|
||||
if err != nil && b {
|
||||
t.Errorf("Test Failed - Common IsValidCryptoAddress error: %s", err)
|
||||
}
|
||||
b, err = IsValidCryptoAddress(
|
||||
"xxb794f5ea0ba39494ce839613fffba74279579268",
|
||||
"eTh",
|
||||
)
|
||||
if err == nil && b {
|
||||
t.Error("Test Failed - Common IsValidCryptoAddress error")
|
||||
}
|
||||
b, err = IsValidCryptoAddress(
|
||||
"xxb794f5ea0ba39494ce839613fffba74279579268",
|
||||
"ding",
|
||||
)
|
||||
if err == nil && b {
|
||||
t.Error("Test Failed - Common IsValidCryptoAddress error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +82,10 @@ func TestGetMD5(t *testing.T) {
|
||||
actualOutput := GetMD5(originalString)
|
||||
actualStr := HexEncodeToString(actualOutput)
|
||||
if !bytes.Equal(expectedOutput, []byte(actualStr)) {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, []byte(actualStr)))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'",
|
||||
expectedOutput, []byte(actualStr)),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,22 +93,77 @@ func TestGetMD5(t *testing.T) {
|
||||
func TestGetSHA512(t *testing.T) {
|
||||
t.Parallel()
|
||||
var originalString = []byte("I am testing the GetSHA512 function in common!")
|
||||
var expectedOutput = []byte("a2273f492ea73fddc4f25c267b34b3b74998bd8a6301149e1e1c835678e3c0b90859fce22e4e7af33bde1711cbb924809aedf5d759d648d61774b7185c5dc02b")
|
||||
var expectedOutput = []byte(
|
||||
`a2273f492ea73fddc4f25c267b34b3b74998bd8a6301149e1e1c835678e3c0b90859fce22e4e7af33bde1711cbb924809aedf5d759d648d61774b7185c5dc02b`,
|
||||
)
|
||||
actualOutput := GetSHA512(originalString)
|
||||
actualStr := HexEncodeToString(actualOutput)
|
||||
if !bytes.Equal(expectedOutput, []byte(actualStr)) {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'",
|
||||
expectedOutput, []byte(actualStr)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSHA256(t *testing.T) {
|
||||
t.Parallel()
|
||||
var originalString = []byte("I am testing the GetSHA256 function in common!")
|
||||
var expectedOutput = []byte("0962813d7a9f739cdcb7f0c0be0c2a13bd630167e6e54468266e4af6b1ad9303")
|
||||
var expectedOutput = []byte(
|
||||
"0962813d7a9f739cdcb7f0c0be0c2a13bd630167e6e54468266e4af6b1ad9303",
|
||||
)
|
||||
actualOutput := GetSHA256(originalString)
|
||||
actualStr := HexEncodeToString(actualOutput)
|
||||
if !bytes.Equal(expectedOutput, []byte(actualStr)) {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'",
|
||||
expectedOutput, []byte(actualStr)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHMAC(t *testing.T) {
|
||||
expectedSha1 := []byte{
|
||||
74, 253, 245, 154, 87, 168, 110, 182, 172, 101, 177, 49, 142, 2, 253, 165,
|
||||
100, 66, 86, 246,
|
||||
}
|
||||
expectedsha256 := []byte{
|
||||
54, 68, 6, 12, 32, 158, 80, 22, 142, 8, 131, 111, 248, 145, 17, 202, 224,
|
||||
59, 135, 206, 11, 170, 154, 197, 183, 28, 150, 79, 168, 105, 62, 102,
|
||||
}
|
||||
expectedsha512 := []byte{
|
||||
249, 212, 31, 38, 23, 3, 93, 220, 81, 209, 214, 112, 92, 75, 126, 40, 109,
|
||||
95, 247, 182, 210, 54, 217, 224, 199, 252, 129, 226, 97, 201, 245, 220, 37,
|
||||
201, 240, 15, 137, 236, 75, 6, 97, 12, 190, 31, 53, 153, 223, 17, 214, 11,
|
||||
153, 203, 49, 29, 158, 217, 204, 93, 179, 109, 140, 216, 202, 71,
|
||||
}
|
||||
expectedsha512384 := []byte{
|
||||
121, 203, 109, 105, 178, 68, 179, 57, 21, 217, 76, 82, 94, 100, 210, 1, 55,
|
||||
201, 8, 232, 194, 168, 165, 58, 192, 26, 193, 167, 254, 183, 172, 4, 189,
|
||||
158, 158, 150, 173, 33, 119, 125, 94, 13, 125, 89, 241, 184, 166, 128,
|
||||
}
|
||||
|
||||
sha1 := GetHMAC(HashSHA1, []byte("Hello,World"), []byte("1234"))
|
||||
if string(sha1) != string(expectedSha1) {
|
||||
t.Errorf("Test failed.Common GetHMAC error: Expected '%x'. Actual '%x'",
|
||||
expectedSha1, sha1,
|
||||
)
|
||||
}
|
||||
sha256 := GetHMAC(HashSHA256, []byte("Hello,World"), []byte("1234"))
|
||||
if string(sha256) != string(expectedsha256) {
|
||||
t.Errorf("Test failed.Common GetHMAC error: Expected '%x'. Actual '%x'",
|
||||
expectedSha1, sha1,
|
||||
)
|
||||
}
|
||||
sha512 := GetHMAC(HashSHA512, []byte("Hello,World"), []byte("1234"))
|
||||
if string(sha512) != string(expectedsha512) {
|
||||
t.Errorf("Test failed.Common GetHMAC error: Expected '%x'. Actual '%x'",
|
||||
expectedSha1, sha1,
|
||||
)
|
||||
}
|
||||
sha512384 := GetHMAC(HashSHA512_384, []byte("Hello,World"), []byte("1234"))
|
||||
if string(sha512384) != string(expectedsha512384) {
|
||||
t.Errorf("Test failed.Common GetHMAC error: Expected '%x'. Actual '%x'",
|
||||
expectedSha1, sha1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +193,9 @@ func TestHexEncodeToString(t *testing.T) {
|
||||
expectedOutput := "737472696e67"
|
||||
actualResult := HexEncodeToString(originalInput)
|
||||
if actualResult != expectedOutput {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'",
|
||||
expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +205,14 @@ func TestBase64Decode(t *testing.T) {
|
||||
expectedOutput := []byte("hello")
|
||||
actualResult, err := Base64Decode(originalInput)
|
||||
if !bytes.Equal(actualResult, expectedOutput) {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'. Error: %s", expectedOutput, actualResult, err))
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'. Error: %s",
|
||||
expectedOutput, actualResult, err),
|
||||
)
|
||||
}
|
||||
|
||||
_, err = Base64Decode("-")
|
||||
if err == nil {
|
||||
t.Error("Test failed. Bad base64 string failed returned nil error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,18 +222,22 @@ func TestBase64Encode(t *testing.T) {
|
||||
expectedOutput := "aGVsbG8="
|
||||
actualResult := Base64Encode(originalInput)
|
||||
if actualResult != expectedOutput {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceDifference(t *testing.T) {
|
||||
t.Parallel()
|
||||
originalInputOne := []string{"hello"}
|
||||
originalInputTwo := []string{"moto"}
|
||||
originalInputTwo := []string{"hello", "moto"}
|
||||
expectedOutput := []string{"hello moto"}
|
||||
actualResult := StringSliceDifference(originalInputOne, originalInputTwo)
|
||||
if reflect.DeepEqual(expectedOutput, actualResult) {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +248,9 @@ func TestStringContains(t *testing.T) {
|
||||
expectedOutput := true
|
||||
actualResult := StringContains(originalInput, originalInputSubstring)
|
||||
if actualResult != expectedOutput {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,44 +263,106 @@ func TestDataContains(t *testing.T) {
|
||||
expectedOutputTwo := false
|
||||
actualResult := DataContains(originalHaystack, originalNeedle)
|
||||
if actualResult != expectedOutput {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
actualResult = DataContains(originalHaystack, anotherNeedle)
|
||||
if actualResult != expectedOutputTwo {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutputTwo, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%t'. Actual '%t'", expectedOutputTwo,
|
||||
actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinStrings(t *testing.T) {
|
||||
t.Parallel()
|
||||
originalInputOne := []string{"hello", "moto"}
|
||||
seperator := ","
|
||||
separator := ","
|
||||
expectedOutput := "hello,moto"
|
||||
actualResult := JoinStrings(originalInputOne, seperator)
|
||||
actualResult := JoinStrings(originalInputOne, separator)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitStrings(t *testing.T) {
|
||||
t.Parallel()
|
||||
originalInputOne := "hello,moto"
|
||||
seperator := ","
|
||||
separator := ","
|
||||
expectedOutput := []string{"hello", "moto"}
|
||||
actualResult := SplitStrings(originalInputOne, seperator)
|
||||
actualResult := SplitStrings(originalInputOne, separator)
|
||||
if !reflect.DeepEqual(expectedOutput, actualResult) {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrimString(t *testing.T) {
|
||||
t.Parallel()
|
||||
originalInput := "abcd"
|
||||
cutset := "ad"
|
||||
expectedOutput := "bc"
|
||||
actualResult := TrimString(originalInput, cutset)
|
||||
if expectedOutput != actualResult {
|
||||
t.Errorf(
|
||||
"Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ReplaceString replaces a string with another
|
||||
func TestReplaceString(t *testing.T) {
|
||||
t.Parallel()
|
||||
currency := "BTC-USD"
|
||||
expectedOutput := "BTCUSD"
|
||||
|
||||
actualResult := ReplaceString(currency, "-", "", -1)
|
||||
if expectedOutput != actualResult {
|
||||
t.Errorf(
|
||||
"Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult,
|
||||
)
|
||||
}
|
||||
|
||||
currency = "BTC-USD--"
|
||||
actualResult = ReplaceString(currency, "-", "", 3)
|
||||
if expectedOutput != actualResult {
|
||||
t.Errorf(
|
||||
"Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundFloat(t *testing.T) {
|
||||
t.Parallel()
|
||||
originalInput := float64(1.4545445445)
|
||||
precisionInput := 2
|
||||
expectedOutput := float64(1.45)
|
||||
actualResult := RoundFloat(originalInput, precisionInput)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
|
||||
// mapping of input vs expected result
|
||||
testTable := map[float64]float64{
|
||||
2.3232323: 2.32,
|
||||
-2.3232323: -2.32,
|
||||
}
|
||||
for testInput, expectedOutput := range testTable {
|
||||
actualOutput := RoundFloat(testInput, 2)
|
||||
if actualOutput != expectedOutput {
|
||||
t.Error(fmt.Sprintf("Test failed. RoundFloat Expected '%f'. Actual '%f'.",
|
||||
expectedOutput, actualOutput))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestYesOrNo(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !YesOrNo("y") {
|
||||
t.Error("Test failed - Common YesOrNo Error.")
|
||||
}
|
||||
if !YesOrNo("yes") {
|
||||
t.Error("Test failed - Common YesOrNo Error.")
|
||||
}
|
||||
if YesOrNo("ding") {
|
||||
t.Error("Test failed - Common YesOrNo Error.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +373,9 @@ func TestCalculateFee(t *testing.T) {
|
||||
expectedOutput := float64(0.01)
|
||||
actualResult := CalculateFee(originalInput, fee)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +386,9 @@ func TestCalculateAmountWithFee(t *testing.T) {
|
||||
expectedOutput := float64(1.01)
|
||||
actualResult := CalculateAmountWithFee(originalInput, fee)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +399,9 @@ func TestCalculatePercentageGainOrLoss(t *testing.T) {
|
||||
expectedOutput := 3.3333333333333335
|
||||
actualResult := CalculatePercentageGainOrLoss(originalInput, secondInput)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +412,9 @@ func TestCalculatePercentageDifference(t *testing.T) {
|
||||
expectedOutput := 66.66666666666666
|
||||
actualResult := CalculatePercentageDifference(originalInput, secondAmount)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,24 +427,151 @@ func TestCalculateNetProfit(t *testing.T) {
|
||||
expectedOutput := float64(44)
|
||||
actualResult := CalculateNetProfit(amount, priceThen, priceNow, costs)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendHTTPRequest(t *testing.T) {
|
||||
methodPost := "pOst"
|
||||
methodGet := "GeT"
|
||||
methodDelete := "dEleTe"
|
||||
methodGarbage := "ding"
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
_, err := SendHTTPRequest(
|
||||
methodGarbage, "https://query.yahooapis.com/v1/public/yql", headers,
|
||||
strings.NewReader(""),
|
||||
)
|
||||
if err == nil {
|
||||
t.Error("Test failed. ")
|
||||
}
|
||||
_, err = SendHTTPRequest(
|
||||
methodPost, "https://query.yahooapis.com/v1/public/yql", headers,
|
||||
strings.NewReader(""),
|
||||
)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. %s ", err)
|
||||
}
|
||||
_, err = SendHTTPRequest(
|
||||
methodGet, "https://query.yahooapis.com/v1/public/yql", headers,
|
||||
strings.NewReader(""),
|
||||
)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. %s ", err)
|
||||
}
|
||||
_, err = SendHTTPRequest(
|
||||
methodDelete, "https://query.yahooapis.com/v1/public/yql", headers,
|
||||
strings.NewReader(""),
|
||||
)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. %s ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendHTTPGetRequest(t *testing.T) {
|
||||
type test struct {
|
||||
Status int `json:"status"`
|
||||
Data []struct {
|
||||
Address string `json:"address"`
|
||||
Balance float64 `json:"balance"`
|
||||
Nonce interface{} `json:"nonce"`
|
||||
Code string `json:"code"`
|
||||
Name interface{} `json:"name"`
|
||||
Storage interface{} `json:"storage"`
|
||||
FirstSeen interface{} `json:"firstSeen"`
|
||||
} `json:"data"`
|
||||
}
|
||||
url := `https://etherchain.org/api/account/multiple/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe`
|
||||
result := test{}
|
||||
|
||||
err := SendHTTPGetRequest(url, true, &result)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed - common SendHTTPGetRequest error: %s", err)
|
||||
}
|
||||
err = SendHTTPGetRequest("DINGDONG", true, &result)
|
||||
if err == nil {
|
||||
t.Error("Test failed - common SendHTTPGetRequest error")
|
||||
}
|
||||
err = SendHTTPGetRequest(url, false, &result)
|
||||
if err != nil {
|
||||
t.Error("Test failed - common SendHTTPGetRequest error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONEncode(t *testing.T) {
|
||||
type test struct {
|
||||
Status int `json:"status"`
|
||||
Data []struct {
|
||||
Address string `json:"address"`
|
||||
Balance float64 `json:"balance"`
|
||||
Nonce interface{} `json:"nonce"`
|
||||
Code string `json:"code"`
|
||||
Name interface{} `json:"name"`
|
||||
Storage interface{} `json:"storage"`
|
||||
FirstSeen interface{} `json:"firstSeen"`
|
||||
} `json:"data"`
|
||||
}
|
||||
expectOutputString := `{"status":0,"data":null}`
|
||||
v := test{}
|
||||
|
||||
bitey, err := JSONEncode(v)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed - common JSONEncode error: %s", err)
|
||||
}
|
||||
if string(bitey) != expectOutputString {
|
||||
t.Error("Test failed - common JSONEncode error")
|
||||
}
|
||||
_, err = JSONEncode("WigWham")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed - common JSONEncode error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeURLValues(t *testing.T) {
|
||||
urlstring := "https://www.test.com"
|
||||
expectedOutput := `https://www.test.com?env=TEST%2FDATABASE&format=json&q=SELECT+%2A+from+yahoo.finance.xchange+WHERE+pair+in+%28%22BTC%2CUSD%22%29`
|
||||
values := url.Values{}
|
||||
values.Set("q", fmt.Sprintf(
|
||||
"SELECT * from yahoo.finance.xchange WHERE pair in (\"%s\")", "BTC,USD"),
|
||||
)
|
||||
values.Set("format", "json")
|
||||
values.Set("env", "TEST/DATABASE")
|
||||
|
||||
output := EncodeURLValues(urlstring, values)
|
||||
if output != expectedOutput {
|
||||
t.Error("Test Failed - common EncodeURLValues error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractHost(t *testing.T) {
|
||||
t.Parallel()
|
||||
address := "localhost:1337"
|
||||
addresstwo := ":1337"
|
||||
expectedOutput := "localhost"
|
||||
actualResult := ExtractHost(address)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
actualResultTwo := ExtractHost(addresstwo)
|
||||
if expectedOutput != actualResultTwo {
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
|
||||
address = "192.168.1.100:1337"
|
||||
expectedOutput = "192.168.1.100"
|
||||
actualResult = ExtractHost(address)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +581,23 @@ func TestExtractPort(t *testing.T) {
|
||||
expectedOutput := 1337
|
||||
actualResult := ExtractPort(address)
|
||||
if expectedOutput != actualResult {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%d'. Actual '%d'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%d'. Actual '%d'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputCSV(t *testing.T) {
|
||||
path := "../testdata/dump"
|
||||
data := [][]string{}
|
||||
rowOne := []string{"Appended", "to", "two", "dimensional", "array"}
|
||||
rowTwo := []string{"Appended", "to", "two", "dimensional", "array", "two"}
|
||||
data = append(data, rowOne)
|
||||
data = append(data, rowTwo)
|
||||
|
||||
err := OutputCSV(path, data)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed - common OutputCSV error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,21 +608,76 @@ func TestUnixTimestampToTime(t *testing.T) {
|
||||
expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
|
||||
actualResult := UnixTimestampToTime(testTime)
|
||||
if tm.String() != actualResult.String() {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTimestampStrToTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
testTime := "1489439831"
|
||||
incorrectTime := "DINGDONG"
|
||||
expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
|
||||
actualResult, err := UnixTimestampStrToTime(testTime)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if actualResult.UTC().String() != expectedOutput {
|
||||
t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
|
||||
t.Error(fmt.Sprintf(
|
||||
"Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult),
|
||||
)
|
||||
}
|
||||
actualResult, err = UnixTimestampStrToTime(incorrectTime)
|
||||
if err == nil {
|
||||
t.Error("Test failed. Common UnixTimestampStrToTime error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
pathCorrect := "../testdata/dump"
|
||||
pathIncorrect := "testdata/dump"
|
||||
|
||||
_, err := ReadFile(pathCorrect)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed - Common ReadFile error: %s", err)
|
||||
}
|
||||
_, err = ReadFile(pathIncorrect)
|
||||
if err == nil {
|
||||
t.Errorf("Test failed - Common ReadFile error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteFile(t *testing.T) {
|
||||
path := "../testdata/writefiletest"
|
||||
err := WriteFile(path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. Common WriteFile error: %s", err)
|
||||
}
|
||||
_, err = ReadFile(path)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. Common WriteFile error: %s", err)
|
||||
}
|
||||
|
||||
err = WriteFile("", nil)
|
||||
if err == nil {
|
||||
t.Error("Test failed. Common WriteFile allowed bad path")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveFile(t *testing.T) {
|
||||
TestWriteFile(t)
|
||||
path := "../testdata/writefiletest"
|
||||
err := RemoveFile(path)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. Common RemoveFile error: %s", err)
|
||||
}
|
||||
|
||||
TestOutputCSV(t)
|
||||
path = "../testdata/dump"
|
||||
err = RemoveFile(path)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. Common RemoveFile error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,9 +685,9 @@ func TestGetURIPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
// mapping of input vs expected result
|
||||
testTable := map[string]string{
|
||||
"https://api.gdax.com/accounts": "/accounts",
|
||||
"https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2",
|
||||
"ht:tp:/invalidurl": "",
|
||||
"https://api.gdax.com/accounts": "/accounts",
|
||||
"https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2",
|
||||
"http://www.google.com/accounts?!@#$%;^^": "",
|
||||
}
|
||||
for testInput, expectedOutput := range testTable {
|
||||
actualOutput := GetURIPath(testInput)
|
||||
|
||||
249
config/config.go
249
config/config.go
@@ -3,6 +3,7 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
@@ -13,18 +14,20 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/portfolio"
|
||||
"github.com/thrasher-/gocryptotrader/smsglobal"
|
||||
)
|
||||
|
||||
// Constants declared here are filename strings and test strings
|
||||
const (
|
||||
CONFIG_FILE = "config.dat"
|
||||
OLD_CONFIG_FILE = "config.json"
|
||||
CONFIG_TEST_FILE = "../testdata/configtest.dat"
|
||||
|
||||
CONFIG_FILE_ENCRYPTION_PROMPT = 0
|
||||
CONFIG_FILE_ENCRYPTION_ENABLED = 1
|
||||
CONFIG_FILE_ENCRYPTION_DISABLED = -1
|
||||
ConfigFile = "config.dat"
|
||||
OldConfigFile = "config.json"
|
||||
ConfigTestFile = "../testdata/configtest.dat"
|
||||
configFileEncryptionPrompt = 0
|
||||
configFileEncryptionEnabled = 1
|
||||
configFileEncryptionDisabled = -1
|
||||
)
|
||||
|
||||
// Variables here are mainly alerts and a configuration object
|
||||
var (
|
||||
ErrExchangeNameEmpty = "Exchange #%d in config: Exchange name is empty."
|
||||
ErrExchangeAvailablePairsEmpty = "Exchange %s: Available pairs is empty."
|
||||
@@ -43,57 +46,78 @@ var (
|
||||
WarningWebserverListenAddressInvalid = "WARNING -- Webserver support disabled due to invalid listen address."
|
||||
WarningWebserverRootWebFolderNotFound = "WARNING -- Webserver support disabled due to missing web folder."
|
||||
WarningExchangeAuthAPIDefaultOrEmptyValues = "WARNING -- Exchange %s: Authenticated API support disabled due to default/empty APIKey/Secret/ClientID values."
|
||||
WarningCurrencyExchangeProvider = "WARNING -- Currency exchange provider invalid valid. Reset to Fixer."
|
||||
RenamingConfigFile = "Renaming config file %s to %s."
|
||||
Cfg Config
|
||||
)
|
||||
|
||||
// WebserverConfig struct holds the prestart variables for the webserver.
|
||||
type WebserverConfig struct {
|
||||
Enabled bool
|
||||
AdminUsername string
|
||||
AdminPassword string
|
||||
ListenAddress string
|
||||
Enabled bool
|
||||
AdminUsername string
|
||||
AdminPassword string
|
||||
ListenAddress string
|
||||
WebsocketConnectionLimit int
|
||||
WebsocketAllowInsecureOrigin bool
|
||||
}
|
||||
|
||||
// SMSGlobalConfig structure holds all the variables you need for instant
|
||||
// messaging and broadcast used by SMSGlobal
|
||||
type SMSGlobalConfig struct {
|
||||
Enabled bool
|
||||
Username string
|
||||
Password string
|
||||
Contacts []struct {
|
||||
Name string
|
||||
Number string
|
||||
Enabled bool
|
||||
}
|
||||
Contacts []smsglobal.Contact
|
||||
}
|
||||
|
||||
type ConfigPost struct {
|
||||
// Post holds the bot configuration data
|
||||
type Post struct {
|
||||
Data Config `json:"Data"`
|
||||
}
|
||||
|
||||
// CurrencyPairFormatConfig stores the users preferred currency pair display
|
||||
type CurrencyPairFormatConfig struct {
|
||||
Uppercase bool
|
||||
Delimiter string `json:",omitempty"`
|
||||
Separator string `json:",omitempty"`
|
||||
Index string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Config is the overarching object that holds all the information for
|
||||
// prestart management of portfolio, SMSGlobal, webserver and enabled exchange
|
||||
type Config struct {
|
||||
Name string
|
||||
EncryptConfig int
|
||||
Cryptocurrencies string
|
||||
Portfolio portfolio.PortfolioBase `json:"PortfolioAddresses"`
|
||||
SMS SMSGlobalConfig `json:"SMSGlobal"`
|
||||
Webserver WebserverConfig `json:"Webserver"`
|
||||
Exchanges []ExchangeConfig `json:"Exchanges"`
|
||||
Name string
|
||||
EncryptConfig int
|
||||
Cryptocurrencies string
|
||||
CurrencyExchangeProvider string
|
||||
CurrencyPairFormat *CurrencyPairFormatConfig `json:"CurrencyPairFormat"`
|
||||
FiatDisplayCurrency string
|
||||
Portfolio portfolio.Base `json:"PortfolioAddresses"`
|
||||
SMS SMSGlobalConfig `json:"SMSGlobal"`
|
||||
Webserver WebserverConfig `json:"Webserver"`
|
||||
Exchanges []ExchangeConfig `json:"Exchanges"`
|
||||
}
|
||||
|
||||
// ExchangeConfig holds all the information needed for each enabled Exchange.
|
||||
type ExchangeConfig struct {
|
||||
Name string
|
||||
Enabled bool
|
||||
Verbose bool
|
||||
Websocket bool
|
||||
RESTPollingDelay time.Duration
|
||||
AuthenticatedAPISupport bool
|
||||
APIKey string
|
||||
APISecret string
|
||||
ClientID string `json:",omitempty"`
|
||||
AvailablePairs string
|
||||
EnabledPairs string
|
||||
BaseCurrencies string
|
||||
Name string
|
||||
Enabled bool
|
||||
Verbose bool
|
||||
Websocket bool
|
||||
RESTPollingDelay time.Duration
|
||||
AuthenticatedAPISupport bool
|
||||
APIKey string
|
||||
APISecret string
|
||||
ClientID string `json:",omitempty"`
|
||||
AvailablePairs string
|
||||
EnabledPairs string
|
||||
BaseCurrencies string
|
||||
AssetTypes string
|
||||
ConfigCurrencyPairFormat *CurrencyPairFormatConfig `json:"ConfigCurrencyPairFormat"`
|
||||
RequestCurrencyPairFormat *CurrencyPairFormatConfig `json:"RequestCurrencyPairFormat"`
|
||||
}
|
||||
|
||||
// GetConfigEnabledExchanges returns the number of exchanges that are enabled.
|
||||
func (c *Config) GetConfigEnabledExchanges() int {
|
||||
counter := 0
|
||||
for i := range c.Exchanges {
|
||||
@@ -104,8 +128,14 @@ func (c *Config) GetConfigEnabledExchanges() int {
|
||||
return counter
|
||||
}
|
||||
|
||||
// GetCurrencyPairDisplayConfig retrieves the currency pair display preference
|
||||
func (c *Config) GetCurrencyPairDisplayConfig() *CurrencyPairFormatConfig {
|
||||
return c.CurrencyPairFormat
|
||||
}
|
||||
|
||||
// GetExchangeConfig returns your exchange configurations by its indivdual name
|
||||
func (c *Config) GetExchangeConfig(name string) (ExchangeConfig, error) {
|
||||
for i, _ := range c.Exchanges {
|
||||
for i := range c.Exchanges {
|
||||
if c.Exchanges[i].Name == name {
|
||||
return c.Exchanges[i], nil
|
||||
}
|
||||
@@ -113,8 +143,9 @@ func (c *Config) GetExchangeConfig(name string) (ExchangeConfig, error) {
|
||||
return ExchangeConfig{}, fmt.Errorf(ErrExchangeNotFound, name)
|
||||
}
|
||||
|
||||
// UpdateExchangeConfig updates exchange configurations
|
||||
func (c *Config) UpdateExchangeConfig(e ExchangeConfig) error {
|
||||
for i, _ := range c.Exchanges {
|
||||
for i := range c.Exchanges {
|
||||
if c.Exchanges[i].Name == e.Name {
|
||||
c.Exchanges[i] = e
|
||||
return nil
|
||||
@@ -123,6 +154,7 @@ func (c *Config) UpdateExchangeConfig(e ExchangeConfig) error {
|
||||
return fmt.Errorf(ErrExchangeNotFound, e.Name)
|
||||
}
|
||||
|
||||
// CheckSMSGlobalConfigValues checks concurrent SMSGlobal configurations
|
||||
func (c *Config) CheckSMSGlobalConfigValues() error {
|
||||
if c.SMS.Username == "" || c.SMS.Username == "Username" || c.SMS.Password == "" || c.SMS.Password == "Password" {
|
||||
return errors.New(WarningSMSGlobalDefaultOrEmptyValues)
|
||||
@@ -143,6 +175,8 @@ func (c *Config) CheckSMSGlobalConfigValues() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckExchangeConfigValues returns configuation values for all enabled
|
||||
// exchanges
|
||||
func (c *Config) CheckExchangeConfigValues() error {
|
||||
if c.Cryptocurrencies == "" {
|
||||
return errors.New(ErrCryptocurrenciesEmpty)
|
||||
@@ -185,6 +219,8 @@ func (c *Config) CheckExchangeConfigValues() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckWebserverConfigValues checks information before webserver starts and
|
||||
// returns an error if values are incorrect.
|
||||
func (c *Config) CheckWebserverConfigValues() error {
|
||||
if c.Webserver.AdminUsername == "" || c.Webserver.AdminPassword == "" {
|
||||
return errors.New(WarningWebserverCredentialValuesEmpty)
|
||||
@@ -203,12 +239,19 @@ func (c *Config) CheckWebserverConfigValues() error {
|
||||
if port < 1 || port > 65355 {
|
||||
return errors.New(WarningWebserverListenAddressInvalid)
|
||||
}
|
||||
|
||||
if c.Webserver.WebsocketConnectionLimit <= 0 {
|
||||
c.Webserver.WebsocketConnectionLimit = 1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetrieveConfigCurrencyPairs splits, assigns and verifies enabled currency
|
||||
// pairs either cryptoCurrencies or fiatCurrencies
|
||||
func (c *Config) RetrieveConfigCurrencyPairs() error {
|
||||
cryptoCurrencies := common.SplitStrings(c.Cryptocurrencies, ",")
|
||||
fiatCurrencies := common.SplitStrings(currency.DEFAULT_CURRENCIES, ",")
|
||||
fiatCurrencies := common.SplitStrings(currency.DefaultCurrencies, ",")
|
||||
|
||||
for _, s := range cryptoCurrencies {
|
||||
_, err := strconv.Atoi(s)
|
||||
@@ -266,26 +309,36 @@ func (c *Config) RetrieveConfigCurrencyPairs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFilePath returns the desired config file or the default config file name
|
||||
// based on if the application is being run under test or normal mode.
|
||||
func GetFilePath(file string) string {
|
||||
if file != "" {
|
||||
return file
|
||||
}
|
||||
if flag.Lookup("test.v") == nil {
|
||||
return ConfigFile
|
||||
}
|
||||
return ConfigTestFile
|
||||
}
|
||||
|
||||
// CheckConfig checks to see if there is an old configuration filename and path
|
||||
// if found it will change it to correct filename.
|
||||
func CheckConfig() error {
|
||||
_, err := common.ReadFile(OLD_CONFIG_FILE)
|
||||
_, err := common.ReadFile(OldConfigFile)
|
||||
if err == nil {
|
||||
err = os.Rename(OLD_CONFIG_FILE, CONFIG_FILE)
|
||||
err = os.Rename(OldConfigFile, ConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf(RenamingConfigFile+"\n", OLD_CONFIG_FILE, CONFIG_FILE)
|
||||
log.Printf(RenamingConfigFile+"\n", OldConfigFile, ConfigFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadConfig verifies and checks for encryption and verifies the unencrypted
|
||||
// file contains JSON.
|
||||
func (c *Config) ReadConfig(configPath string) error {
|
||||
defaultPath := ""
|
||||
if configPath == "" {
|
||||
defaultPath = CONFIG_FILE
|
||||
} else {
|
||||
defaultPath = configPath
|
||||
}
|
||||
|
||||
defaultPath := GetFilePath(configPath)
|
||||
err := CheckConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -302,13 +355,13 @@ func (c *Config) ReadConfig(configPath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.EncryptConfig == CONFIG_FILE_ENCRYPTION_DISABLED {
|
||||
if c.EncryptConfig == configFileEncryptionDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.EncryptConfig == CONFIG_FILE_ENCRYPTION_PROMPT {
|
||||
if c.EncryptConfig == configFileEncryptionPrompt {
|
||||
if c.PromptForConfigEncryption() {
|
||||
c.EncryptConfig = CONFIG_FILE_ENCRYPTION_ENABLED
|
||||
c.EncryptConfig = configFileEncryptionEnabled
|
||||
return c.SaveConfig("")
|
||||
}
|
||||
}
|
||||
@@ -331,19 +384,14 @@ func (c *Config) ReadConfig(configPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveConfig saves your configuration to your desired path
|
||||
func (c *Config) SaveConfig(configPath string) error {
|
||||
defaultPath := ""
|
||||
if configPath == "" {
|
||||
defaultPath = CONFIG_FILE
|
||||
} else {
|
||||
defaultPath = configPath
|
||||
}
|
||||
|
||||
defaultPath := GetFilePath(configPath)
|
||||
payload, err := json.MarshalIndent(c, "", " ")
|
||||
|
||||
if c.EncryptConfig == CONFIG_FILE_ENCRYPTION_ENABLED {
|
||||
key, err := PromptForConfigKey()
|
||||
if err != nil {
|
||||
if c.EncryptConfig == configFileEncryptionEnabled {
|
||||
key, err2 := PromptForConfigKey()
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -360,10 +408,11 @@ func (c *Config) SaveConfig(configPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadConfig loads your configuration file into your configuration object
|
||||
func (c *Config) LoadConfig(configPath string) error {
|
||||
err := c.ReadConfig(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf(ErrFailureOpeningConfig, CONFIG_FILE, err)
|
||||
return fmt.Errorf(ErrFailureOpeningConfig, configPath, err)
|
||||
}
|
||||
|
||||
err = c.CheckExchangeConfigValues()
|
||||
@@ -371,9 +420,83 @@ func (c *Config) LoadConfig(configPath string) error {
|
||||
return fmt.Errorf(ErrCheckingConfigValues, err)
|
||||
}
|
||||
|
||||
if c.SMS.Enabled {
|
||||
err = c.CheckSMSGlobalConfigValues()
|
||||
if err != nil {
|
||||
log.Print(fmt.Errorf(ErrCheckingConfigValues, err))
|
||||
c.SMS.Enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
if c.Webserver.Enabled {
|
||||
err = c.CheckWebserverConfigValues()
|
||||
if err != nil {
|
||||
log.Print(fmt.Errorf(ErrCheckingConfigValues, err))
|
||||
c.Webserver.Enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
if c.CurrencyExchangeProvider == "" {
|
||||
c.CurrencyExchangeProvider = "fixer"
|
||||
} else {
|
||||
if c.CurrencyExchangeProvider != "yahoo" && c.CurrencyExchangeProvider != "fixer" {
|
||||
log.Println(WarningCurrencyExchangeProvider)
|
||||
c.CurrencyExchangeProvider = "fixer"
|
||||
}
|
||||
}
|
||||
|
||||
if c.CurrencyPairFormat == nil {
|
||||
c.CurrencyPairFormat = &CurrencyPairFormatConfig{
|
||||
Delimiter: "-",
|
||||
Uppercase: true,
|
||||
}
|
||||
}
|
||||
|
||||
if c.FiatDisplayCurrency == "" {
|
||||
c.FiatDisplayCurrency = "USD"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateConfig updates the config with a supplied config file
|
||||
func (c *Config) UpdateConfig(configPath string, newCfg Config) error {
|
||||
if c.Name != newCfg.Name && newCfg.Name != "" {
|
||||
c.Name = newCfg.Name
|
||||
}
|
||||
|
||||
err := newCfg.CheckExchangeConfigValues()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Exchanges = newCfg.Exchanges
|
||||
|
||||
if c.CurrencyPairFormat != newCfg.CurrencyPairFormat {
|
||||
c.CurrencyPairFormat = newCfg.CurrencyPairFormat
|
||||
}
|
||||
|
||||
c.Portfolio = newCfg.Portfolio
|
||||
|
||||
err = newCfg.CheckSMSGlobalConfigValues()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.SMS = newCfg.SMS
|
||||
|
||||
err = c.SaveConfig(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConfig returns a pointer to a confiuration object
|
||||
func GetConfig() *Config {
|
||||
return &Cfg
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
CONFIG_ENCRYPTION_CONFIRMATION_STRING = "THORS-HAMMER"
|
||||
|
||||
ErrConfigDataLessThenRequiredAESBlockSize = "The config file data is too small for the AES required block size."
|
||||
// EncryptConfirmString has a the general confirmation string to allow us to
|
||||
// see if the file is correctly encrypted
|
||||
EncryptConfirmString = "THORS-HAMMER"
|
||||
errAESBlockSize = "The config file data is too small for the AES required block size"
|
||||
errNotAPointer = "Error: parameter interface is not a pointer"
|
||||
)
|
||||
|
||||
// PromptForConfigEncryption asks for encryption key
|
||||
func (c *Config) PromptForConfigEncryption() bool {
|
||||
log.Println("Would you like to encrypt your config file (y/n)?")
|
||||
|
||||
@@ -30,13 +33,14 @@ func (c *Config) PromptForConfigEncryption() bool {
|
||||
}
|
||||
|
||||
if !common.YesOrNo(input) {
|
||||
c.EncryptConfig = CONFIG_FILE_ENCRYPTION_DISABLED
|
||||
c.EncryptConfig = configFileEncryptionDisabled
|
||||
c.SaveConfig("")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// PromptForConfigKey asks for configuration key
|
||||
func PromptForConfigKey() ([]byte, error) {
|
||||
var cryptoKey []byte
|
||||
|
||||
@@ -60,6 +64,8 @@ func PromptForConfigKey() ([]byte, error) {
|
||||
return cryptoKey, nil
|
||||
}
|
||||
|
||||
// EncryptConfigFile encrypts configuration data that is parsed in with a key
|
||||
// and returns it as a byte array with an error
|
||||
func EncryptConfigFile(configData, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
@@ -75,11 +81,13 @@ func EncryptConfigFile(configData, key []byte) ([]byte, error) {
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext[aes.BlockSize:], configData)
|
||||
|
||||
appendedFile := []byte(CONFIG_ENCRYPTION_CONFIRMATION_STRING)
|
||||
appendedFile := []byte(EncryptConfirmString)
|
||||
appendedFile = append(appendedFile, ciphertext...)
|
||||
return appendedFile, nil
|
||||
}
|
||||
|
||||
// DecryptConfigFile decrypts configuration data with the supplied key and
|
||||
// returns the un-encrypted file as a byte array with an error
|
||||
func DecryptConfigFile(configData, key []byte) ([]byte, error) {
|
||||
configData = RemoveECS(configData)
|
||||
blockDecrypt, err := aes.NewCipher(key)
|
||||
@@ -88,7 +96,7 @@ func DecryptConfigFile(configData, key []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
if len(configData) < aes.BlockSize {
|
||||
return nil, errors.New(ErrConfigDataLessThenRequiredAESBlockSize)
|
||||
return nil, errors.New(errAESBlockSize)
|
||||
}
|
||||
|
||||
iv := configData[:aes.BlockSize]
|
||||
@@ -100,18 +108,21 @@ func DecryptConfigFile(configData, key []byte) ([]byte, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ConfirmConfigJSON confirms JSON in file
|
||||
func ConfirmConfigJSON(file []byte, result interface{}) error {
|
||||
if !common.StringContains(reflect.TypeOf(result).String(), "*") {
|
||||
return errors.New("ConfirmConfigJSON Error: Parameter interface is not a pointer.")
|
||||
return errors.New(errNotAPointer)
|
||||
}
|
||||
return common.JSONDecode(file, &result)
|
||||
}
|
||||
|
||||
// ConfirmECS confirms that the encryption confirmation string is found
|
||||
func ConfirmECS(file []byte) bool {
|
||||
subslice := []byte(CONFIG_ENCRYPTION_CONFIRMATION_STRING)
|
||||
subslice := []byte(EncryptConfirmString)
|
||||
return bytes.Contains(file, subslice)
|
||||
}
|
||||
|
||||
// RemoveECS removes encryption confirmation string
|
||||
func RemoveECS(file []byte) []byte {
|
||||
return bytes.Trim(file, CONFIG_ENCRYPTION_CONFIRMATION_STRING)
|
||||
return bytes.Trim(file, EncryptConfirmString)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -27,7 +26,8 @@ func TestPromptForConfigKey(t *testing.T) {
|
||||
|
||||
func TestEncryptDecryptConfigFile(t *testing.T) { //Dual function Test
|
||||
testKey := []byte("12345678901234567890123456789012")
|
||||
testConfigData, err := common.ReadFile(CONFIG_TEST_FILE)
|
||||
|
||||
testConfigData, err := common.ReadFile(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. EncryptConfigFile: %s", err)
|
||||
}
|
||||
@@ -46,16 +46,16 @@ func TestEncryptDecryptConfigFile(t *testing.T) { //Dual function Test
|
||||
if reflect.TypeOf(decryptedFile).String() != "[]uint8" {
|
||||
t.Errorf("Test failed. DecryptConfigFile: Incorrect Type")
|
||||
}
|
||||
unmarshalled := Config{}
|
||||
err4 := json.Unmarshal(decryptedFile, &unmarshalled)
|
||||
if err4 != nil {
|
||||
t.Errorf("Test failed. DecryptConfigFile: %s", err3)
|
||||
}
|
||||
// unmarshalled := Config{} // racecondition
|
||||
// err4 := json.Unmarshal(decryptedFile, &unmarshalled)
|
||||
// if err4 != nil {
|
||||
// t.Errorf("Test failed. DecryptConfigFile: %s", err3)
|
||||
// }
|
||||
}
|
||||
|
||||
func TestConfirmJson(t *testing.T) {
|
||||
func TestConfirmConfigJSON(t *testing.T) {
|
||||
var result interface{}
|
||||
testConfirmJSON, err := common.ReadFile(CONFIG_TEST_FILE)
|
||||
testConfirmJSON, err := common.ReadFile(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. testConfirmJSON: %s", err)
|
||||
}
|
||||
@@ -67,12 +67,16 @@ func TestConfirmJson(t *testing.T) {
|
||||
if result == nil {
|
||||
t.Errorf("Test failed. testConfirmJSON: Error Unmarshalling JSON")
|
||||
}
|
||||
err3 := ConfirmConfigJSON(testConfirmJSON, result)
|
||||
if err3 == nil {
|
||||
t.Errorf("Test failed. testConfirmJSON: %s", err3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirmECS(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ECStest := []byte(CONFIG_ENCRYPTION_CONFIRMATION_STRING)
|
||||
ECStest := []byte(EncryptConfirmString)
|
||||
if !ConfirmECS(ECStest) {
|
||||
t.Errorf("Test failed. TestConfirmECS: Error finding ECS.")
|
||||
}
|
||||
@@ -81,7 +85,7 @@ func TestConfirmECS(t *testing.T) {
|
||||
func TestRemoveECS(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ECStest := []byte(CONFIG_ENCRYPTION_CONFIRMATION_STRING)
|
||||
ECStest := []byte(EncryptConfirmString)
|
||||
isremoved := RemoveECS(ECStest)
|
||||
|
||||
if string(isremoved) != "" {
|
||||
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
)
|
||||
|
||||
func TestGetConfigEnabledExchanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
defaultEnabledExchanges := 17
|
||||
defaultEnabledExchanges := 18
|
||||
GetConfigEnabledExchanges := GetConfig()
|
||||
err := GetConfigEnabledExchanges.LoadConfig(CONFIG_TEST_FILE)
|
||||
err := GetConfigEnabledExchanges.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Error("Test failed. GetConfigEnabledExchanges load config error: " + err.Error())
|
||||
t.Error(
|
||||
"Test failed. GetConfigEnabledExchanges load config error: " + err.Error(),
|
||||
)
|
||||
}
|
||||
enabledExch := GetConfigEnabledExchanges.GetConfigEnabledExchanges()
|
||||
if enabledExch != defaultEnabledExchanges {
|
||||
@@ -19,36 +19,67 @@ func TestGetConfigEnabledExchanges(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExchangeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
GetExchangeConfig := GetConfig()
|
||||
err := GetExchangeConfig.LoadConfig(CONFIG_TEST_FILE)
|
||||
func TestGetCurrencyPairDisplayConfig(t *testing.T) {
|
||||
cfg := GetConfig()
|
||||
err := cfg.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. GetExchangeConfig.LoadConfig Error: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. GetCurrencyPairDisplayConfig. LoadConfig Error: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
settings := cfg.GetCurrencyPairDisplayConfig()
|
||||
if settings.Delimiter != "-" || !settings.Uppercase {
|
||||
t.Errorf(
|
||||
"Test failed. GetCurrencyPairDisplayConfi. Invalid values",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExchangeConfig(t *testing.T) {
|
||||
GetExchangeConfig := GetConfig()
|
||||
err := GetExchangeConfig.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Test failed. GetExchangeConfig.LoadConfig Error: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
r, err := GetExchangeConfig.GetExchangeConfig("ANX")
|
||||
if err != nil && (ExchangeConfig{}) == r {
|
||||
t.Errorf("Test failed. GetExchangeConfig.GetExchangeConfig Error: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. GetExchangeConfig.GetExchangeConfig Error: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
r, err = GetExchangeConfig.GetExchangeConfig("Testy")
|
||||
if err == nil && (ExchangeConfig{}) == r {
|
||||
t.Error("Test failed. GetExchangeConfig.GetExchangeConfig Error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateExchangeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
UpdateExchangeConfig := GetConfig()
|
||||
err := UpdateExchangeConfig.LoadConfig(CONFIG_TEST_FILE)
|
||||
err := UpdateExchangeConfig.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. UpdateExchangeConfig.LoadConfig Error: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. UpdateExchangeConfig.LoadConfig Error: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
e, err2 := UpdateExchangeConfig.GetExchangeConfig("ANX")
|
||||
if err2 != nil {
|
||||
t.Errorf("Test failed. UpdateExchangeConfig.GetExchangeConfig: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. UpdateExchangeConfig.GetExchangeConfig: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
e.APIKey = "test1234"
|
||||
err3 := UpdateExchangeConfig.UpdateExchangeConfig(e)
|
||||
if err3 != nil {
|
||||
t.Errorf("Test failed. UpdateExchangeConfig.UpdateExchangeConfig: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. UpdateExchangeConfig.UpdateExchangeConfig: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
e.Name = "testyTest"
|
||||
err = UpdateExchangeConfig.UpdateExchangeConfig(e)
|
||||
if err == nil {
|
||||
t.Error("Test failed. UpdateExchangeConfig.UpdateExchangeConfig Error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,87 +87,251 @@ func TestCheckSMSGlobalConfigValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
checkSMSGlobalConfigValues := GetConfig()
|
||||
err := checkSMSGlobalConfigValues.LoadConfig(CONFIG_TEST_FILE)
|
||||
err := checkSMSGlobalConfigValues.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. checkSMSGlobalConfigValues.LoadConfig: %s", err)
|
||||
}
|
||||
err2 := checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues()
|
||||
if err2 != nil {
|
||||
t.Error("Test failed. checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues: Incorrect Return Value")
|
||||
err = checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues()
|
||||
if err != nil {
|
||||
t.Error(
|
||||
`Test failed. checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues: Incorrect Return Value`,
|
||||
)
|
||||
}
|
||||
|
||||
checkSMSGlobalConfigValues.SMS.Username = "Username"
|
||||
err = checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues()
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test failed. checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues: Incorrect Return Value",
|
||||
)
|
||||
}
|
||||
|
||||
checkSMSGlobalConfigValues.SMS.Username = "1234"
|
||||
checkSMSGlobalConfigValues.SMS.Contacts[0].Name = "Bob"
|
||||
checkSMSGlobalConfigValues.SMS.Contacts[0].Number = "12345"
|
||||
err = checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues()
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test failed. checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues: Incorrect Return Value",
|
||||
)
|
||||
}
|
||||
checkSMSGlobalConfigValues.SMS.Contacts = checkSMSGlobalConfigValues.SMS.Contacts[:0]
|
||||
err = checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues()
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test failed. checkSMSGlobalConfigValues.CheckSMSGlobalConfigValues: Incorrect Return Value",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckExchangeConfigValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
checkExchangeConfigValues := Config{}
|
||||
err := checkExchangeConfigValues.LoadConfig(CONFIG_TEST_FILE)
|
||||
|
||||
err := checkExchangeConfigValues.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. checkExchangeConfigValues.LoadConfig: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.LoadConfig: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues: %s",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
err3 := checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err3 != nil {
|
||||
t.Errorf("Test failed. checkExchangeConfigValues.CheckExchangeConfigValues: %s", err.Error())
|
||||
checkExchangeConfigValues.Exchanges[0].APIKey = "Key"
|
||||
checkExchangeConfigValues.Exchanges[0].APISecret = "Secret"
|
||||
checkExchangeConfigValues.Exchanges[0].AuthenticatedAPISupport = true
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
|
||||
checkExchangeConfigValues.Exchanges[0].AuthenticatedAPISupport = true
|
||||
checkExchangeConfigValues.Exchanges[0].APIKey = "TESTYTEST"
|
||||
checkExchangeConfigValues.Exchanges[0].APISecret = "TESTYTEST"
|
||||
checkExchangeConfigValues.Exchanges[0].Name = "ITBIT"
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
|
||||
checkExchangeConfigValues.Exchanges[0].BaseCurrencies = ""
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err == nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
|
||||
checkExchangeConfigValues.Exchanges[0].EnabledPairs = ""
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err == nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
|
||||
checkExchangeConfigValues.Exchanges[0].AvailablePairs = ""
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err == nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
|
||||
checkExchangeConfigValues.Exchanges[0].Name = ""
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err == nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
|
||||
checkExchangeConfigValues.Cryptocurrencies = ""
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err == nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
|
||||
checkExchangeConfigValues.Exchanges = checkExchangeConfigValues.Exchanges[:0]
|
||||
checkExchangeConfigValues.Cryptocurrencies = "TESTYTEST"
|
||||
err = checkExchangeConfigValues.CheckExchangeConfigValues()
|
||||
if err == nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkExchangeConfigValues.CheckExchangeConfigValues Error",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckWebserverConfigValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
checkWebserverConfigValues := GetConfig()
|
||||
err := checkWebserverConfigValues.LoadConfig(CONFIG_TEST_FILE)
|
||||
err := checkWebserverConfigValues.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. checkWebserverConfigValues.LoadConfig: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. checkWebserverConfigValues.LoadConfig: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
err2 := checkWebserverConfigValues.CheckWebserverConfigValues()
|
||||
if err2 != nil {
|
||||
t.Errorf("Test failed. checkWebserverConfigValues.CheckWebserverConfigValues: %s", err2.Error())
|
||||
err = checkWebserverConfigValues.CheckWebserverConfigValues()
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkWebserverConfigValues.CheckWebserverConfigValues: %s",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
checkWebserverConfigValues.Webserver.ListenAddress = ":0"
|
||||
err = checkWebserverConfigValues.CheckWebserverConfigValues()
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test failed. checkWebserverConfigValues.CheckWebserverConfigValues error",
|
||||
)
|
||||
}
|
||||
|
||||
checkWebserverConfigValues.Webserver.ListenAddress = ":LOLOLOL"
|
||||
err = checkWebserverConfigValues.CheckWebserverConfigValues()
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test failed. checkWebserverConfigValues.CheckWebserverConfigValues error",
|
||||
)
|
||||
}
|
||||
|
||||
checkWebserverConfigValues.Webserver.ListenAddress = "LOLOLOL"
|
||||
err = checkWebserverConfigValues.CheckWebserverConfigValues()
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test failed. checkWebserverConfigValues.CheckWebserverConfigValues error",
|
||||
)
|
||||
}
|
||||
|
||||
checkWebserverConfigValues.Webserver.AdminUsername = ""
|
||||
err = checkWebserverConfigValues.CheckWebserverConfigValues()
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test failed. checkWebserverConfigValues.CheckWebserverConfigValues error",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveConfigCurrencyPairs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
retrieveConfigCurrencyPairs := GetConfig()
|
||||
err := retrieveConfigCurrencyPairs.LoadConfig(CONFIG_TEST_FILE)
|
||||
err := retrieveConfigCurrencyPairs.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. checkWebserverConfigValues.LoadConfig: %s", err.Error())
|
||||
t.Errorf(
|
||||
"Test failed. checkWebserverConfigValues.LoadConfig: %s", err.Error(),
|
||||
)
|
||||
}
|
||||
err2 := retrieveConfigCurrencyPairs.RetrieveConfigCurrencyPairs()
|
||||
if err2 != nil {
|
||||
t.Errorf("Test failed. checkWebserverConfigValues.RetrieveConfigCurrencyPairs: %s", err2.Error())
|
||||
err = retrieveConfigCurrencyPairs.RetrieveConfigCurrencyPairs()
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Test failed. checkWebserverConfigValues.RetrieveConfigCurrencyPairs: %s",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
readConfig := GetConfig()
|
||||
err := readConfig.ReadConfig(CONFIG_TEST_FILE)
|
||||
err := readConfig.ReadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. TestReadConfig %s", err.Error())
|
||||
}
|
||||
|
||||
err = readConfig.ReadConfig("bla")
|
||||
if err == nil {
|
||||
t.Error("Test failed. TestReadConfig " + err.Error())
|
||||
}
|
||||
|
||||
err = readConfig.ReadConfig("")
|
||||
if err != nil {
|
||||
t.Error("Test failed. TestReadConfig error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loadConfig := GetConfig()
|
||||
err := loadConfig.LoadConfig(CONFIG_TEST_FILE)
|
||||
err := loadConfig.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Error("Test failed. TestLoadConfig " + err.Error())
|
||||
}
|
||||
|
||||
err = loadConfig.LoadConfig("testy")
|
||||
if err == nil {
|
||||
t.Error("Test failed. TestLoadConfig ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveConfig(t *testing.T) {
|
||||
saveConfig := GetConfig()
|
||||
err := saveConfig.LoadConfig(CONFIG_TEST_FILE)
|
||||
err := saveConfig.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. TestSaveConfig.LoadConfig: %s", err.Error())
|
||||
}
|
||||
err2 := saveConfig.SaveConfig(CONFIG_TEST_FILE)
|
||||
err2 := saveConfig.SaveConfig(ConfigTestFile)
|
||||
if err2 != nil {
|
||||
t.Errorf("Test failed. TestSaveConfig.SaveConfig, %s", err2.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFilePath(t *testing.T) {
|
||||
expected := "blah.json"
|
||||
result := GetFilePath("blah.json")
|
||||
if result != "blah.json" {
|
||||
t.Errorf("Test failed. TestGetFilePath: expected %s got %s", expected, result)
|
||||
}
|
||||
|
||||
expected = ConfigTestFile
|
||||
result = GetFilePath("")
|
||||
if result != expected {
|
||||
t.Errorf("Test failed. TestGetFilePath: expected %s got %s", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,27 +2,35 @@
|
||||
"Name": "Skynet",
|
||||
"EncryptConfig": 0,
|
||||
"Cryptocurrencies": "BTC,LTC,ETH,XRP,NMC,NVC,PPC,XBT,DOGE,DASH",
|
||||
"CurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "-"
|
||||
},
|
||||
"PortfolioAddresses": {
|
||||
"Addresses": [
|
||||
{
|
||||
"Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy",
|
||||
"CoinType": "BTC",
|
||||
"Balance": 124178.0002442
|
||||
"Balance": 124178.00647714,
|
||||
"Description": ""
|
||||
},
|
||||
{
|
||||
"Address": "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v",
|
||||
"CoinType": "BTC",
|
||||
"Balance": 103439.83659727
|
||||
"Balance": 107843.84030984,
|
||||
"Description": ""
|
||||
},
|
||||
{
|
||||
"Address": "LgY8ahfHRhvjVQC1zJnBhFMG5pCTMuKRqh",
|
||||
"CoinType": "LTC",
|
||||
"Balance": 3.00000005e+06
|
||||
"Balance": 100000.052,
|
||||
"Description": ""
|
||||
},
|
||||
{
|
||||
"Address": "0xb794f5ea0ba39494ce839613fffba74279579268",
|
||||
"CoinType": "ETH",
|
||||
"Balance": 5.774999820458524e+06
|
||||
"Balance": 3.224999915984445e+24,
|
||||
"Description": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -54,10 +62,18 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCUSD,BTCHKD,BTCEUR,BTCCAD,BTCAUD,BTCSGD,BTCJPY,BTCGBP,BTCNZD,LTCBTC,DOGEBTC,STRBTC,XRPBTC",
|
||||
"EnabledPairs": "BTCUSD,BTCHKD,BTCEUR,BTCCAD,BTCAUD,BTCSGD,BTCJPY,BTCGBP,BTCNZD,LTCBTC,DOGEBTC,STRBTC,XRPBTC",
|
||||
"BaseCurrencies": "USD,HKD,EUR,CAD,AUD,SGD,JPY,GBP,NZD"
|
||||
"BaseCurrencies": "USD,HKD,EUR,CAD,AUD,SGD,JPY,GBP,NZD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Index": "BTC"
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Index": "BTC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bitfinex",
|
||||
@@ -68,10 +84,16 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC,ETCBTC,ETCUSD,BFXUSD,BFXBTC,RRTUSD,RRTBTC,ZECUSD,ZECBTC,XMRUSD,XMRBTC,DSHUSD,DSHBTC",
|
||||
"AvailablePairs": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC,ETCBTC,ETCUSD,RRTUSD,RRTBTC,ZECUSD,ZECBTC,XMRUSD,XMRBTC,DSHUSD,DSHBTC,BCCBTC,BCUBTC,BCCUSD,BCUUSD,XRPUSD,XRPBTC,IOTUSD,IOTBTC,IOTETH,EOSUSD,EOSBTC,EOSETH,SANUSD,SANBTC,SANETH,OMGUSD,OMGBTC,OMGETH,BCHUSD,BCHBTC,BCHETH",
|
||||
"EnabledPairs": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC",
|
||||
"BaseCurrencies": "USD"
|
||||
"BaseCurrencies": "USD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bitstamp",
|
||||
@@ -85,7 +107,36 @@
|
||||
"ClientID": "ClientID",
|
||||
"AvailablePairs": "BTCUSD,BTCEUR,EURUSD,XRPUSD,XRPEUR",
|
||||
"EnabledPairs": "BTCUSD,BTCEUR,EURUSD,XRPUSD,XRPEUR",
|
||||
"BaseCurrencies": "USD,EUR"
|
||||
"BaseCurrencies": "USD,EUR",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bittrex",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"AvailablePairs": "BTC-LTC,BTC-DOGE,BTC-VTC,BTC-PPC,BTC-FTC,BTC-RDD,BTC-NXT,BTC-DASH,BTC-POT,BTC-BLK,BTC-EMC2,BTC-XMY,BTC-AUR,BTC-EFL,BTC-GLD,BTC-SLR,BTC-PTC,BTC-GRS,BTC-NLG,BTC-RBY,BTC-XWC,BTC-MONA,BTC-THC,BTC-ENRG,BTC-ERC,BTC-VRC,BTC-CURE,BTC-XBB,BTC-XMR,BTC-CLOAK,BTC-START,BTC-KORE,BTC-XDN,BTC-TRUST,BTC-NAV,BTC-XST,BTC-BTCD,BTC-VIA,BTC-UNO,BTC-PINK,BTC-IOC,BTC-CANN,BTC-SYS,BTC-NEOS,BTC-DGB,BTC-BURST,BTC-EXCL,BTC-SWIFT,BTC-DOPE,BTC-BLOCK,BTC-ABY,BTC-BYC,BTC-XMG,BTC-BLITZ,BTC-BAY,BTC-BTS,BTC-FAIR,BTC-SPR,BTC-VTR,BTC-XRP,BTC-GAME,BTC-COVAL,BTC-NXS,BTC-XCP,BTC-BITB,BTC-GEO,BTC-FLDC,BTC-GRC,BTC-FLO,BTC-NBT,BTC-MUE,BTC-XEM,BTC-CLAM,BTC-DMD,BTC-GAM,BTC-SPHR,BTC-OK,BTC-SNRG,BTC-PKB,BTC-CPC,BTC-AEON,BTC-ETH,BTC-GCR,BTC-TX,BTC-BCY,BTC-EXP,BTC-INFX,BTC-OMNI,BTC-AMP,BTC-AGRS,BTC-XLM,BTC-BTA,USDT-BTC,BTC-CLUB,BTC-VOX,BTC-EMC,BTC-FCT,BTC-MAID,BTC-EGC,BTC-SLS,BTC-RADS,BTC-DCR,BTC-SAFEX,BTC-BSD,BTC-XVG,BTC-PIVX,BTC-XVC,BTC-MEME,BTC-STEEM,BTC-2GIVE,BTC-LSK,BTC-PDC,BTC-BRK,BTC-DGD,ETH-DGD,BTC-WAVES,BTC-RISE,BTC-LBC,BTC-SBD,BTC-BRX,BTC-DRACO,BTC-ETC,ETH-ETC,BTC-STRAT,BTC-UNB,BTC-SYNX,BTC-TRIG,BTC-EBST,BTC-VRM,BTC-SEQ,BTC-XAUR,BTC-SNGLS,BTC-REP,BTC-SHIFT,BTC-ARDR,BTC-XZC,BTC-NEO,BTC-ZEC,BTC-ZCL,BTC-IOP,BTC-DAR,BTC-GOLOS,BTC-HKG,BTC-UBQ,BTC-KMD,BTC-GBG,BTC-SIB,BTC-ION,BTC-LMC,BTC-QWARK,BTC-CRW,BTC-SWT,BTC-TIME,BTC-MLN,BTC-ARK,BTC-DYN,BTC-TKS,BTC-MUSIC,BTC-DTB,BTC-INCNT,BTC-GBYTE,BTC-GNT,BTC-NXC,BTC-EDG,BTC-LGD,BTC-TRST,ETH-GNT,ETH-REP,USDT-ETH,ETH-WINGS,BTC-WINGS,BTC-RLC,BTC-GNO,BTC-GUP,BTC-LUN,ETH-GUP,ETH-RLC,ETH-LUN,ETH-SNGLS,ETH-GNO,BTC-APX,BTC-TKN,ETH-TKN,BTC-HMQ,ETH-HMQ,BTC-ANT,ETH-TRST,ETH-ANT,BTC-SC,ETH-BAT,BTC-BAT,BTC-ZEN,BTC-1ST,BTC-QRL,ETH-1ST,ETH-QRL,BTC-CRB,ETH-CRB,ETH-LGD,BTC-PTOY,ETH-PTOY,BTC-MYST,ETH-MYST,BTC-CFI,ETH-CFI,BTC-BNT,ETH-BNT,BTC-NMR,ETH-NMR,ETH-TIME,ETH-LTC,ETH-XRP,BTC-SNT,ETH-SNT,BTC-DCT,BTC-XEL,BTC-MCO,ETH-MCO,BTC-ADT,ETH-ADT,BTC-FUN,ETH-FUN,BTC-PAY,ETH-PAY,BTC-MTL,ETH-MTL,BTC-STORJ,ETH-STORJ,BTC-ADX,ETH-ADX,ETH-DASH,ETH-SC,ETH-ZEC,USDT-ZEC,USDT-LTC,USDT-ETC,USDT-XRP,BTC-OMG,ETH-OMG,BTC-CVC,ETH-CVC,BTC-PART,BTC-QTUM,ETH-QTUM,ETH-XMR,ETH-XEM,ETH-XLM,ETH-NEO,USDT-XMR,USDT-DASH,ETH-BCC,USDT-BCC,BTC-BCC,USDT-NEO,ETH-WAVES,ETH-STRAT,ETH-DGB,ETH-FCT,ETH-BTS",
|
||||
"EnabledPairs": "USDT-BTC",
|
||||
"BaseCurrencies": "USD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "-"
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "-"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "BTCC",
|
||||
@@ -96,24 +147,16 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCCNY,LTCCNY,LTCBTC",
|
||||
"EnabledPairs": "BTCCNY,LTCCNY,LTCBTC",
|
||||
"BaseCurrencies": "CNY"
|
||||
},
|
||||
{
|
||||
"Name": "BTCE",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD",
|
||||
"EnabledPairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD",
|
||||
"BaseCurrencies": "USD,RUR,EUR"
|
||||
"BaseCurrencies": "CNY",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "BTC Markets",
|
||||
@@ -124,10 +167,16 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "LTC,BTC",
|
||||
"EnabledPairs": "LTC,BTC",
|
||||
"BaseCurrencies": "AUD"
|
||||
"AvailablePairs": "LTCAUD,BTCAUD",
|
||||
"EnabledPairs": "LTCAUD,BTCAUD",
|
||||
"BaseCurrencies": "AUD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "COINUT",
|
||||
@@ -141,7 +190,14 @@
|
||||
"ClientID": "ClientID",
|
||||
"AvailablePairs": "LTCBTC,ETCBTC,ETHBTC",
|
||||
"EnabledPairs": "LTCBTC,ETCBTC,ETHBTC",
|
||||
"BaseCurrencies": "USD"
|
||||
"BaseCurrencies": "USD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "GDAX",
|
||||
@@ -153,9 +209,17 @@
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "ClientID",
|
||||
"AvailablePairs": "BTCGBP,BTCEUR,ETHUSD,ETHBTC,LTCUSD,LTCBTC,BTCUSD",
|
||||
"AvailablePairs": "LTCEUR,LTCBTC,BTCGBP,BTCEUR,ETHEUR,ETHBTC,LTCUSD,BTCUSD,ETHUSD",
|
||||
"EnabledPairs": "BTCUSD,BTCGBP,BTCEUR",
|
||||
"BaseCurrencies": "USD,GBP,EUR"
|
||||
"BaseCurrencies": "USD,GBP,EUR",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "-"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Gemini",
|
||||
@@ -166,10 +230,16 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCUSD,ETHBTC,ETHUSD",
|
||||
"EnabledPairs": "BTCUSD",
|
||||
"BaseCurrencies": "USD"
|
||||
"BaseCurrencies": "USD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Huobi",
|
||||
@@ -180,10 +250,16 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCCNY,LTCCNY",
|
||||
"EnabledPairs": "BTCCNY,LTCCNY",
|
||||
"BaseCurrencies": "CNY"
|
||||
"BaseCurrencies": "CNY",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "ITBIT",
|
||||
@@ -197,7 +273,14 @@
|
||||
"ClientID": "ClientID",
|
||||
"AvailablePairs": "XBTUSD,XBTSGD,XBTEUR",
|
||||
"EnabledPairs": "XBTUSD,XBTSGD,XBTEUR",
|
||||
"BaseCurrencies": "USD,SGD,EUR"
|
||||
"BaseCurrencies": "USD,SGD,EUR",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Kraken",
|
||||
@@ -208,10 +291,17 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "ETCUSD,ICNETH,REPXBT,ZECXBT,ETHXBT,ETHXBT.d,ETHGBP,LTCXBT,XBTGBP.d,XDGXBT,XMRUSD,ZECUSD,ETCETH,ETHJPY,XBTCAD.d,XBTJPY.d,XBTUSD.d,XLMXBT,XLMEUR,XLMUSD,XMREUR,ETCXBT,ETHCAD.d,ETHEUR.d,ETHJPY.d,XBTEUR.d,ETHEUR,ETHGBP.d,ICNXBT,LTCEUR,REPEUR,XBTGBP,XBTJPY,ETHUSD,ETHUSD.d,LTCUSD,REPETH,XBTUSD,XMRXBT,ETCEUR,ETHCAD,REPUSD,XBTCAD,XBTEUR,XRPXBT,ZECEUR",
|
||||
"AvailablePairs": "BCHEUR,REPEUR,XBTGBP,XBTUSD,ETHXBT,MLNXBT,ETCEUR,ETHGBP,ICNXBT,ZECEUR,EOSETH,GNOXBT,ETHCAD.D,ETHGBP.D,XRPEUR,BCHXBT,EOSXBT,LTCXBT,XBTEUR.D,XBTUSD.D,DASHUSD,GNOETH,ETHJPY,ETHUSD.D,REPETH,USDTUSD,ETHEUR,XLMXBT,BCHUSD,ETHCAD,XBTEUR,XMRUSD,ZECXBT,LTCUSD,XBTCAD,XMRXBT,ETHJPY.D,ICNETH,XBTCAD.D,XBTJPY,XRPUSD,ZECUSD,DASHEUR,ETCETH,ETCUSD,MLNETH,XMREUR,DASHXBT,ETHXBT.D,XDGXBT,XBTGBP.D,XRPXBT,XBTJPY.D,ETCXBT,ETHEUR.D,ETHUSD,LTCEUR,REPXBT",
|
||||
"EnabledPairs": "ETCUSD,XBTUSD,ETHUSD",
|
||||
"BaseCurrencies": "EUR,USD,CAD,GBP,JPY"
|
||||
"BaseCurrencies": "EUR,USD,CAD,GBP,JPY",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Separator": ","
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "LakeBTC",
|
||||
@@ -222,10 +312,16 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCUSD,BTCEUR,USDHKD,AUDUSD,BTCGBP,BTCNZD,USDJPY,BTCSGD,BTCNGN,EURUSD,USDSGD,NZDUSD,USDNGN,USDCHF,BTCJPY,BTCAUD,BTCCAD,BTCCHF,GBPUSD,USDCAD",
|
||||
"EnabledPairs": "BTCUSD,BTCAUD",
|
||||
"BaseCurrencies": "USD,EUR,HKD,AUD,GBP,NZD,JPY,SGD,NGN,CHF,CAD"
|
||||
"BaseCurrencies": "USD,EUR,HKD,AUD,GBP,NZD,JPY,SGD,NGN,CHF,CAD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Liqui",
|
||||
@@ -236,10 +332,19 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "TIME_BTC,ETH_BTC,GNT_BTC,WAVES_BTC,ICN_BTC,1ST_BTC,WINGS_BTC,MLN_BTC,ROUND_BTC,VSL_BTC,LTC_BTC,DCT_BTC,INCNT_BTC,PLU_BTC,DASH_BTC",
|
||||
"AvailablePairs": "LUN_BTC,BCAP_ETH,NET_USDT,WAVES_ETH,GNO_ETH,CVC_ETH,GNO_BTC,XID_BTC,TAAS_BTC,MGO_ETH,STORJ_BTC,ADX_USDT,BCC_BTC,ICN_ETH,ETH_USDT,LUN_ETH,SNGLS_BTC,OMG_USDT,STX_BTC,RLC_USDT,TRST_BTC,STX_USDT,INCNT_ETH,EOS_BTC,CVC_USDT,NET_ETH,DGD_BTC,OAX_ETH,DNT_ETH,DASH_USDT,QTUM_BTC,TKN_USDT,SNM_USDT,MCO_ETH,SAN_ETH,TNT_ETH,ROUND_BTC,VSL_ETH,SAN_USDT,VSL_BTC,INCNT_BTC,STORJ_ETH,ZRX_ETH,BCAP_BTC,PTOY_ETH,PAY_BTC,MGO_USDT,EOS_USDT,TIME_USDT,INCNT_USDT,ANT_BTC,MYST_ETH,CFI_ETH,SNM_BTC,DASH_BTC,MLN_BTC,OMG_BTC,SAN_BTC,QTUM_ETH,LTC_ETH,QRL_ETH,QRL_USDT,BNT_ETH,QTUM_USDT,WAVES_USDT,REP_ETH,BNT_BTC,ETH_BTC,WINGS_USDT,SNGLS_ETH,XID_USDT,TNT_BTC,GNT_ETH,WINGS_ETH,BTC_USDT,GUP_USDT,TAAS_ETH,LUN_USDT,HMQ_ETH,MYST_BTC,WAVES_BTC,MLN_ETH,TNT_USDT,STORJ_USDT,OMG_ETH,EDG_BTC,GNO_USDT,BAT_ETH,SNT_USDT,DNT_BTC,PLU_ETH,REP_BTC,ADX_BTC,PAY_ETH,DGD_USDT,ZRX_BTC,WINGS_BTC,QRL_BTC,MCO_BTC,VSL_USDT,BAT_BTC,ANT_USDT,PAY_USDT,XID_ETH,TKN_BTC,EOS_ETH,NET_BTC,RLC_BTC,PTOY_BTC,SNM_ETH,OAX_BTC,1ST_ETH,BCAP_USDT,TRST_USDT,PLU_USDT,GUP_ETH,MCO_USDT,BCC_ETH,ROUND_ETH,TIME_ETH,TIME_BTC,ICN_USDT,GUP_BTC,SNGLS_USDT,PLU_BTC,MYST_USDT,CFI_USDT,SNT_BTC,SNT_ETH,ZRX_USDT,ICN_BTC,BAT_USDT,REP_USDT,HMQ_BTC,OAX_USDT,LTC_BTC,EDG_ETH,GNT_USDT,ROUND_USDT,BNT_USDT,CFI_BTC,CVC_BTC,BCC_USDT,GNT_BTC,STX_ETH,1ST_BTC,MGO_BTC,DNT_USDT,DASH_ETH,1ST_USDT,EDG_USDT,TKN_ETH,PTOY_USDT,ADX_ETH,LTC_USDT,RLC_ETH,HMQ_USDT,ANT_ETH,DGD_ETH,MLN_USDT,TRST_ETH,TAAS_USDT",
|
||||
"EnabledPairs": "ETH_BTC,LTC_BTC,DASH_BTC",
|
||||
"BaseCurrencies": "USD"
|
||||
"BaseCurrencies": "USD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_",
|
||||
"Separator": "-"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "LocalBitcoins",
|
||||
@@ -250,10 +355,16 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCARS,BTCAUD,BTCBRL,BTCCAD,BTCCHF,BTCCZK,BTCDKK,BTCEUR,BTCGBP,BTCHKD,BTCILS,BTCINR,BTCMXN,BTCNOK,BTCNZD,BTCPLN,BTCRUB,BTCSEK,BTCSGD,BTCTHB,BTCUSD,BTCZAR",
|
||||
"EnabledPairs": "BTCARS,BTCAUD,BTCBRL,BTCCAD,BTCCHF,BTCCZK,BTCDKK,BTCEUR,BTCGBP,BTCHKD,BTCILS,BTCINR,BTCMXN,BTCNOK,BTCNZD,BTCPLN,BTCRUB,BTCSEK,BTCSGD,BTCTHB,BTCUSD,BTCZAR",
|
||||
"BaseCurrencies": "ARS,AUD,BRL,CAD,CHF,CZK,DKK,EUR,GBP,HKD,ILS,INR,MXN,NOK,NZD,PLN,RUB,SEK,SGD,THB,USD,ZAR"
|
||||
"BaseCurrencies": "ARS,AUD,BRL,CAD,CHF,CZK,DKK,EUR,GBP,HKD,ILS,INR,MXN,NOK,NZD,PLN,RUB,SEK,SGD,THB,USD,ZAR",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "OKCOIN China",
|
||||
@@ -264,10 +375,17 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCCNY,LTCCNY",
|
||||
"EnabledPairs": "BTCCNY,LTCCNY",
|
||||
"BaseCurrencies": "CNY"
|
||||
"BaseCurrencies": "CNY",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "OKCOIN International",
|
||||
@@ -278,10 +396,17 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTCUSD,LTCUSD",
|
||||
"EnabledPairs": "BTCUSD,LTCUSD",
|
||||
"BaseCurrencies": "USD"
|
||||
"BaseCurrencies": "USD",
|
||||
"AssetTypes": "SPOT,this_week,next_week,quarter",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Poloniex",
|
||||
@@ -292,10 +417,40 @@
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "",
|
||||
"AvailablePairs": "BTC_XUSD,BTC_FCT,BTC_MMNXT,BTC_NMC,BTC_BITUSD,BTC_RDD,BTC_XMR,BTC_XST,BTC_DSH,BTC_MAID,BTC_DGB,BTC_NEOS,BTC_BLK,BTC_NAUT,BTC_NBT,BTC_XCP,BTC_STR,BTC_BTCD,BTC_GRC,BTC_HUC,BTC_BBR,BTC_XDN,BTC_INDEX,BTC_IOC,BTC_SWARM,BTC_EMC2,BTC_MCN,BTC_NOXT,BTC_MINT,BTC_PTS,BTC_SC,BTC_GEO,BTC_XRP,BTC_FLO,BTC_BITS,BTC_HYP,BTC_XCR,BTC_LTBC,BTC_SYS,BTC_GMC,BTC_ETH,BTC_SYNC,BTC_GAP,BTC_BCN,BTC_C2,BTC_PINK,BTC_FIBRE,BTC_POT,BTC_QTL,BTC_SDC,BTC_XC,BTC_DASH,BTC_SILK,BTC_CLAM,BTC_NAV,BTC_PIGGY,BTC_BCY,BTC_MIL,BTC_XCN,BTC_YACC,BTC_BTS,BTC_QBK,BTC_SJCX,BTC_LQD,BTC_BURST,BTC_RIC,BTC_VRC,BTC_LTC,BTC_XPB,BTC_GRS,BTC_XCH,BTC_ARCH,BTC_QORA,BTC_HZ,BTC_NSR,BTC_XPM,BTC_BITCNY,BTC_EXE,BTC_XMG,BTC_BTC,BTC_BTM,BTC_NOBL,BTC_NXT,BTC_DOGE,BTC_CURE,BTC_MNTA,BTC_ADN,BTC_EXP,BTC_VTC,BTC_FLDC,BTC_MRS,BTC_MYR,BTC_OMNI,BTC_VNL,BTC_USDT,BTC_NOTE,BTC_WDC,BTC_BELA,BTC_VIA,BTC_CGA,BTC_DIEM,BTC_IFC,BTC_XDP,BTC_BLOCK,BTC_MMC,BTC_1CR,BTC_UNITY,BTC_XBC,BTC_GEMZ,BTC_FLT,BTC_PPC,BTC_XEM,BTC_RBY,BTC_CNMT,BTC_ABY,XMR_XDN,XMR_IFC,XMR_DIEM,XMR_BBR,XMR_DSH,XMR_BCN,XMR_LTC,XMR_MAID,XMR_DASH,XMR_BTCD,XMR_HYP,XMR_BLK,XMR_QORA,XMR_MNTA,XMR_NXT,USDT_BTC,USDT_ETH,USDT_XRP,USDT_DASH,USDT_LTC,USDT_NXT,USDT_XMR,USDT_STR",
|
||||
"EnabledPairs": "BTC_LTC,BTC_ETH,BTC_DOGE,BTC_DASH,BTC_XRP",
|
||||
"BaseCurrencies": "USD"
|
||||
"BaseCurrencies": "USD",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "WEX",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"AvailablePairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD",
|
||||
"EnabledPairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD",
|
||||
"BaseCurrencies": "USD,RUR,EUR",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_",
|
||||
"Separator": "-"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
func GetAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(bot.config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func SaveAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||
//Get the data from the request
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var responseData config.ConfigPost
|
||||
jsonerr := decoder.Decode(&responseData)
|
||||
if jsonerr != nil {
|
||||
panic(jsonerr)
|
||||
}
|
||||
//Save change the settings
|
||||
for x, _ := range bot.config.Exchanges {
|
||||
for i := 0; i < len(responseData.Data.Exchanges); i++ {
|
||||
if responseData.Data.Exchanges[i].Name == bot.config.Exchanges[x].Name {
|
||||
bot.config.Exchanges[x].Enabled = responseData.Data.Exchanges[i].Enabled
|
||||
bot.config.Exchanges[x].APIKey = responseData.Data.Exchanges[i].APIKey
|
||||
bot.config.Exchanges[x].APISecret = responseData.Data.Exchanges[i].APISecret
|
||||
bot.config.Exchanges[x].EnabledPairs = responseData.Data.Exchanges[i].EnabledPairs
|
||||
}
|
||||
}
|
||||
}
|
||||
//Reload the configuration
|
||||
err := bot.config.SaveConfig("")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = bot.config.LoadConfig("")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
setupBotExchanges()
|
||||
//Return response status
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(bot.config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var ConfigRoutes = Routes{
|
||||
Route{
|
||||
"GetAllSettings",
|
||||
"GET",
|
||||
"/config/all",
|
||||
GetAllSettings,
|
||||
},
|
||||
|
||||
Route{
|
||||
"SaveAllSettings",
|
||||
"POST",
|
||||
"/config/all/save",
|
||||
SaveAllSettings,
|
||||
},
|
||||
}
|
||||
99
coverage.txt
99
coverage.txt
@@ -1,99 +0,0 @@
|
||||
|
||||
mode: atomic
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:37.34,41.2 3 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:43.37,47.2 3 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:49.37,53.2 3 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:55.54,58.18 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:77.2,79.22 3 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:59.2,60.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:63.2,64.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:67.2,68.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:71.2,72.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:60.3,62.4 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:64.3,66.4 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:68.3,70.4 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:72.3,74.4 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:82.45,84.2 1 4
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:86.49,88.16 2 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:91.2,91.20 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:88.16,90.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:94.40,96.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:98.71,100.25 2 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:117.2,117.13 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:100.25,101.29 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:113.3,113.13 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:101.29,103.30 2 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:109.4,109.14 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:103.30,104.17 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:104.17,106.11 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:109.14,111.5 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:113.13,115.4 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:120.51,122.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:124.59,126.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:128.53,130.2 1 4
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:132.46,134.2 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:136.41,138.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:140.41,142.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:144.46,151.16 7 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:155.2,155.15 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:161.2,161.22 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:151.16,154.3 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:155.15,157.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:157.3,159.3 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:164.39,165.15 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:165.15,167.3 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:167.3,169.3 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:172.33,173.66 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:176.2,176.14 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:173.66,175.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:179.58,181.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:183.48,185.2 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:187.73,189.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:191.74,193.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:195.77,197.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:199.102,202.63 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:206.2,208.16 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:212.2,212.28 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:216.2,219.16 3 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:223.2,226.16 3 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:230.2,230.30 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:202.63,204.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:208.16,210.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:212.28,214.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:219.16,221.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:226.16,228.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:233.86,236.16 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:240.2,240.27 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:245.2,247.16 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:251.2,253.16 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:263.2,263.12 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:236.16,238.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:240.27,243.3 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:247.16,249.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:253.16,256.17 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:256.17,258.4 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:259.3,261.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:266.48,268.2 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:270.52,272.2 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:274.60,276.21 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:279.2,279.13 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:276.21,278.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:282.41,284.16 2 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:287.2,287.13 1 2
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:284.16,286.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:290.35,294.2 3 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:296.52,298.16 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:302.2,305.16 3 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:309.2,310.12 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:298.16,300.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:305.16,307.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:313.53,315.2 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:317.64,319.16 2 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:323.2,323.29 1 1
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:319.16,321.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:326.44,328.16 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:331.2,331.18 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:328.16,330.3 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:334.48,336.16 2 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:339.2,339.12 1 0
|
||||
github.com/thrasher-/gocryptotrader/common/common.go:336.16,338.3 1 0
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
// Rate holds the current exchange rates for the currency pair.
|
||||
type Rate struct {
|
||||
Id string `json:"id"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"Name"`
|
||||
Rate float64 `json:",string"`
|
||||
Date string `json:"Date"`
|
||||
@@ -21,12 +22,14 @@ type Rate struct {
|
||||
Bid float64 `json:",string"`
|
||||
}
|
||||
|
||||
// YahooJSONResponseInfo is a sub type that holds JSON response info
|
||||
type YahooJSONResponseInfo struct {
|
||||
Count int `json:"count"`
|
||||
Created time.Time `json:"created"`
|
||||
Lang string `json:"lang"`
|
||||
}
|
||||
|
||||
// YahooJSONResponse holds Yahoo API responses
|
||||
type YahooJSONResponse struct {
|
||||
Query struct {
|
||||
YahooJSONResponseInfo
|
||||
@@ -36,46 +39,105 @@ type YahooJSONResponse struct {
|
||||
}
|
||||
}
|
||||
|
||||
// FixerResponse contains the data fields for the Fixer API response
|
||||
type FixerResponse struct {
|
||||
Base string `json:"base"`
|
||||
Date string `json:"date"`
|
||||
Rates map[string]float64 `json:"rates"`
|
||||
}
|
||||
|
||||
const (
|
||||
MAX_CURRENCY_PAIRS_PER_REQUEST = 350
|
||||
YAHOO_YQL_URL = "http://query.yahooapis.com/v1/public/yql"
|
||||
YAHOO_DATABASE = "store://datatables.org/alltableswithkeys"
|
||||
DEFAULT_CURRENCIES = "USD,AUD,EUR,CNY"
|
||||
DEFAULT_CRYPTOCURRENCIES = "BTC,LTC,ETH,DOGE,DASH,XRP,XMR"
|
||||
maxCurrencyPairsPerRequest = 350
|
||||
yahooYQLURL = "https://query.yahooapis.com/v1/public/yql?"
|
||||
yahooDatabase = "store://datatables.org/alltableswithkeys"
|
||||
fixerAPI = "http://api.fixer.io/latest"
|
||||
// DefaultCurrencies has the default minimum of FIAT values
|
||||
DefaultCurrencies = "USD,AUD,EUR,CNY"
|
||||
// DefaultCryptoCurrencies has the default minimum of crytpocurrency values
|
||||
DefaultCryptoCurrencies = "BTC,LTC,ETH,DOGE,DASH,XRP,XMR"
|
||||
)
|
||||
|
||||
// Variables for package which includes base error strings & exportable
|
||||
// queries
|
||||
var (
|
||||
CurrencyStore map[string]Rate
|
||||
CurrencyStoreFixer map[string]float64
|
||||
BaseCurrencies string
|
||||
CryptoCurrencies string
|
||||
ErrCurrencyDataNotFetched = errors.New("Yahoo currency data has not been fetched yet.")
|
||||
ErrCurrencyNotFound = errors.New("Unable to find specified currency.")
|
||||
ErrQueryingYahoo = errors.New("Unable to query Yahoo currency values.")
|
||||
ErrQueryingYahooZeroCount = errors.New("Yahoo returned zero currency data.")
|
||||
ErrCurrencyDataNotFetched = errors.New("yahoo currency data has not been fetched yet")
|
||||
ErrCurrencyNotFound = errors.New("unable to find specified currency")
|
||||
ErrQueryingYahoo = errors.New("unable to query Yahoo currency values")
|
||||
ErrQueryingYahooZeroCount = errors.New("yahoo returned zero currency data")
|
||||
YahooEnabled = true
|
||||
)
|
||||
|
||||
// SetProvider sets the currency exchange service used by the currency
|
||||
// converter
|
||||
func SetProvider(yahooEnabled bool) {
|
||||
if yahooEnabled {
|
||||
YahooEnabled = true
|
||||
return
|
||||
}
|
||||
YahooEnabled = false
|
||||
}
|
||||
|
||||
// SwapProvider swaps the currency exchange service used by the curency
|
||||
// converter
|
||||
func SwapProvider() {
|
||||
if YahooEnabled {
|
||||
YahooEnabled = false
|
||||
return
|
||||
}
|
||||
YahooEnabled = true
|
||||
}
|
||||
|
||||
// GetProvider returns the currency exchange service used by the currency
|
||||
// converter
|
||||
func GetProvider() string {
|
||||
if YahooEnabled {
|
||||
return "yahoo"
|
||||
}
|
||||
return "fixer"
|
||||
}
|
||||
|
||||
// IsDefaultCurrency checks if the currency passed in matches the default
|
||||
// FIAT currency
|
||||
func IsDefaultCurrency(currency string) bool {
|
||||
return common.StringContains(DEFAULT_CURRENCIES, common.StringToUpper(currency))
|
||||
return common.StringContains(
|
||||
DefaultCurrencies, common.StringToUpper(currency),
|
||||
)
|
||||
}
|
||||
|
||||
// IsDefaultCryptocurrency checks if the currency passed in matches the default
|
||||
// CRYPTO currency
|
||||
func IsDefaultCryptocurrency(currency string) bool {
|
||||
return common.StringContains(DEFAULT_CRYPTOCURRENCIES, common.StringToUpper(currency))
|
||||
return common.StringContains(
|
||||
DefaultCryptoCurrencies, common.StringToUpper(currency),
|
||||
)
|
||||
}
|
||||
|
||||
// IsFiatCurrency checks if the currency passed is an enabled FIAT currency
|
||||
func IsFiatCurrency(currency string) bool {
|
||||
if BaseCurrencies == "" {
|
||||
log.Println("IsFiatCurrency: BaseCurrencies string variable not populated")
|
||||
return false
|
||||
}
|
||||
return common.StringContains(BaseCurrencies, common.StringToUpper(currency))
|
||||
}
|
||||
|
||||
// IsCryptocurrency checks if the currency passed is an enabled CRYPTO currency.
|
||||
func IsCryptocurrency(currency string) bool {
|
||||
if CryptoCurrencies == "" {
|
||||
log.Println("IsCryptocurrency: CryptoCurrencies string variable not populated")
|
||||
log.Println(
|
||||
"IsCryptocurrency: CryptoCurrencies string variable not populated",
|
||||
)
|
||||
return false
|
||||
}
|
||||
return common.StringContains(CryptoCurrencies, common.StringToUpper(currency))
|
||||
}
|
||||
|
||||
// ContainsSeparator checks to see if the string passed contains "-" or "_"
|
||||
// separated strings and returns what the separators were.
|
||||
func ContainsSeparator(input string) (bool, string) {
|
||||
separators := []string{"-", "_"}
|
||||
var separatorsContainer []string
|
||||
@@ -87,11 +149,12 @@ func ContainsSeparator(input string) (bool, string) {
|
||||
}
|
||||
if len(separatorsContainer) == 0 {
|
||||
return false, ""
|
||||
} else {
|
||||
return true, strings.Join(separatorsContainer, ",")
|
||||
}
|
||||
return true, strings.Join(separatorsContainer, ",")
|
||||
}
|
||||
|
||||
// ContainsBaseCurrencyIndex checks the currency against the baseCurrencies and
|
||||
// returns a bool and its corresponding basecurrency.
|
||||
func ContainsBaseCurrencyIndex(baseCurrencies []string, currency string) (bool, string) {
|
||||
for _, x := range baseCurrencies {
|
||||
if common.StringContains(currency, x) {
|
||||
@@ -101,6 +164,8 @@ func ContainsBaseCurrencyIndex(baseCurrencies []string, currency string) (bool,
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// ContainsBaseCurrency checks the currency against the baseCurrencies and
|
||||
// returns a bool
|
||||
func ContainsBaseCurrency(baseCurrencies []string, currency string) bool {
|
||||
for _, x := range baseCurrencies {
|
||||
if common.StringContains(currency, x) {
|
||||
@@ -110,6 +175,9 @@ func ContainsBaseCurrency(baseCurrencies []string, currency string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// CheckAndAddCurrency checks the string you passed with the input string array,
|
||||
// if not already added, checks to see if it is part of the default currency
|
||||
// list and returns the appended string.
|
||||
func CheckAndAddCurrency(input []string, check string) []string {
|
||||
for _, x := range input {
|
||||
if IsDefaultCurrency(x) {
|
||||
@@ -118,40 +186,39 @@ func CheckAndAddCurrency(input []string, check string) []string {
|
||||
return input
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
return input
|
||||
}
|
||||
return input
|
||||
} else if IsDefaultCryptocurrency(x) {
|
||||
if IsDefaultCryptocurrency(check) {
|
||||
if check == x {
|
||||
return input
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
return input
|
||||
}
|
||||
} else {
|
||||
return input
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
input = append(input, check)
|
||||
return input
|
||||
}
|
||||
|
||||
// SeedCurrencyData takes the desired FIAT currency string, if not defined the
|
||||
// function will assign it the default values. The function will query
|
||||
// yahoo for the currency values and will seed currency data.
|
||||
func SeedCurrencyData(fiatCurrencies string) error {
|
||||
if fiatCurrencies == "" {
|
||||
fiatCurrencies = DEFAULT_CURRENCIES
|
||||
fiatCurrencies = DefaultCurrencies
|
||||
}
|
||||
|
||||
err := QueryYahooCurrencyValues(fiatCurrencies)
|
||||
if err != nil {
|
||||
return ErrQueryingYahoo
|
||||
if YahooEnabled {
|
||||
return QueryYahooCurrencyValues(fiatCurrencies)
|
||||
}
|
||||
|
||||
return nil
|
||||
return FetchFixerCurrencyData()
|
||||
}
|
||||
|
||||
// MakecurrencyPairs takes all supported currency and turns them into pairs.
|
||||
func MakecurrencyPairs(supportedCurrencies string) string {
|
||||
currencies := common.SplitStrings(supportedCurrencies, ",")
|
||||
pairs := []string{}
|
||||
@@ -167,42 +234,120 @@ func MakecurrencyPairs(supportedCurrencies string) string {
|
||||
return common.JoinStrings(pairs, ",")
|
||||
}
|
||||
|
||||
// ConvertCurrency for example converts $1 USD to the equivalent Japanese Yen
|
||||
// or vice versa.
|
||||
func ConvertCurrency(amount float64, from, to string) (float64, error) {
|
||||
currency := common.StringToUpper(from + to)
|
||||
from = common.StringToUpper(from)
|
||||
to = common.StringToUpper(to)
|
||||
|
||||
if CurrencyStore[currency].Name != currency {
|
||||
err := SeedCurrencyData(currency[:len(from)] + "," + currency[len(to):])
|
||||
if from == to {
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
if YahooEnabled {
|
||||
currency := from + to
|
||||
_, ok := CurrencyStore[currency]
|
||||
if !ok {
|
||||
err := SeedCurrencyData(currency[:len(from)] + "," + currency[len(to):])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
result, ok := CurrencyStore[currency]
|
||||
if !ok {
|
||||
return 0, ErrCurrencyNotFound
|
||||
}
|
||||
return amount * result.Rate, nil
|
||||
}
|
||||
|
||||
_, ok := CurrencyStoreFixer[from]
|
||||
if !ok {
|
||||
err := FetchFixerCurrencyData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
for x, y := range CurrencyStore {
|
||||
if x == currency {
|
||||
return amount * y.Rate, nil
|
||||
var resultFrom float64
|
||||
var resultTo float64
|
||||
|
||||
// First check if we're converting to USD, USD doesn't exist in the rates map
|
||||
if to == "USD" {
|
||||
resultFrom, ok = CurrencyStoreFixer[from]
|
||||
if !ok {
|
||||
return 0, ErrCurrencyNotFound
|
||||
}
|
||||
return amount / resultFrom, nil
|
||||
}
|
||||
return 0, ErrCurrencyNotFound
|
||||
|
||||
// Check to see if we're converting from USD
|
||||
if from == "USD" {
|
||||
resultTo, ok = CurrencyStoreFixer[to]
|
||||
if !ok {
|
||||
return 0, ErrCurrencyNotFound
|
||||
}
|
||||
return resultTo * amount, nil
|
||||
}
|
||||
|
||||
// Otherwise convert to USD, then to the target currency
|
||||
resultFrom, ok = CurrencyStoreFixer[from]
|
||||
if !ok {
|
||||
return 0, ErrCurrencyNotFound
|
||||
}
|
||||
|
||||
converted := amount / resultFrom
|
||||
resultTo, ok = CurrencyStoreFixer[to]
|
||||
if !ok {
|
||||
return 0, ErrCurrencyNotFound
|
||||
}
|
||||
|
||||
return converted * resultTo, nil
|
||||
}
|
||||
|
||||
func FetchYahooCurrencyData(currencyPairs []string) error {
|
||||
// FetchFixerCurrencyData seeds the variable C
|
||||
func FetchFixerCurrencyData() error {
|
||||
var result FixerResponse
|
||||
values := url.Values{}
|
||||
values.Set("q", fmt.Sprintf("SELECT * from yahoo.finance.xchange WHERE pair in (\"%s\")", common.JoinStrings(currencyPairs, ",")))
|
||||
values.Set("format", "json")
|
||||
values.Set("env", YAHOO_DATABASE)
|
||||
values.Set("base", "USD")
|
||||
url := common.EncodeURLValues(fixerAPI, values)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", YAHOO_YQL_URL, headers, strings.NewReader(values.Encode()))
|
||||
CurrencyStoreFixer = make(map[string]float64)
|
||||
|
||||
err := common.SendHTTPGetRequest(url, true, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
CurrencyStoreFixer = result.Rates
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchYahooCurrencyData seeds the variable CurrencyStore; this is a
|
||||
// map[string]Rate
|
||||
func FetchYahooCurrencyData(currencyPairs []string) error {
|
||||
values := url.Values{}
|
||||
values.Set(
|
||||
"q", fmt.Sprintf("SELECT * from yahoo.finance.xchange WHERE pair in (\"%s\")",
|
||||
common.JoinStrings(currencyPairs, ",")),
|
||||
)
|
||||
values.Set("format", "json")
|
||||
values.Set("env", yahooDatabase)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest(
|
||||
"POST", yahooYQLURL, headers, strings.NewReader(values.Encode()),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Currency recv: %s", resp)
|
||||
|
||||
yahooResp := YahooJSONResponse{}
|
||||
err = common.JSONDecode([]byte(resp), &yahooResp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -212,41 +357,19 @@ func FetchYahooCurrencyData(currencyPairs []string) error {
|
||||
}
|
||||
|
||||
for i := 0; i < yahooResp.Query.YahooJSONResponseInfo.Count; i++ {
|
||||
CurrencyStore[yahooResp.Query.Results.Rate[i].Id] = yahooResp.Query.Results.Rate[i]
|
||||
CurrencyStore[yahooResp.Query.Results.Rate[i].ID] = yahooResp.Query.Results.Rate[i]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueryYahooCurrencyValues takes in desired currencies, creates pairs then
|
||||
// uses FetchYahooCurrencyData to seed CurrencyStore
|
||||
func QueryYahooCurrencyValues(currencies string) error {
|
||||
CurrencyStore = make(map[string]Rate)
|
||||
currencyPairs := common.SplitStrings(MakecurrencyPairs(currencies), ",")
|
||||
log.Printf("%d fiat currency pairs generated. Fetching Yahoo currency data (this may take a minute)..\n", len(currencyPairs))
|
||||
var err error
|
||||
var pairs []string
|
||||
index := 0
|
||||
|
||||
if len(currencyPairs) > MAX_CURRENCY_PAIRS_PER_REQUEST {
|
||||
for index < len(currencyPairs) {
|
||||
if len(currencyPairs)-index > MAX_CURRENCY_PAIRS_PER_REQUEST {
|
||||
pairs = currencyPairs[index : index+MAX_CURRENCY_PAIRS_PER_REQUEST]
|
||||
index += MAX_CURRENCY_PAIRS_PER_REQUEST
|
||||
} else {
|
||||
pairs = currencyPairs[index:len(currencyPairs)]
|
||||
index += (len(currencyPairs) - index)
|
||||
}
|
||||
err = FetchYahooCurrencyData(pairs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pairs = currencyPairs[index:len(currencyPairs)]
|
||||
err = FetchYahooCurrencyData(pairs)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
log.Printf(
|
||||
"%d fiat currency pairs generated. Fetching Yahoo currency data (this may take a minute)..\n",
|
||||
len(currencyPairs),
|
||||
)
|
||||
return FetchYahooCurrencyData(currencyPairs)
|
||||
}
|
||||
|
||||
@@ -7,19 +7,85 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
func TestSetProvider(t *testing.T) {
|
||||
defaultVal := YahooEnabled
|
||||
expected := "yahoo"
|
||||
SetProvider(true)
|
||||
actual := GetProvider()
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed. TestGetProvider expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
SetProvider(false)
|
||||
expected = "fixer"
|
||||
actual = GetProvider()
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed. TestGetProvider expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
SetProvider(defaultVal)
|
||||
}
|
||||
|
||||
func TestSwapProvider(t *testing.T) {
|
||||
defaultVal := YahooEnabled
|
||||
expected := "fixer"
|
||||
SetProvider(true)
|
||||
SwapProvider()
|
||||
actual := GetProvider()
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed. TestGetProvider expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
SetProvider(false)
|
||||
SwapProvider()
|
||||
expected = "yahoo"
|
||||
actual = GetProvider()
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed. TestGetProvider expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
SetProvider(defaultVal)
|
||||
}
|
||||
|
||||
func TestGetProvider(t *testing.T) {
|
||||
defaultVal := YahooEnabled
|
||||
SetProvider(true)
|
||||
expected := "yahoo"
|
||||
actual := GetProvider()
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed. TestGetProvider expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
SetProvider(false)
|
||||
expected = "fixer"
|
||||
actual = GetProvider()
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed. TestGetProvider expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
SetProvider(defaultVal)
|
||||
}
|
||||
|
||||
func TestIsDefaultCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var str1, str2, str3 string = "USD", "usd", "cats123"
|
||||
|
||||
if !IsDefaultCurrency(str1) {
|
||||
t.Errorf("Test Failed. TestIsDefaultCurrency: \nCannot match currency, %s.", str1)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsDefaultCurrency: \nCannot match currency, %s.", str1,
|
||||
)
|
||||
}
|
||||
if !IsDefaultCurrency(str2) {
|
||||
t.Errorf("Test Failed. TestIsDefaultCurrency: \nCannot match currency, %s.", str2)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsDefaultCurrency: \nCannot match currency, %s.", str2,
|
||||
)
|
||||
}
|
||||
if IsDefaultCurrency(str3) {
|
||||
t.Errorf("Test Failed. TestIsDefaultCurrency: \nFunction return is incorrect with, %s.", str3)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsDefaultCurrency: \nFunction return is incorrect with, %s.",
|
||||
str3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,47 +95,76 @@ func TestIsDefaultCryptocurrency(t *testing.T) {
|
||||
var str1, str2, str3 string = "BTC", "btc", "dogs123"
|
||||
|
||||
if !IsDefaultCryptocurrency(str1) {
|
||||
t.Errorf("Test Failed. TestIsDefaultCryptocurrency: \nCannot match currency, %s.", str1)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsDefaultCryptocurrency: \nCannot match currency, %s.",
|
||||
str1,
|
||||
)
|
||||
}
|
||||
if !IsDefaultCryptocurrency(str2) {
|
||||
t.Errorf("Test Failed. TestIsDefaultCryptocurrency: \nCannot match currency, %s.", str2)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsDefaultCryptocurrency: \nCannot match currency, %s.",
|
||||
str2,
|
||||
)
|
||||
}
|
||||
if IsDefaultCryptocurrency(str3) {
|
||||
t.Errorf("Test Failed. TestIsDefaultCryptocurrency: \nFunction return is incorrect with, %s.", str3)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsDefaultCryptocurrency: \nFunction return is incorrect with, %s.",
|
||||
str3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsFiatCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if IsFiatCurrency("") {
|
||||
t.Error("Test failed. TestIsFiatCurrency returned true on an empty string")
|
||||
}
|
||||
|
||||
BaseCurrencies = "USD,AUD"
|
||||
var str1, str2, str3 string = "BTC", "USD", "birds123"
|
||||
|
||||
if IsFiatCurrency(str1) {
|
||||
t.Errorf("Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str1)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str1,
|
||||
)
|
||||
}
|
||||
if !IsFiatCurrency(str2) {
|
||||
t.Errorf("Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str2)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str2,
|
||||
)
|
||||
}
|
||||
if IsFiatCurrency(str3) {
|
||||
t.Errorf("Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str3)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCryptocurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if IsCryptocurrency("") {
|
||||
t.Error("Test failed. TestIsCryptocurrency returned true on an empty string")
|
||||
}
|
||||
|
||||
CryptoCurrencies = "BTC,LTC,DASH"
|
||||
var str1, str2, str3 string = "USD", "BTC", "pterodactyl123"
|
||||
|
||||
if IsCryptocurrency(str1) {
|
||||
t.Errorf("Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str1)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str1,
|
||||
)
|
||||
}
|
||||
if !IsCryptocurrency(str2) {
|
||||
t.Errorf("Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str2)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str2,
|
||||
)
|
||||
}
|
||||
if IsCryptocurrency(str3) {
|
||||
t.Errorf("Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str3)
|
||||
t.Errorf(
|
||||
"Test Failed. TestIsFiatCurrency: \nCannot match currency, %s.", str3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,19 +175,28 @@ func TestContainsSeparator(t *testing.T) {
|
||||
|
||||
doesIt, whatIsIt := ContainsSeparator(str1)
|
||||
if doesIt != true || whatIsIt != "-" {
|
||||
t.Errorf("Test Failed. ContainsSeparator: \nCannot find separator, %s.", str1)
|
||||
t.Errorf(
|
||||
"Test Failed. ContainsSeparator: \nCannot find separator, %s.", str1,
|
||||
)
|
||||
}
|
||||
doesIt2, whatIsIt2 := ContainsSeparator(str2)
|
||||
if doesIt2 != true || whatIsIt2 != "_" {
|
||||
t.Errorf("Test Failed. ContainsSeparator: \nCannot find separator, %s.", str2)
|
||||
t.Errorf(
|
||||
"Test Failed. ContainsSeparator: \nCannot find separator, %s.", str2,
|
||||
)
|
||||
}
|
||||
doesIt3, whatIsIt3 := ContainsSeparator(str3)
|
||||
if doesIt3 != true || len(whatIsIt3) != 3 {
|
||||
t.Errorf("Test Failed. ContainsSeparator: \nCannot find or incorrect separator, %s.", str3)
|
||||
t.Errorf(
|
||||
"Test Failed. ContainsSeparator: \nCannot find or incorrect separator, %s.",
|
||||
str3,
|
||||
)
|
||||
}
|
||||
doesIt4, whatIsIt4 := ContainsSeparator(str4)
|
||||
if doesIt4 != false || whatIsIt4 != "" {
|
||||
t.Errorf("Test Failed. ContainsSeparator: \nReturn Issues with string, %s.", str3)
|
||||
t.Errorf(
|
||||
"Test Failed. ContainsSeparator: \nReturn Issues with string, %s.", str3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +208,17 @@ func TestContainsBaseCurrencyIndex(t *testing.T) {
|
||||
|
||||
isIt, whatIsIt := ContainsBaseCurrencyIndex(baseCurrencies, currency1)
|
||||
if !isIt && whatIsIt != "USD" {
|
||||
t.Errorf("Test Failed. ContainsBaseCurrencyIndex: \nReturned: %t & %s, with Currency as %s.", isIt, whatIsIt, currency1)
|
||||
t.Errorf(
|
||||
"Test Failed. ContainsBaseCurrencyIndex: \nReturned: %t & %s, with Currency as %s.",
|
||||
isIt, whatIsIt, currency1,
|
||||
)
|
||||
}
|
||||
isIt2, whatIsIt2 := ContainsBaseCurrencyIndex(baseCurrencies, currency2)
|
||||
if isIt2 && whatIsIt2 != "DINGDONG" {
|
||||
t.Errorf("Test Failed. ContainsBaseCurrencyIndex: \nReturned: %t & %s, with Currency as %s.", isIt2, whatIsIt2, currency2)
|
||||
t.Errorf(
|
||||
"Test Failed. ContainsBaseCurrencyIndex: \nReturned: %t & %s, with Currency as %s.",
|
||||
isIt2, whatIsIt2, currency2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +230,15 @@ func TestContainsBaseCurrency(t *testing.T) {
|
||||
|
||||
isIt := ContainsBaseCurrency(baseCurrencies, currency1)
|
||||
if !isIt {
|
||||
t.Errorf("Test Failed. ContainsBaseCurrency: \nReturned: %t, with Currency as %s.", isIt, currency1)
|
||||
t.Errorf("Test Failed. ContainsBaseCurrency: \nReturned: %t, with Currency as %s.",
|
||||
isIt, currency1,
|
||||
)
|
||||
}
|
||||
isIt2 := ContainsBaseCurrency(baseCurrencies, currency2)
|
||||
if isIt2 {
|
||||
t.Errorf("Test Failed. ContainsBaseCurrency: \nReturned: %t, with Currency as %s.", isIt2, currency2)
|
||||
t.Errorf("Test Failed. ContainsBaseCurrency: \nReturned: %t, with Currency as %s.",
|
||||
isIt2, currency2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +247,7 @@ func TestCheckAndAddCurrency(t *testing.T) {
|
||||
|
||||
inputFiat := []string{"USD", "AUD", "EUR"}
|
||||
inputCrypto := []string{"BTC", "LTC", "ETH", "DOGE", "DASH", "XRP"}
|
||||
testError := []string{"Testy"}
|
||||
fiat := "USD"
|
||||
fiatIncrease := "CNY"
|
||||
crypto := "LTC"
|
||||
@@ -141,66 +256,114 @@ func TestCheckAndAddCurrency(t *testing.T) {
|
||||
|
||||
appendedString := CheckAndAddCurrency(inputFiat, fiat)
|
||||
if len(appendedString) > len(inputFiat) {
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.", fiat)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.",
|
||||
fiat,
|
||||
)
|
||||
}
|
||||
appendedString = CheckAndAddCurrency(inputFiat, fiatIncrease)
|
||||
if len(appendedString) <= len(inputFiat) {
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.", fiatIncrease)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.",
|
||||
fiatIncrease,
|
||||
)
|
||||
}
|
||||
appendedString = CheckAndAddCurrency(inputFiat, crypto)
|
||||
if len(appendedString) > len(inputFiat) {
|
||||
t.Log(appendedString)
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.", crypto)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.",
|
||||
crypto,
|
||||
)
|
||||
}
|
||||
appendedString = CheckAndAddCurrency(inputFiat, obtuse)
|
||||
if len(appendedString) > len(inputFiat) {
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.", obtuse)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputFiat, currency as %s.",
|
||||
obtuse,
|
||||
)
|
||||
}
|
||||
|
||||
appendedString = CheckAndAddCurrency(inputCrypto, crypto)
|
||||
if len(appendedString) > len(inputCrypto) {
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.", crypto)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.",
|
||||
crypto,
|
||||
)
|
||||
}
|
||||
appendedString = CheckAndAddCurrency(inputCrypto, cryptoIncrease)
|
||||
if len(appendedString) <= len(inputCrypto) {
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.", cryptoIncrease)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.",
|
||||
cryptoIncrease,
|
||||
)
|
||||
}
|
||||
appendedString = CheckAndAddCurrency(inputCrypto, fiat)
|
||||
if len(appendedString) > len(inputCrypto) {
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.", fiat)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.",
|
||||
fiat,
|
||||
)
|
||||
}
|
||||
appendedString = CheckAndAddCurrency(inputCrypto, obtuse)
|
||||
if len(appendedString) > len(inputCrypto) {
|
||||
t.Errorf("Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.", obtuse)
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputCrytpo, currency as %s.",
|
||||
obtuse,
|
||||
)
|
||||
}
|
||||
|
||||
appendedString = CheckAndAddCurrency(testError, "USD")
|
||||
if appendedString[0] != testError[0] {
|
||||
t.Errorf(
|
||||
"Test Failed. CheckAndAddCurrency: Error with inputCrytpo, basecurrency as %s.",
|
||||
testError,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeedCurrencyData(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
SetProvider(true)
|
||||
currencyRequestDefault := ""
|
||||
currencyRequestUSDAUD := "USD,AUD"
|
||||
currencyRequestObtuse := "WigWham"
|
||||
|
||||
err := SeedCurrencyData(currencyRequestDefault)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. SeedCurrencyData: Error %s with currency as %s.", err, currencyRequestDefault)
|
||||
t.Errorf(
|
||||
"Test Failed. SeedCurrencyData: Error %s with currency as %s.",
|
||||
err, currencyRequestDefault,
|
||||
)
|
||||
}
|
||||
err2 := SeedCurrencyData(currencyRequestUSDAUD)
|
||||
if err2 != nil {
|
||||
t.Errorf("Test Failed. SeedCurrencyData: Error %s with currency as %s.", err2, currencyRequestUSDAUD)
|
||||
t.Errorf(
|
||||
"Test Failed. SeedCurrencyData: Error %s with currency as %s.",
|
||||
err2, currencyRequestUSDAUD,
|
||||
)
|
||||
}
|
||||
err3 := SeedCurrencyData(currencyRequestObtuse)
|
||||
if err3 == nil {
|
||||
t.Errorf("Test Failed. SeedCurrencyData: Error %s with currency as %s.", err3, currencyRequestObtuse)
|
||||
t.Errorf(
|
||||
"Test Failed. SeedCurrencyData: Error %s with currency as %s.",
|
||||
err3, currencyRequestObtuse,
|
||||
)
|
||||
}
|
||||
|
||||
SetProvider(false)
|
||||
err = SeedCurrencyData("")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. SeedCurrencyData via Fixer. Error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakecurrencyPairs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lengthDefault := len(common.SplitStrings(DEFAULT_CURRENCIES, ","))
|
||||
fiatPairsLength := len(common.SplitStrings(MakecurrencyPairs(DEFAULT_CURRENCIES), ","))
|
||||
lengthDefault := len(common.SplitStrings(DefaultCurrencies, ","))
|
||||
fiatPairsLength := len(
|
||||
common.SplitStrings(MakecurrencyPairs(DefaultCurrencies), ","),
|
||||
)
|
||||
|
||||
if lengthDefault*(lengthDefault-1) > fiatPairsLength {
|
||||
t.Error("Test Failed. MakecurrencyPairs: Error, mismatched length")
|
||||
@@ -208,34 +371,78 @@ func TestMakecurrencyPairs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConvertCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fiatCurrencies := DEFAULT_CURRENCIES
|
||||
SetProvider(true)
|
||||
fiatCurrencies := DefaultCurrencies
|
||||
for _, currencyFrom := range common.SplitStrings(fiatCurrencies, ",") {
|
||||
for _, currencyTo := range common.SplitStrings(fiatCurrencies, ",") {
|
||||
if currencyFrom == currencyTo {
|
||||
continue
|
||||
} else {
|
||||
floatyMcfloat, err := ConvertCurrency(1000, currencyFrom, currencyTo)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. ConvertCurrency: Error %s with return: %.2f Currency 1: %s Currency 2: %s",
|
||||
err, floatyMcfloat, currencyFrom, currencyTo)
|
||||
}
|
||||
if reflect.TypeOf(floatyMcfloat).String() != "float64" {
|
||||
t.Error("Test Failed. ConvertCurrency: Error, incorrect return type")
|
||||
}
|
||||
if floatyMcfloat <= 0 {
|
||||
t.Error("Test Failed. ConvertCurrency: Error, negative return or a serious issue with current fiat")
|
||||
}
|
||||
floatyMcfloat, err := ConvertCurrency(1000, currencyFrom, currencyTo)
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Test Failed. ConvertCurrency: Error %s with return: %.2f Currency 1: %s Currency 2: %s",
|
||||
err, floatyMcfloat, currencyFrom, currencyTo,
|
||||
)
|
||||
}
|
||||
if reflect.TypeOf(floatyMcfloat).String() != "float64" {
|
||||
t.Error("Test Failed. ConvertCurrency: Error, incorrect return type")
|
||||
}
|
||||
if floatyMcfloat <= 0 {
|
||||
t.Error(
|
||||
"Test Failed. ConvertCurrency: Error, negative return or a serious issue with current fiat",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetProvider(false)
|
||||
_, err := ConvertCurrency(1000, "USD", "AUD")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. ConvertCurrency USD -> AUD. Error %s", err)
|
||||
}
|
||||
|
||||
_, err = ConvertCurrency(1000, "AUD", "USD")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. ConvertCurrency AUD -> AUD. Error %s", err)
|
||||
}
|
||||
|
||||
_, err = ConvertCurrency(1000, "CNY", "AUD")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. ConvertCurrency USD -> AUD. Error %s", err)
|
||||
}
|
||||
|
||||
// Test non-existant currencies
|
||||
|
||||
_, err = ConvertCurrency(1000, "ASDF", "USD")
|
||||
if err == nil {
|
||||
t.Errorf("Test failed. ConvertCurrency non-existant currency -> USD. Error %s", err)
|
||||
}
|
||||
|
||||
_, err = ConvertCurrency(1000, "USD", "ASDF")
|
||||
if err == nil {
|
||||
t.Errorf("Test failed. ConvertCurrency USD -> non-existant currency. Error %s", err)
|
||||
}
|
||||
|
||||
_, err = ConvertCurrency(1000, "CNY", "UAHF")
|
||||
if err == nil {
|
||||
t.Errorf("Test failed. ConvertCurrency non-USD currency CNY -> non-existant currency. Error %s", err)
|
||||
}
|
||||
|
||||
_, err = ConvertCurrency(1000, "UASF", "UAHF")
|
||||
if err == nil {
|
||||
t.Errorf("Test failed. ConvertCurrency non-existant currency -> non-existant currency. Error %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchFixerCurrencyData(t *testing.T) {
|
||||
err := FetchFixerCurrencyData()
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. FetchFixerCurrencyData returned %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchYahooCurrencyData(t *testing.T) {
|
||||
t.Parallel()
|
||||
var fetchData []string
|
||||
fiatCurrencies := DEFAULT_CURRENCIES
|
||||
fiatCurrencies := DefaultCurrencies
|
||||
|
||||
for _, currencyOne := range common.SplitStrings(fiatCurrencies, ",") {
|
||||
for _, currencyTwo := range common.SplitStrings(fiatCurrencies, ",") {
|
||||
@@ -253,16 +460,13 @@ func TestFetchYahooCurrencyData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryYahooCurrencyValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := QueryYahooCurrencyValues(DEFAULT_CURRENCIES)
|
||||
err := QueryYahooCurrencyValues(DefaultCurrencies)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. QueryYahooCurrencyValues: Error, %s", err)
|
||||
}
|
||||
|
||||
err2 := QueryYahooCurrencyValues(DEFAULT_CRYPTOCURRENCIES)
|
||||
if err2 == nil {
|
||||
t.Errorf("Test Failed. QueryYahooCurrencyValues: Error, %s", err2)
|
||||
err = QueryYahooCurrencyValues(DefaultCryptoCurrencies)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. QueryYahooCurrencyValues: Error, %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,39 +1,78 @@
|
||||
package pair
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CurrencyItem is an exported string with methods to manipulate the data instead
|
||||
// of using array/slice access modifiers
|
||||
type CurrencyItem string
|
||||
|
||||
// Lower converts the CurrencyItem object c to lowercase
|
||||
func (c CurrencyItem) Lower() CurrencyItem {
|
||||
return CurrencyItem(strings.ToLower(string(c)))
|
||||
}
|
||||
|
||||
// Upper converts the CurrencyItem object c to uppercase
|
||||
func (c CurrencyItem) Upper() CurrencyItem {
|
||||
return CurrencyItem(strings.ToUpper(string(c)))
|
||||
}
|
||||
|
||||
// String converts the CurrencyItem object c to string
|
||||
func (c CurrencyItem) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// CurrencyPair holds currency pair information
|
||||
type CurrencyPair struct {
|
||||
Delimiter string `json:"delimiter"`
|
||||
FirstCurrency CurrencyItem `json:"first_currency"`
|
||||
SecondCurrency CurrencyItem `json:"second_currency"`
|
||||
}
|
||||
|
||||
// GetFirstCurrency returns the first currency item
|
||||
func (c CurrencyPair) GetFirstCurrency() CurrencyItem {
|
||||
return c.FirstCurrency
|
||||
}
|
||||
|
||||
// GetSecondCurrency returns the second currency item
|
||||
func (c CurrencyPair) GetSecondCurrency() CurrencyItem {
|
||||
return c.SecondCurrency
|
||||
}
|
||||
|
||||
// Pair returns a currency pair string
|
||||
func (c CurrencyPair) Pair() CurrencyItem {
|
||||
return c.FirstCurrency + CurrencyItem(c.Delimiter) + c.SecondCurrency
|
||||
}
|
||||
|
||||
// Display formats and returns the currency based on user preferences,
|
||||
// overriding the default Pair() display
|
||||
func (c CurrencyPair) Display(delimiter string, uppercase bool) CurrencyItem {
|
||||
var pair CurrencyItem
|
||||
|
||||
if delimiter != "" {
|
||||
pair = c.FirstCurrency + CurrencyItem(delimiter) + c.SecondCurrency
|
||||
} else {
|
||||
pair = c.FirstCurrency + c.SecondCurrency
|
||||
}
|
||||
|
||||
if uppercase {
|
||||
return pair.Upper()
|
||||
}
|
||||
return pair.Lower()
|
||||
}
|
||||
|
||||
// Equal compares two currency pairs and returns whether or not they are equal
|
||||
func (c CurrencyPair) Equal(p CurrencyPair) bool {
|
||||
if c.FirstCurrency.Upper() == p.FirstCurrency.Upper() &&
|
||||
c.SecondCurrency.Upper() == p.SecondCurrency.Upper() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewCurrencyPairDelimiter splits the desired currency string at delimeter,
|
||||
// the returns a CurrencyPair struct
|
||||
func NewCurrencyPairDelimiter(currency, delimiter string) CurrencyPair {
|
||||
result := strings.Split(currency, delimiter)
|
||||
return CurrencyPair{
|
||||
@@ -43,6 +82,7 @@ func NewCurrencyPairDelimiter(currency, delimiter string) CurrencyPair {
|
||||
}
|
||||
}
|
||||
|
||||
// NewCurrencyPair returns a CurrencyPair without a delimiter
|
||||
func NewCurrencyPair(firstCurrency, secondCurrency string) CurrencyPair {
|
||||
return CurrencyPair{
|
||||
FirstCurrency: CurrencyItem(firstCurrency),
|
||||
@@ -50,10 +90,22 @@ func NewCurrencyPair(firstCurrency, secondCurrency string) CurrencyPair {
|
||||
}
|
||||
}
|
||||
|
||||
// NewCurrencyPairFromIndex returns a CurrencyPair via a currency string and
|
||||
// specific index
|
||||
func NewCurrencyPairFromIndex(currency, index string) CurrencyPair {
|
||||
i := strings.Index(currency, index)
|
||||
if i == 0 {
|
||||
return NewCurrencyPair(currency[0:len(index)], currency[len(index):])
|
||||
}
|
||||
return NewCurrencyPair(currency[0:i], currency[i:])
|
||||
}
|
||||
|
||||
// NewCurrencyPairFromString converts currency string into a new CurrencyPair
|
||||
// with or without delimeter
|
||||
func NewCurrencyPairFromString(currency string) CurrencyPair {
|
||||
delmiters := []string{"_", "-"}
|
||||
delimiters := []string{"_", "-"}
|
||||
var delimiter string
|
||||
for _, x := range delmiters {
|
||||
for _, x := range delimiters {
|
||||
if strings.Contains(currency, x) {
|
||||
delimiter = x
|
||||
return NewCurrencyPairDelimiter(currency, delimiter)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package pair
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
func TestLower(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -43,7 +41,10 @@ func TestGetFirstCurrency(t *testing.T) {
|
||||
actual := pair.GetFirstCurrency()
|
||||
expected := CurrencyItem("BTC")
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. GetFirstCurrency(): %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. GetFirstCurrency(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +54,10 @@ func TestGetSecondCurrency(t *testing.T) {
|
||||
actual := pair.GetSecondCurrency()
|
||||
expected := CurrencyItem("USD")
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. GetSecondCurrency(): %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. GetSecondCurrency(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +67,65 @@ func TestPair(t *testing.T) {
|
||||
actual := pair.Pair()
|
||||
expected := CurrencyItem("BTCUSD")
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. Pair(): %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplay(t *testing.T) {
|
||||
t.Parallel()
|
||||
pair := NewCurrencyPairDelimiter("BTC-USD", "-")
|
||||
actual := pair.Pair()
|
||||
expected := CurrencyItem("BTC-USD")
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
|
||||
actual = pair.Display("", false)
|
||||
expected = CurrencyItem("btcusd")
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
|
||||
actual = pair.Display("~", true)
|
||||
expected = CurrencyItem("BTC~USD")
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
pair := NewCurrencyPair("BTC", "USD")
|
||||
secondPair := NewCurrencyPair("btc", "uSd")
|
||||
actual := pair.Equal(secondPair)
|
||||
expected := true
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"Test failed. Equal(): %v was not equal to expected value: %v",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
|
||||
secondPair.SecondCurrency = "ETH"
|
||||
actual = pair.Equal(secondPair)
|
||||
expected = false
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"Test failed. Equal(): %v was not equal to expected value: %v",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +135,10 @@ func TestNewCurrencyPair(t *testing.T) {
|
||||
actual := pair.Pair()
|
||||
expected := CurrencyItem("BTCUSD")
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. Pair(): %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,13 +148,53 @@ func TestNewCurrencyPairDelimiter(t *testing.T) {
|
||||
actual := pair.Pair()
|
||||
expected := CurrencyItem("BTC-USD")
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. Pair(): %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
|
||||
actual = CurrencyItem(pair.Delimiter)
|
||||
expected = "-"
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. Delmiter: %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. Delmiter: %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NewCurrencyPairFromIndex returns a CurrencyPair via a currency string and
|
||||
// specific index
|
||||
func TestNewCurrencyPairFromIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
currency := "BTCUSD"
|
||||
index := "BTC"
|
||||
|
||||
pair := NewCurrencyPairFromIndex(currency, index)
|
||||
pair.Delimiter = "-"
|
||||
actual := pair.Pair()
|
||||
|
||||
expected := CurrencyItem("BTC-USD")
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
|
||||
currency = "DOGEBTC"
|
||||
|
||||
pair = NewCurrencyPairFromIndex(currency, index)
|
||||
pair.Delimiter = "-"
|
||||
actual = pair.Pair()
|
||||
|
||||
expected = CurrencyItem("DOGE-BTC")
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +205,10 @@ func TestNewCurrencyPairFromString(t *testing.T) {
|
||||
actual := pair.Pair()
|
||||
expected := CurrencyItem("BTC-USD")
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. Pair(): %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
|
||||
pairStr = "BTCUSD"
|
||||
@@ -108,6 +216,9 @@ func TestNewCurrencyPairFromString(t *testing.T) {
|
||||
actual = pair.Pair()
|
||||
expected = CurrencyItem("BTCUSD")
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. Pair(): %s was not equal to expected value: %s", actual, expected)
|
||||
t.Errorf(
|
||||
"Test failed. Pair(): %s was not equal to expected value: %s",
|
||||
actual, expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
125
currency/symbol/symbol.go
Normal file
125
currency/symbol/symbol.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package symbol
|
||||
|
||||
import "errors"
|
||||
|
||||
// symbols map holds the currency name and symbol mappings
|
||||
var symbols = map[string]string{
|
||||
"ALL": "Lek",
|
||||
"AFN": "؋",
|
||||
"ARS": "$",
|
||||
"AWG": "ƒ",
|
||||
"AUD": "$",
|
||||
"AZN": "ман",
|
||||
"BSD": "$",
|
||||
"BBD": "$",
|
||||
"BYN": "Br",
|
||||
"BZD": "BZ$",
|
||||
"BMD": "$",
|
||||
"BOB": "$b",
|
||||
"BAM": "KM",
|
||||
"BWP": "P",
|
||||
"BGN": "лв",
|
||||
"BRL": "R$",
|
||||
"BND": "$",
|
||||
"KHR": "៛",
|
||||
"CAD": "$",
|
||||
"KYD": "$",
|
||||
"CLP": "$",
|
||||
"CNY": "¥",
|
||||
"COP": "$",
|
||||
"CRC": "₡",
|
||||
"HRK": "kn",
|
||||
"CUP": "₱",
|
||||
"CZK": "Kč",
|
||||
"DKK": "kr",
|
||||
"DOP": "RD$",
|
||||
"XCD": "$",
|
||||
"EGP": "£",
|
||||
"SVC": "$",
|
||||
"EUR": "€",
|
||||
"FKP": "£",
|
||||
"FJD": "$",
|
||||
"GHS": "¢",
|
||||
"GIP": "£",
|
||||
"GTQ": "Q",
|
||||
"GGP": "£",
|
||||
"GYD": "$",
|
||||
"HNL": "L",
|
||||
"HKD": "$",
|
||||
"HUF": "Ft",
|
||||
"ISK": "kr",
|
||||
"INR": "₹",
|
||||
"IDR": "Rp",
|
||||
"IRR": "﷼",
|
||||
"IMP": "£",
|
||||
"ILS": "₪",
|
||||
"JMD": "J$",
|
||||
"JPY": "¥",
|
||||
"JEP": "£",
|
||||
"KZT": "лв",
|
||||
"KPW": "₩",
|
||||
"KRW": "₩",
|
||||
"KGS": "лв",
|
||||
"LAK": "₭",
|
||||
"LBP": "£",
|
||||
"LRD": "$",
|
||||
"MKD": "ден",
|
||||
"MYR": "RM",
|
||||
"MUR": "₨",
|
||||
"MXN": "$",
|
||||
"MNT": "₮",
|
||||
"MZN": "MT",
|
||||
"NAD": "$",
|
||||
"NPR": "₨",
|
||||
"ANG": "ƒ",
|
||||
"NZD": "$",
|
||||
"NIO": "C$",
|
||||
"NGN": "₦",
|
||||
"NOK": "kr",
|
||||
"OMR": "﷼",
|
||||
"PKR": "₨",
|
||||
"PAB": "B/.",
|
||||
"PYG": "Gs",
|
||||
"PEN": "S/.",
|
||||
"PHP": "₱",
|
||||
"PLN": "zł",
|
||||
"QAR": "﷼",
|
||||
"RON": "lei",
|
||||
"RUB": "₽",
|
||||
"SHP": "£",
|
||||
"SAR": "﷼",
|
||||
"RSD": "Дин.",
|
||||
"SCR": "₨",
|
||||
"SGD": "$",
|
||||
"SBD": "$",
|
||||
"SOS": "S",
|
||||
"ZAR": "R",
|
||||
"LKR": "₨",
|
||||
"SEK": "kr",
|
||||
"CHF": "CHF",
|
||||
"SRD": "$",
|
||||
"SYP": "£",
|
||||
"TWD": "NT$",
|
||||
"THB": "฿",
|
||||
"TTD": "TT$",
|
||||
"TRY": "₺",
|
||||
"TVD": "$",
|
||||
"UAH": "₴",
|
||||
"GBP": "£",
|
||||
"USD": "$",
|
||||
"UYU": "$U",
|
||||
"UZS": "лв",
|
||||
"VEF": "Bs",
|
||||
"VND": "₫",
|
||||
"YER": "﷼",
|
||||
"ZWD": "Z$",
|
||||
}
|
||||
|
||||
// GetSymbolByCurrencyName returns a currency symbol
|
||||
func GetSymbolByCurrencyName(currency string) (string, error) {
|
||||
result, ok := symbols[currency]
|
||||
if !ok {
|
||||
return "", errors.New("currency symbol not found")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
21
currency/symbol/symbol_test.go
Normal file
21
currency/symbol/symbol_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package symbol
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetSymbolByCurrencyName(t *testing.T) {
|
||||
expected := "₩"
|
||||
actual, err := GetSymbolByCurrencyName("KPW")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. TestGetSymbolByCurrencyName error: %s", err)
|
||||
}
|
||||
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed. TestGetSymbolByCurrencyName differing values")
|
||||
}
|
||||
|
||||
_, err = GetSymbolByCurrencyName("BLAH")
|
||||
if err == nil {
|
||||
t.Errorf("Test failed. TestGetSymbolByCurrencyNam returned nil on non-existant currency")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ In order to maintain a consistent style across the codebase, the following codin
|
||||
- Function names using acronyms are capitilised (func SendHTTPRequest()).
|
||||
- Variable names use CamelCase (var someVar()).
|
||||
- Coding style uses gofmt.
|
||||
- Const variables are capitilised.
|
||||
- Const variables are CamelCase depending on exported items.
|
||||
- In line with gofmt, for loops and if statements don't require paranthesis.
|
||||
|
||||
Block style example:
|
||||
|
||||
@@ -2,67 +2,98 @@ package events
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-/gocryptotrader/smsglobal"
|
||||
)
|
||||
|
||||
var (
|
||||
loaded = false
|
||||
)
|
||||
|
||||
func testSetup(t *testing.T) {
|
||||
if !loaded {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig("")
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. Failed to load config %s", err)
|
||||
}
|
||||
smsglobal.New(cfg.SMS.Username, cfg.SMS.Password, cfg.Name, cfg.SMS.Contacts)
|
||||
loaded = true
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddEvent(t *testing.T) {
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
testSetup(t)
|
||||
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil && eventID != 0 {
|
||||
t.Errorf("Test Failed. AddEvent: Error, %s", err)
|
||||
}
|
||||
eventID, err = AddEvent("ANXX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
eventID, err = AddEvent("ANXX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Exchange")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "prices", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
eventID, err = AddEvent("ANX", "prices", ">,==", pair, "SPOT", actionTest)
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Item")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "price", "3===D", "BTC", "LTC", ACTION_TEST)
|
||||
eventID, err = AddEvent("ANX", "price", "3===D", pair, "SPOT", actionTest)
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Condition")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "price", ">,==", "BTC", "LTC", "console_prints")
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Action")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "price", ">,==", "BATMAN", "ROBIN", ACTION_TEST)
|
||||
eventID, err = AddEvent("ANX", "price", ">,==", pair, "SPOT", "console_prints")
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Action")
|
||||
}
|
||||
|
||||
if !RemoveEvent(eventID) {
|
||||
t.Error("Test Failed. RemoveEvent: Error, error removing event")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveEvent(t *testing.T) {
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
testSetup(t)
|
||||
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil && eventID != 0 {
|
||||
t.Errorf("Test Failed. RemoveEvent: Error, %s", err)
|
||||
}
|
||||
if !RemoveEvent(eventID) {
|
||||
t.Error("Test Failed. RemoveEvent: Error, error removing event")
|
||||
}
|
||||
if RemoveEvent(1234) {
|
||||
t.Error("Test Failed. RemoveEvent: Error, error removing event")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEventCounter(t *testing.T) {
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
testSetup(t)
|
||||
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
|
||||
}
|
||||
two, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
two, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
|
||||
}
|
||||
three, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
three, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
|
||||
}
|
||||
|
||||
Events[three-1].Executed = true
|
||||
|
||||
total, _ := GetEventCounter()
|
||||
if total <= 0 {
|
||||
t.Errorf("Test Failed. GetEventCounter: Total = %d", total)
|
||||
}
|
||||
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. GetEventCounter: Error, error removing event")
|
||||
}
|
||||
@@ -80,84 +111,205 @@ func TestGetEventCounter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExecuteAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
testSetup(t)
|
||||
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
}
|
||||
isExecuted := Events[one].ExecuteAction()
|
||||
if !isExecuted {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
|
||||
action := actionSMSNotify + "," + "ALL"
|
||||
one, err = AddEvent("ANX", "price", ">,==", pair, "SPOT", action)
|
||||
if err != nil {
|
||||
t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
}
|
||||
|
||||
isExecuted = Events[one].ExecuteAction()
|
||||
if !isExecuted {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
|
||||
action = actionSMSNotify + "," + "StyleGherkin"
|
||||
one, err = AddEvent("ANX", "price", ">,==", pair, "SPOT", action)
|
||||
if err != nil {
|
||||
t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
}
|
||||
|
||||
isExecuted = Events[one].ExecuteAction()
|
||||
if !isExecuted {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
// More tests when ExecuteAction is expanded
|
||||
}
|
||||
|
||||
func TestEventToString(t *testing.T) {
|
||||
t.Parallel()
|
||||
testSetup(t)
|
||||
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. EventToString: Error, %s", err)
|
||||
}
|
||||
|
||||
eventString := Events[one].EventToString()
|
||||
if eventString != "If the BTCLTC price on ANX is > == then ACTION_TEST." {
|
||||
eventString := Events[one].String()
|
||||
if eventString != "If the BTCUSD [SPOT] price on ANX is > == then ACTION_TEST." {
|
||||
t.Error("Test Failed. EventToString: Error, incorrect return string")
|
||||
}
|
||||
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. EventToString: Error, error removing event")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCheckCondition(t *testing.T) { //error handling needs to be implemented
|
||||
t.Parallel()
|
||||
func TestCheckCondition(t *testing.T) {
|
||||
testSetup(t)
|
||||
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
|
||||
// Test invalid currency pair
|
||||
newPair := pair.NewCurrencyPair("A", "B")
|
||||
one, err := AddEvent("ANX", "price", ">=,10", newPair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. EventToString: Error, %s", err)
|
||||
t.Errorf("Test Failed. CheckCondition: Error, %s", err)
|
||||
}
|
||||
conditionBool := Events[one].CheckCondition()
|
||||
if conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
conditionBool := Events[one].CheckCondition()
|
||||
if conditionBool { //check once error handling is implemented
|
||||
t.Error("Test Failed. EventToString: Error, wrong conditional.")
|
||||
// Test last price == 0
|
||||
var tickerNew ticker.Price
|
||||
tickerNew.Last = 0
|
||||
newPair = pair.NewCurrencyPair("BTC", "USD")
|
||||
ticker.ProcessTicker("ANX", newPair, tickerNew, ticker.Spot)
|
||||
Events[one].Pair = newPair
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last pricce > 0 and conditional logic
|
||||
tickerNew.Last = 11
|
||||
ticker.ProcessTicker("ANX", newPair, tickerNew, ticker.Spot)
|
||||
Events[one].Condition = ">,10"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last price >= 10
|
||||
Events[one].Condition = ">=,10"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last price <= 10
|
||||
Events[one].Condition = "<,100"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last price <= 10
|
||||
Events[one].Condition = "<=,100"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
Events[one].Condition = "==,11"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
Events[one].Condition = "^,11"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. EventToString: Error, error removing event")
|
||||
t.Error("Test Failed. CheckCondition: Error, error removing event")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIsValidEvent(t *testing.T) {
|
||||
err := IsValidEvent("ANX", "price", ">,==", ACTION_TEST)
|
||||
testSetup(t)
|
||||
|
||||
err := IsValidEvent("ANX", "price", ">,==", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. IsValidExchange: Error %s", err)
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
err = IsValidEvent("ANX", "price", ">,", actionTest)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
err = IsValidEvent("ANX", "Testy", ">,==", actionTest)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
err = IsValidEvent("Testys", "price", ">,==", actionTest)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
|
||||
action := "blah,blah"
|
||||
err = IsValidEvent("ANX", "price", ">=,10", action)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
|
||||
action = "SMS,blah"
|
||||
err = IsValidEvent("ANX", "price", ">=,10", action)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
|
||||
//Function tests need to appended to this function when more actions are
|
||||
//implemented
|
||||
}
|
||||
|
||||
func TestCheckEvents(t *testing.T) { //Add error handling
|
||||
//CheckEvents() //check once error handling is implemented
|
||||
func TestCheckEvents(t *testing.T) {
|
||||
testSetup(t)
|
||||
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
_, err := AddEvent("ANX", "price", ">=,10", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestChcheckEvents add event")
|
||||
}
|
||||
|
||||
go CheckEvents()
|
||||
}
|
||||
|
||||
func TestIsValidExchange(t *testing.T) {
|
||||
boolean := IsValidExchange("ANX", CONFIG_PATH_TEST)
|
||||
testSetup(t)
|
||||
|
||||
boolean := IsValidExchange("ANX")
|
||||
if !boolean {
|
||||
t.Error("Test Failed. IsValidExchange: Error, incorrect Exchange")
|
||||
}
|
||||
boolean = IsValidExchange("OBTUSE", CONFIG_PATH_TEST)
|
||||
boolean = IsValidExchange("OBTUSE")
|
||||
if boolean {
|
||||
t.Error("Test Failed. IsValidExchange: Error, incorrect return")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidCondition(t *testing.T) {
|
||||
t.Parallel()
|
||||
testSetup(t)
|
||||
|
||||
boolean := IsValidCondition(">")
|
||||
if !boolean {
|
||||
@@ -186,13 +338,13 @@ func TestIsValidCondition(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsValidAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
testSetup(t)
|
||||
|
||||
boolean := IsValidAction("sms")
|
||||
if !boolean {
|
||||
t.Error("Test Failed. IsValidAction: Error, incorrect Action")
|
||||
}
|
||||
boolean = IsValidAction(ACTION_TEST)
|
||||
boolean = IsValidAction(actionTest)
|
||||
if !boolean {
|
||||
t.Error("Test Failed. IsValidAction: Error, incorrect Action")
|
||||
}
|
||||
@@ -203,7 +355,7 @@ func TestIsValidAction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsValidItem(t *testing.T) {
|
||||
t.Parallel()
|
||||
testSetup(t)
|
||||
|
||||
boolean := IsValidItem("price")
|
||||
if !boolean {
|
||||
|
||||
172
events/events.go
172
events/events.go
@@ -8,56 +8,54 @@ import (
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-/gocryptotrader/smsglobal"
|
||||
)
|
||||
|
||||
const (
|
||||
ITEM_PRICE = "PRICE"
|
||||
GREATER_THAN = ">"
|
||||
GREATER_THAN_OR_EQUAL = ">="
|
||||
LESS_THAN = "<"
|
||||
LESS_THAN_OR_EQUAL = "<="
|
||||
IS_EQUAL = "=="
|
||||
ACTION_SMS_NOTIFY = "SMS"
|
||||
ACTION_CONSOLE_PRINT = "CONSOLE_PRINT"
|
||||
ACTION_TEST = "ACTION_TEST"
|
||||
CONFIG_PATH_TEST = config.CONFIG_TEST_FILE
|
||||
itemPrice = "PRICE"
|
||||
greaterThan = ">"
|
||||
greaterThanOrEqual = ">="
|
||||
lessThan = "<"
|
||||
lessThanOrEqual = "<="
|
||||
isEqual = "=="
|
||||
actionSMSNotify = "SMS"
|
||||
actionConsolePrint = "CONSOLE_PRINT"
|
||||
actionTest = "ACTION_TEST"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidItem = errors.New("Invalid item.")
|
||||
ErrInvalidCondition = errors.New("Invalid conditional option.")
|
||||
ErrInvalidAction = errors.New("Invalid action.")
|
||||
ErrExchangeDisabled = errors.New("Desired exchange is disabled.")
|
||||
ErrCurrencyInvalid = errors.New("Invalid currency.")
|
||||
errInvalidItem = errors.New("invalid item")
|
||||
errInvalidCondition = errors.New("invalid conditional option")
|
||||
errInvalidAction = errors.New("invalid action")
|
||||
errExchangeDisabled = errors.New("desired exchange is disabled")
|
||||
)
|
||||
|
||||
// Event struct holds the event variables
|
||||
type Event struct {
|
||||
ID int
|
||||
Exchange string
|
||||
Item string
|
||||
Condition string
|
||||
FirstCurrency string
|
||||
SecondCurrency string
|
||||
Action string
|
||||
Executed bool
|
||||
ID int
|
||||
Exchange string
|
||||
Item string
|
||||
Condition string
|
||||
Pair pair.CurrencyPair
|
||||
Asset string
|
||||
Action string
|
||||
Executed bool
|
||||
}
|
||||
|
||||
// Events variable is a pointer array to the event structures that will be
|
||||
// appended
|
||||
var Events []*Event
|
||||
|
||||
func AddEvent(Exchange, Item, Condition, FirstCurrency, SecondCurrency, Action string) (int, error) {
|
||||
// AddEvent adds an event to the Events chain and returns an index/eventID
|
||||
// and an error
|
||||
func AddEvent(Exchange, Item, Condition string, CurrencyPair pair.CurrencyPair, Asset, Action string) (int, error) {
|
||||
err := IsValidEvent(Exchange, Item, Condition, Action)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !IsValidCurrency(FirstCurrency, SecondCurrency) {
|
||||
return 0, ErrCurrencyInvalid
|
||||
}
|
||||
|
||||
Event := &Event{}
|
||||
|
||||
if len(Events) == 0 {
|
||||
@@ -69,14 +67,15 @@ func AddEvent(Exchange, Item, Condition, FirstCurrency, SecondCurrency, Action s
|
||||
Event.Exchange = Exchange
|
||||
Event.Item = Item
|
||||
Event.Condition = Condition
|
||||
Event.FirstCurrency = FirstCurrency
|
||||
Event.SecondCurrency = SecondCurrency
|
||||
Event.Pair = CurrencyPair
|
||||
Event.Asset = Asset
|
||||
Event.Action = Action
|
||||
Event.Executed = false
|
||||
Events = append(Events, Event)
|
||||
return Event.ID, nil
|
||||
}
|
||||
|
||||
// RemoveEvent deletes and event by its ID
|
||||
func RemoveEvent(EventID int) bool {
|
||||
for i, x := range Events {
|
||||
if x.ID == EventID {
|
||||
@@ -87,6 +86,8 @@ func RemoveEvent(EventID int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetEventCounter displays the emount of total events on the chain and the
|
||||
// events that have been executed.
|
||||
func GetEventCounter() (int, int) {
|
||||
total := len(Events)
|
||||
executed := 0
|
||||
@@ -99,70 +100,78 @@ func GetEventCounter() (int, int) {
|
||||
return total, executed
|
||||
}
|
||||
|
||||
// ExecuteAction will execute the action pending on the chain
|
||||
func (e *Event) ExecuteAction() bool {
|
||||
if common.StringContains(e.Action, ",") {
|
||||
action := common.SplitStrings(e.Action, ",")
|
||||
if action[0] == ACTION_SMS_NOTIFY {
|
||||
message := fmt.Sprintf("Event triggered: %s", e.EventToString())
|
||||
if action[0] == actionSMSNotify {
|
||||
message := fmt.Sprintf("Event triggered: %s", e.String())
|
||||
s := smsglobal.SMSGlobal
|
||||
if action[1] == "ALL" {
|
||||
smsglobal.SMSSendToAll(message, config.Cfg)
|
||||
s.SendMessageToAll(message)
|
||||
} else {
|
||||
smsglobal.SMSNotify(smsglobal.SMSGetNumberByName(action[1], config.Cfg.SMS), message, config.Cfg)
|
||||
contact, _ := s.GetContactByName(action[1])
|
||||
s.SendMessage(contact.Number, message)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("Event triggered: %s", e.EventToString())
|
||||
log.Printf("Event triggered: %s", e.String())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Event) EventToString() string {
|
||||
// EventToString turns the structure event into a string
|
||||
func (e *Event) String() string {
|
||||
condition := common.SplitStrings(e.Condition, ",")
|
||||
return fmt.Sprintf("If the %s%s %s on %s is %s then %s.", e.FirstCurrency, e.SecondCurrency, e.Item, e.Exchange, condition[0]+" "+condition[1], e.Action)
|
||||
return fmt.Sprintf(
|
||||
"If the %s%s [%s] %s on %s is %s then %s.", e.Pair.FirstCurrency.String(),
|
||||
e.Pair.SecondCurrency.String(), e.Asset, e.Item, e.Exchange, condition[0]+" "+condition[1], e.Action,
|
||||
)
|
||||
}
|
||||
|
||||
func (e *Event) CheckCondition() bool { //Add error handling
|
||||
lastPrice := 0.00
|
||||
// CheckCondition will check the event structure to see if there is a condition
|
||||
// met
|
||||
func (e *Event) CheckCondition() bool {
|
||||
condition := common.SplitStrings(e.Condition, ",")
|
||||
targetPrice, _ := strconv.ParseFloat(condition[1], 64)
|
||||
|
||||
ticker, err := ticker.GetTickerByExchange(e.Exchange)
|
||||
t, err := ticker.GetTicker(e.Exchange, e.Pair, e.Asset)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
lastPrice = ticker.Price[pair.CurrencyItem(e.FirstCurrency)][pair.CurrencyItem(e.SecondCurrency)].Last
|
||||
lastPrice := t.Last
|
||||
|
||||
if lastPrice == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch condition[0] {
|
||||
case GREATER_THAN:
|
||||
case greaterThan:
|
||||
{
|
||||
if lastPrice > targetPrice {
|
||||
return e.ExecuteAction()
|
||||
}
|
||||
}
|
||||
case GREATER_THAN_OR_EQUAL:
|
||||
case greaterThanOrEqual:
|
||||
{
|
||||
if lastPrice >= targetPrice {
|
||||
return e.ExecuteAction()
|
||||
}
|
||||
}
|
||||
case LESS_THAN:
|
||||
case lessThan:
|
||||
{
|
||||
if lastPrice < targetPrice {
|
||||
return e.ExecuteAction()
|
||||
}
|
||||
}
|
||||
case LESS_THAN_OR_EQUAL:
|
||||
case lessThanOrEqual:
|
||||
{
|
||||
if lastPrice <= targetPrice {
|
||||
return e.ExecuteAction()
|
||||
}
|
||||
}
|
||||
case IS_EQUAL:
|
||||
case isEqual:
|
||||
{
|
||||
if lastPrice == targetPrice {
|
||||
return e.ExecuteAction()
|
||||
@@ -172,52 +181,54 @@ func (e *Event) CheckCondition() bool { //Add error handling
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValidEvent checks the actions to be taken and returns an error if incorrect
|
||||
func IsValidEvent(Exchange, Item, Condition, Action string) error {
|
||||
Exchange = common.StringToUpper(Exchange)
|
||||
Item = common.StringToUpper(Item)
|
||||
Action = common.StringToUpper(Action)
|
||||
|
||||
configPath := ""
|
||||
if Action == ACTION_TEST {
|
||||
configPath = CONFIG_PATH_TEST
|
||||
}
|
||||
|
||||
if !IsValidExchange(Exchange, configPath) {
|
||||
return ErrExchangeDisabled
|
||||
if !IsValidExchange(Exchange) {
|
||||
return errExchangeDisabled
|
||||
}
|
||||
|
||||
if !IsValidItem(Item) {
|
||||
return ErrInvalidItem
|
||||
return errInvalidItem
|
||||
}
|
||||
|
||||
if !common.StringContains(Condition, ",") {
|
||||
return ErrInvalidCondition
|
||||
return errInvalidCondition
|
||||
}
|
||||
|
||||
condition := common.SplitStrings(Condition, ",")
|
||||
|
||||
if !IsValidCondition(condition[0]) || len(condition[1]) == 0 {
|
||||
return ErrInvalidCondition
|
||||
return errInvalidCondition
|
||||
}
|
||||
|
||||
if common.StringContains(Action, ",") {
|
||||
action := common.SplitStrings(Action, ",")
|
||||
|
||||
if action[0] != ACTION_SMS_NOTIFY {
|
||||
return ErrInvalidAction
|
||||
if action[0] != actionSMSNotify {
|
||||
return errInvalidAction
|
||||
}
|
||||
|
||||
if action[1] != "ALL" && smsglobal.SMSGetNumberByName(action[1], config.Cfg.SMS) == smsglobal.ErrSMSContactNotFound {
|
||||
return ErrInvalidAction
|
||||
if action[1] != "ALL" {
|
||||
s := smsglobal.SMSGlobal
|
||||
_, err := s.GetContactByName(action[1])
|
||||
if err != nil {
|
||||
return errInvalidAction
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if Action != ACTION_CONSOLE_PRINT && Action != ACTION_TEST {
|
||||
return ErrInvalidAction
|
||||
if Action != actionConsolePrint && Action != actionTest {
|
||||
return errInvalidAction
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckEvents is the overarching routine that will iterate through the Events
|
||||
// chain
|
||||
func CheckEvents() {
|
||||
for {
|
||||
total, executed := GetEventCounter()
|
||||
@@ -226,7 +237,10 @@ func CheckEvents() {
|
||||
if !event.Executed {
|
||||
success := event.CheckCondition()
|
||||
if success {
|
||||
log.Printf("Event %d triggered on %s successfully.\n", event.ID, event.Exchange)
|
||||
log.Printf(
|
||||
"Event %d triggered on %s successfully.\n", event.ID,
|
||||
event.Exchange,
|
||||
)
|
||||
event.Executed = true
|
||||
}
|
||||
}
|
||||
@@ -235,27 +249,10 @@ func CheckEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
func IsValidCurrency(currencies ...string) bool {
|
||||
for _, whatIsIt := range currencies {
|
||||
whatIsIt = common.StringToUpper(whatIsIt)
|
||||
if currency.IsDefaultCryptocurrency(whatIsIt) {
|
||||
return true
|
||||
}
|
||||
if currency.IsDefaultCurrency(whatIsIt) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidExchange(Exchange, configPath string) bool {
|
||||
// IsValidExchange validates the exchange
|
||||
func IsValidExchange(Exchange string) bool {
|
||||
Exchange = common.StringToUpper(Exchange)
|
||||
|
||||
cfg := config.GetConfig()
|
||||
if len(cfg.Exchanges) == 0 {
|
||||
cfg.LoadConfig(configPath)
|
||||
}
|
||||
|
||||
for _, x := range cfg.Exchanges {
|
||||
if x.Name == Exchange && x.Enabled {
|
||||
return true
|
||||
@@ -264,27 +261,30 @@ func IsValidExchange(Exchange, configPath string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValidCondition validates passed in condition
|
||||
func IsValidCondition(Condition string) bool {
|
||||
switch Condition {
|
||||
case GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, IS_EQUAL:
|
||||
case greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, isEqual:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValidAction validates passed in action
|
||||
func IsValidAction(Action string) bool {
|
||||
Action = common.StringToUpper(Action)
|
||||
switch Action {
|
||||
case ACTION_SMS_NOTIFY, ACTION_CONSOLE_PRINT, ACTION_TEST:
|
||||
case actionSMSNotify, actionConsolePrint, actionTest:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValidItem validates passed in Item
|
||||
func IsValidItem(Item string) bool {
|
||||
Item = common.StringToUpper(Item)
|
||||
switch Item {
|
||||
case ITEM_PRICE:
|
||||
case itemPrice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -11,47 +11,56 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
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"
|
||||
alphapointDefaultAPIURL = "https://sim3.alphapoint.com:8400"
|
||||
alphapointAPIVersion = "1"
|
||||
alphapointTicker = "GetTicker"
|
||||
alphapointTrades = "GetTrades"
|
||||
alphapointTradesByDate = "GetTradesByDate"
|
||||
alphapointOrderbook = "GetOrderBook"
|
||||
alphapointProductPairs = "GetProductPairs"
|
||||
alphapointProducts = "GetProducts"
|
||||
alphapointCreateAccount = "CreateAccount"
|
||||
alphapointUserInfo = "GetUserInfo"
|
||||
alphapointAccountInfo = "GetAccountInfo"
|
||||
alphapointAccountTrades = "GetAccountTrades"
|
||||
alphapointDepositAddresses = "GetDepositAddresses"
|
||||
alphapointWithdraw = "Withdraw"
|
||||
alphapointCreateOrder = "CreateOrder"
|
||||
alphapointModifyOrder = "ModifyOrder"
|
||||
alphapointCancelOrder = "CancelOrder"
|
||||
alphapointCancelAllOrders = "CancelAllOrders"
|
||||
alphapointOpenOrders = "GetAccountOpenOrders"
|
||||
alphapointOrderFee = "GetOrderFee"
|
||||
|
||||
// Anymore and you get IP banned
|
||||
alphapointMaxRequestsPer10minutes = 500
|
||||
)
|
||||
|
||||
// Alphapoint is the overarching type across the alphapoint package
|
||||
type Alphapoint struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
WebsocketConn *websocket.Conn
|
||||
}
|
||||
|
||||
// SetDefaults sets current default settings
|
||||
func (a *Alphapoint) SetDefaults() {
|
||||
a.APIUrl = ALPHAPOINT_DEFAULT_API_URL
|
||||
a.WebsocketURL = ALPHAPOINT_DEFAULT_WEBSOCKET_URL
|
||||
a.APIUrl = alphapointDefaultAPIURL
|
||||
a.WebsocketURL = alphapointDefaultWebsocketURL
|
||||
a.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetTicker(symbol string) (AlphapointTicker, error) {
|
||||
// GetTicker returns current ticker information from Alphapoint for a selected
|
||||
// currency pair ie "BTCUSD"
|
||||
func (a *Alphapoint) GetTicker(currencyPair string) (Ticker, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["productPair"] = symbol
|
||||
response := AlphapointTicker{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_TICKER, request, &response)
|
||||
request["productPair"] = currencyPair
|
||||
response := Ticker{}
|
||||
|
||||
err := a.SendRequest("POST", alphapointTicker, request, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -61,14 +70,20 @@ func (a *Alphapoint) GetTicker(symbol string) (AlphapointTicker, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetTrades(symbol string, startIndex, count int) (AlphapointTrades, error) {
|
||||
// GetTrades fetches past trades for the given currency pair
|
||||
// currencyPair: ie "BTCUSD"
|
||||
// StartIndex: specifies the index to begin from, -1 being the first trade on
|
||||
// AlphaPoint Exchange. To begin from the most recent trade, set startIndex to
|
||||
// 0 (default: 0)
|
||||
// Count: specifies the number of trades to return (default: 10)
|
||||
func (a *Alphapoint) GetTrades(currencyPair string, startIndex, count int) (Trades, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["ins"] = currencyPair
|
||||
request["startIndex"] = startIndex
|
||||
request["Count"] = count
|
||||
response := AlphapointTrades{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_TRADES, request, &response)
|
||||
response := Trades{}
|
||||
|
||||
err := a.SendRequest("POST", alphapointTrades, request, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -78,14 +93,18 @@ func (a *Alphapoint) GetTrades(symbol string, startIndex, count int) (Alphapoint
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetTradesByDate(symbol string, startDate, endDate int64) (AlphapointTradesByDate, error) {
|
||||
// GetTradesByDate gets trades by date
|
||||
// CurrencyPair - instrument code (ex: “BTCUSD”)
|
||||
// StartDate - specifies the starting time in epoch time, type is long
|
||||
// EndDate - specifies the end time in epoch time, type is long
|
||||
func (a *Alphapoint) GetTradesByDate(currencyPair string, startDate, endDate int64) (Trades, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["ins"] = currencyPair
|
||||
request["startDate"] = startDate
|
||||
request["endDate"] = endDate
|
||||
response := AlphapointTradesByDate{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_TRADESBYDATE, request, &response)
|
||||
response := Trades{}
|
||||
|
||||
err := a.SendRequest("POST", alphapointTradesByDate, request, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -95,12 +114,14 @@ func (a *Alphapoint) GetTradesByDate(symbol string, startDate, endDate int64) (A
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetOrderbook(symbol string) (AlphapointOrderbook, error) {
|
||||
// GetOrderbook fetches the current orderbook for a given currency pair
|
||||
// CurrencyPair - trade pair (ex: “BTCUSD”)
|
||||
func (a *Alphapoint) GetOrderbook(currencyPair string) (Orderbook, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["productPair"] = symbol
|
||||
response := AlphapointOrderbook{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_ORDERBOOK, request, &response)
|
||||
request["productPair"] = currencyPair
|
||||
response := Orderbook{}
|
||||
|
||||
err := a.SendRequest("POST", alphapointOrderbook, request, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -110,10 +131,11 @@ func (a *Alphapoint) GetOrderbook(symbol string) (AlphapointOrderbook, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetProductPairs() (AlphapointProductPairs, error) {
|
||||
response := AlphapointProductPairs{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_PRODUCT_PAIRS, nil, &response)
|
||||
// GetProductPairs gets the currency pairs currently traded on alphapoint
|
||||
func (a *Alphapoint) GetProductPairs() (ProductPairs, error) {
|
||||
response := ProductPairs{}
|
||||
|
||||
err := a.SendRequest("POST", alphapointProductPairs, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -123,10 +145,11 @@ func (a *Alphapoint) GetProductPairs() (AlphapointProductPairs, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetProducts() (AlphapointProducts, error) {
|
||||
response := AlphapointProducts{}
|
||||
err := a.SendRequest("POST", ALPHAPOINT_PRODUCTS, nil, &response)
|
||||
// GetProducts gets the currency products currently supported on alphapoint
|
||||
func (a *Alphapoint) GetProducts() (Products, error) {
|
||||
response := Products{}
|
||||
|
||||
err := a.SendRequest("POST", alphapointProducts, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -136,9 +159,17 @@ func (a *Alphapoint) GetProducts() (AlphapointProducts, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// CreateAccount creates a new account on alphapoint
|
||||
// FirstName - First name
|
||||
// LastName - Last name
|
||||
// Email - Email address
|
||||
// Phone - Phone number (ex: “+12223334444”)
|
||||
// Password - Minimum 8 characters
|
||||
func (a *Alphapoint) CreateAccount(firstName, lastName, email, phone, password string) error {
|
||||
if len(password) < 8 {
|
||||
return errors.New("Alphapoint Error - Create account - Password must be 8 characters or more.")
|
||||
return errors.New(
|
||||
"alphapoint Error - Create account - Password must be 8 characters or more",
|
||||
)
|
||||
}
|
||||
|
||||
request := make(map[string]interface{})
|
||||
@@ -147,38 +178,99 @@ func (a *Alphapoint) CreateAccount(firstName, lastName, email, phone, password s
|
||||
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)
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", alphapointCreateAccount, 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)
|
||||
// GetUserInfo returns current account user information
|
||||
func (a *Alphapoint) GetUserInfo() (UserInfo, error) {
|
||||
response := UserInfo{}
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", alphapointUserInfo, map[string]interface{}{}, &response)
|
||||
if err != nil {
|
||||
return AlphapointUserInfo{}, err
|
||||
return UserInfo{}, err
|
||||
}
|
||||
if !response.IsAccepted {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetAccountInfo() (AlphapointAccountInfo, error) {
|
||||
response := AlphapointAccountInfo{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_ACCOUNT_INFO, map[string]interface{}{}, &response)
|
||||
// SetUserInfo changes user name and/or 2FA settings
|
||||
// userInfoKVP - An array of key value pairs
|
||||
// FirstName - First name
|
||||
// LastName - Last name
|
||||
// UseAuthy2FA - “true” or “false” toggle Authy app
|
||||
// Cell2FACountryCode - Cell country code (ex: 1), required for Authentication
|
||||
// Cell2FAValue - Cell phone number, required for Authentication
|
||||
// Use2FAForWithdraw - “true” or “false” set to true for using 2FA for
|
||||
// withdrawals
|
||||
func (a *Alphapoint) SetUserInfo(firstName, lastName, cell2FACountryCode, cell2FAValue string, useAuthy2FA, use2FAForWithdraw bool) (UserInfoSet, error) {
|
||||
response := UserInfoSet{}
|
||||
|
||||
var userInfoKVPs = []UserInfoKVP{
|
||||
UserInfoKVP{
|
||||
Key: "FirstName",
|
||||
Value: firstName,
|
||||
},
|
||||
UserInfoKVP{
|
||||
Key: "LastName",
|
||||
Value: lastName,
|
||||
},
|
||||
UserInfoKVP{
|
||||
Key: "Cell2FACountryCode",
|
||||
Value: cell2FACountryCode,
|
||||
},
|
||||
UserInfoKVP{
|
||||
Key: "Cell2FAValue",
|
||||
Value: cell2FAValue,
|
||||
},
|
||||
UserInfoKVP{
|
||||
Key: "UseAuthy2FA",
|
||||
Value: strconv.FormatBool(useAuthy2FA),
|
||||
},
|
||||
UserInfoKVP{
|
||||
Key: "Use2FAForWithdraw",
|
||||
Value: strconv.FormatBool(use2FAForWithdraw),
|
||||
},
|
||||
}
|
||||
|
||||
request := make(map[string]interface{})
|
||||
request["userInfoKVP"] = userInfoKVPs
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointUserInfo,
|
||||
request,
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if response.IsAccepted != "true" {
|
||||
return response, errors.New(response.RejectReason)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetAccountInfo returns account info
|
||||
func (a *Alphapoint) GetAccountInfo() (AccountInfo, error) {
|
||||
response := AccountInfo{}
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointAccountInfo,
|
||||
map[string]interface{}{},
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -188,14 +280,23 @@ func (a *Alphapoint) GetAccountInfo() (AlphapointAccountInfo, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetAccountTrades(symbol string, startIndex, count int) (AlphapointTrades, error) {
|
||||
// GetAccountTrades returns the trades executed on the account.
|
||||
// CurrencyPair - Instrument code (ex: “BTCUSD”)
|
||||
// StartIndex - Starting index, if less than 0 then start from the beginning
|
||||
// Count - Returns last trade, (Default: 30)
|
||||
func (a *Alphapoint) GetAccountTrades(currencyPair string, startIndex, count int) (Trades, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
request["ins"] = currencyPair
|
||||
request["startIndex"] = startIndex
|
||||
request["count"] = count
|
||||
response := Trades{}
|
||||
|
||||
response := AlphapointTrades{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_ACCOUNT_TRADES, request, &response)
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointAccountTrades,
|
||||
request,
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -205,15 +306,13 @@ func (a *Alphapoint) GetAccountTrades(symbol string, startIndex, count int) (Alp
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetDepositAddresses() ([]AlphapointDepositAddresses, error) {
|
||||
type Response struct {
|
||||
Addresses []AlphapointDepositAddresses
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
// GetDepositAddresses generates a deposit address
|
||||
func (a *Alphapoint) GetDepositAddresses() ([]DepositAddresses, error) {
|
||||
response := Response{}
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", ALPHAPOINT_DEPOSIT_ADDRESSES, map[string]interface{}{}, &response)
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest("POST", alphapointDepositAddresses,
|
||||
map[string]interface{}{}, &response,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -223,30 +322,40 @@ func (a *Alphapoint) GetDepositAddresses() ([]AlphapointDepositAddresses, error)
|
||||
return response.Addresses, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) WithdrawCoins(symbol, product string, amount float64, address string) error {
|
||||
// WithdrawCoins withdraws a coin to a specific address
|
||||
// symbol - Instrument name (ex: “BTCUSD”)
|
||||
// product - Currency name (ex: “BTC”)
|
||||
// amount - Amount (ex: “.011”)
|
||||
// address - Withdraw address
|
||||
func (a *Alphapoint) WithdrawCoins(symbol, product, address string, amount float64) 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)
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointWithdraw,
|
||||
request,
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return errors.New(response.RejectReason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrder creates a market or limit order
|
||||
// symbol - Instrument code (ex: “BTCUSD”)
|
||||
// side - “buy” or “sell”
|
||||
// orderType - “1” for market orders, “0” for limit orders
|
||||
// quantity - Quantity
|
||||
// price - Price in USD
|
||||
func (a *Alphapoint) CreateOrder(symbol, side string, orderType int, quantity, price float64) (int64, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["ins"] = symbol
|
||||
@@ -254,186 +363,209 @@ func (a *Alphapoint) CreateOrder(symbol, side string, orderType int, quantity, p
|
||||
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)
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointCreateOrder,
|
||||
request,
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.ServerOrderID, nil
|
||||
}
|
||||
|
||||
// ModifyOrder modifies and existing Order
|
||||
// OrderId - tracked order id number
|
||||
// symbol - Instrument code (ex: “BTCUSD”)
|
||||
// modifyAction - “0” or “1”
|
||||
// “0” means "Move to top", which will modify the order price to the top of the
|
||||
// book. A buy order will be modified to the highest bid and a sell order will
|
||||
// be modified to the lowest ask price. “1” means "Execute now", which will
|
||||
// convert a limit order into a market order.
|
||||
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)
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointModifyOrder,
|
||||
request,
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.ModifyOrderID, nil
|
||||
}
|
||||
|
||||
// CancelOrder cancels an order that has not been executed.
|
||||
// symbol - Instrument code (ex: “BTCUSD”)
|
||||
// OrderId - Order id (ex: 1000)
|
||||
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)
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointCancelOrder,
|
||||
request,
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.CancelOrderID, nil
|
||||
}
|
||||
|
||||
// CancelAllOrders cancels all open orders by symbol
|
||||
// symbol - Instrument code (ex: “BTCUSD”)
|
||||
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)
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointCancelAllOrders,
|
||||
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)
|
||||
// GetOrders returns all current open orders
|
||||
func (a *Alphapoint) GetOrders() ([]OpenOrders, error) {
|
||||
response := OrderInfo{}
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointOpenOrders,
|
||||
map[string]interface{}{},
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return nil, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.OpenOrders, nil
|
||||
}
|
||||
|
||||
// GetOrderFee returns a fee associated with an order
|
||||
// symbol - Instrument code (ex: “BTCUSD”)
|
||||
// side - “buy” or “sell”
|
||||
// quantity - Quantity
|
||||
// price - Price in USD
|
||||
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)
|
||||
|
||||
err := a.SendAuthenticatedHTTPRequest(
|
||||
"POST",
|
||||
alphapointOrderFee,
|
||||
request,
|
||||
&response,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !response.IsAccepted {
|
||||
return 0, errors.New(response.RejectReason)
|
||||
}
|
||||
return response.Fee, nil
|
||||
}
|
||||
|
||||
// SendRequest sends an unauthenticated request
|
||||
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)
|
||||
path = fmt.Sprintf("%s/ajax/v%s/%s", a.APIUrl, alphapointAPIVersion, path)
|
||||
|
||||
PayloadJSON, err := common.JSONEncode(data)
|
||||
if err != nil {
|
||||
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
return errors.New("SendHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
resp, err := common.SendHTTPRequest(method, path, headers, bytes.NewBuffer(PayloadJson))
|
||||
|
||||
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 errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an authenticated request
|
||||
func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[string]interface{}, result interface{}) error {
|
||||
if !a.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, a.Name)
|
||||
}
|
||||
|
||||
if a.Nonce.Get() == 0 {
|
||||
a.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
a.Nonce.Inc()
|
||||
}
|
||||
|
||||
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.ClientID+a.APIKey), []byte(a.APISecret))
|
||||
data["apiNonce"] = a.Nonce.Get()
|
||||
hmac := common.GetHMAC(common.HashSHA256, []byte(a.Nonce.String()+a.ClientID+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)
|
||||
path = fmt.Sprintf("%s/ajax/v%s/%s", a.APIUrl, alphapointAPIVersion, 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))
|
||||
|
||||
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 errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package alphapoint
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
const (
|
||||
onlineTest = false
|
||||
|
||||
testAPIKey = ""
|
||||
testAPISecret = ""
|
||||
)
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
@@ -18,227 +26,121 @@ func TestSetDefaults(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testSetAPIKey(a *Alphapoint) {
|
||||
a.APIKey = testAPIKey
|
||||
a.APISecret = testAPISecret
|
||||
a.AuthenticatedAPISupport = true
|
||||
}
|
||||
|
||||
func testIsAPIKeysSet(a *Alphapoint) bool {
|
||||
if testAPIKey != "" && testAPISecret != "" && a.AuthenticatedAPISupport {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func TestGetTicker(t *testing.T) {
|
||||
GetTicker := Alphapoint{}
|
||||
GetTicker.SetDefaults()
|
||||
alpha := Alphapoint{}
|
||||
alpha.SetDefaults()
|
||||
|
||||
response, err := GetTicker.GetTicker("BTCUSD")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - Alphapoint GetTicker init error: ", err)
|
||||
}
|
||||
if reflect.ValueOf(response).NumField() != 13 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker struct change/or updated")
|
||||
}
|
||||
if reflect.TypeOf(response.Ask).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Ask value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.Bid).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Bid value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.BuyOrderCount).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.BuyOrderCount value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.High).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.High value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.IsAccepted).String() != "bool" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.IsAccepted value is not a bool")
|
||||
}
|
||||
if reflect.TypeOf(response.Last).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Last value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.Low).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Low value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.NumOfCreateOrders).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.NumOfCreateOrders value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.RejectReason).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.RejectReason value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(response.SellOrderCount).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.SellOrderCount value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.Total24HrNumTrades).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Total24HrNumTrades value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.Total24HrQtyTraded).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Total24HrQtyTraded value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(response.Volume).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Volume value is not a float64")
|
||||
var ticker Ticker
|
||||
var err error
|
||||
|
||||
if onlineTest {
|
||||
ticker, err = alpha.GetTicker("BTCUSD")
|
||||
if err != nil {
|
||||
t.Fatal("Test Failed - Alphapoint GetTicker init error: ", err)
|
||||
}
|
||||
|
||||
_, err = alpha.GetTicker("wigwham")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Alphapoint GetTicker error")
|
||||
}
|
||||
} else {
|
||||
mockResp := []byte(
|
||||
string(`{"high":253.101,"last":249.76,"bid":248.8901,"volume":5.813354,"low":231.21,"ask":248.9012,"Total24HrQtyTraded":52.654968,"Total24HrProduct2Traded":569.05762,"Total24HrNumTrades":4,"sellOrderCount":7,"buyOrderCount":11,"numOfCreateOrders":0,"isAccepted":true}`),
|
||||
)
|
||||
|
||||
err = common.JSONDecode(mockResp, &ticker)
|
||||
if err != nil {
|
||||
t.Fatal("Test Failed - Alphapoint GetTicker unmarshalling error: ", err)
|
||||
}
|
||||
|
||||
if ticker.Last != 249.76 {
|
||||
t.Error("Test failed - Alphapoint GetTicker expected last = 249.76")
|
||||
}
|
||||
}
|
||||
|
||||
if response.Ask < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Ask value is negative")
|
||||
}
|
||||
if response.Bid < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Bid value is negative")
|
||||
}
|
||||
if response.BuyOrderCount < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.High value is negative")
|
||||
}
|
||||
if response.High < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Last value is negative")
|
||||
}
|
||||
if response.Last < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Low value is negative")
|
||||
}
|
||||
if response.Low < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.Mid value is negative")
|
||||
}
|
||||
if response.NumOfCreateOrders < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.ask value is negative")
|
||||
}
|
||||
if response.SellOrderCount < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.ask value is negative")
|
||||
}
|
||||
if response.Total24HrNumTrades < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.ask value is negative")
|
||||
}
|
||||
if response.Total24HrQtyTraded < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.ask value is negative")
|
||||
}
|
||||
if response.Volume < 0 {
|
||||
t.Error("Test Failed - Alphapoint GetTicker.ask value is negative")
|
||||
if ticker.Last < 0 {
|
||||
t.Error("Test failed - Alphapoint GetTicker last < 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTrades(t *testing.T) {
|
||||
GetTrades := Alphapoint{}
|
||||
GetTrades.SetDefaults()
|
||||
alpha := Alphapoint{}
|
||||
alpha.SetDefaults()
|
||||
|
||||
trades, err := GetTrades.GetTrades("BTCUSD", 0, 10)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
if reflect.ValueOf(trades).NumField() != 7 {
|
||||
t.Error("Test Failed - Alphapoint AlphapointTrades struct updated/changed")
|
||||
}
|
||||
if len(trades.Trades) == 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades: Incorrect length")
|
||||
}
|
||||
if reflect.ValueOf(trades.Trades[0]).NumField() != 8 {
|
||||
t.Error("Test Failed - Alphapoint AlphapointTrades.Trades struct updated/changed")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].BookServerOrderID).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].IncomingOrderSide).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.IncomingOrderSide value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].IncomingServerOrderID).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.IncomingServerOrderID value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].Price).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.Price value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].Quantity).String() != "float64" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.Quantity value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].TID).String() != "int64" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.TID value is not a int64")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].UTCTicks).String() != "int64" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.UTCTicks value is not a int64")
|
||||
}
|
||||
if reflect.TypeOf(trades.Trades[0].Unixtime).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.Unixtime value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(trades.Count).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint trades.Count value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(trades.DateTimeUTC).String() != "int64" {
|
||||
t.Error("Test Failed - Alphapoint trades.DateTimeUTC value is not a int64")
|
||||
}
|
||||
if reflect.TypeOf(trades.Instrument).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint trades.Instrument value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(trades.IsAccepted).String() != "bool" {
|
||||
t.Error("Test Failed - Alphapoint trades.IsAccepted value is not a bool")
|
||||
}
|
||||
if reflect.TypeOf(trades.RejectReason).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint trades.string value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(trades.StartIndex).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint trades.Count value is not a int")
|
||||
var trades Trades
|
||||
var err error
|
||||
|
||||
if onlineTest {
|
||||
trades, err = alpha.GetTrades("BTCUSD", 0, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
|
||||
_, err = alpha.GetTrades("wigwham", 0, 10)
|
||||
if err == nil {
|
||||
t.Fatal("Test Failed - GetTrades error")
|
||||
}
|
||||
} else {
|
||||
mockResp := []byte(
|
||||
string(`{"isAccepted":true,"dateTimeUtc":635507981548085938,"ins":"BTCUSD","startIndex":0,"count":10,"trades":[{"tid":0,"px":231.8379,"qty":4.913,"unixtime":1399951989,"utcticks":635355487898355234,"incomingOrderSide":0,"incomingServerOrderId":2598,"bookServerOrderId":2588},{"tid":1,"px":7895.1487,"qty":0.25,"unixtime":1403143708,"utcticks":635387405087297421,"incomingOrderSide":0,"incomingServerOrderId":284241,"bookServerOrderId":284235},{"tid":2,"px":7935.058,"qty":0.25,"unixtime":1403195348,"utcticks":635387921488684140,"incomingOrderSide":0,"incomingServerOrderId":575845,"bookServerOrderId":574078},{"tid":3,"px":7935.0448,"qty":0.25,"unixtime":1403195378,"utcticks":635387921780090390,"incomingOrderSide":0,"incomingServerOrderId":576028,"bookServerOrderId":575946},{"tid":4,"px":7933.9566,"qty":0.1168,"unixtime":1403195510,"utcticks":635387923108371640,"incomingOrderSide":0,"incomingServerOrderId":576974,"bookServerOrderId":576947},{"tid":5,"px":7961.0856,"qty":0.25,"unixtime":1403202307,"utcticks":635387991073850156,"incomingOrderSide":0,"incomingServerOrderId":600547,"bookServerOrderId":600338},{"tid":6,"px":7961.1388,"qty":0.011,"unixtime":1403202307,"utcticks":635387991073850156,"incomingOrderSide":0,"incomingServerOrderId":600547,"bookServerOrderId":600418},{"tid":7,"px":7961.2451,"qty":0.02,"unixtime":1403202307,"utcticks":635387991073850156,"incomingOrderSide":0,"incomingServerOrderId":600547,"bookServerOrderId":600428},{"tid":8,"px":7947.1437,"qty":0.09,"unixtime":1403202749,"utcticks":635387995498225156,"incomingOrderSide":0,"incomingServerOrderId":602183,"bookServerOrderId":601745},{"tid":9,"px":7818.5073,"qty":0.25,"unixtime":1403219720,"utcticks":635388165206506406,"incomingOrderSide":0,"incomingServerOrderId":661909,"bookServerOrderId":661620}]}`),
|
||||
)
|
||||
|
||||
err = common.JSONDecode(mockResp, &trades)
|
||||
if err != nil {
|
||||
t.Fatal("Test Failed - GetTrades unmarshalling error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
if trades.Count < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Count value is negative")
|
||||
if !trades.IsAccepted {
|
||||
t.Error("Test Failed - GetTrades IsAccepted failed")
|
||||
}
|
||||
if trades.DateTimeUTC <= 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.DateTimeUTC value is negative or 0")
|
||||
|
||||
if trades.Count <= 0 {
|
||||
t.Error("Test failed - GetTrades trades count is <= 0")
|
||||
}
|
||||
|
||||
if trades.Instrument != "BTCUSD" {
|
||||
t.Error("Test Failed - Alphapoint trades.Instrument value is incorrect")
|
||||
}
|
||||
if trades.IsAccepted != true {
|
||||
t.Error("Test Failed - Alphapoint trades.IsAccepted value is true")
|
||||
}
|
||||
if len(trades.RejectReason) > 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.IsAccepted value has been returned")
|
||||
}
|
||||
if trades.StartIndex != 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.StartIndex value is incorrect")
|
||||
}
|
||||
if trades.Trades[0].BookServerOrderID < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
}
|
||||
if trades.Trades[0].IncomingOrderSide < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
}
|
||||
if trades.Trades[0].IncomingServerOrderID < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
}
|
||||
if trades.Trades[0].Price < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
}
|
||||
if trades.Trades[0].Quantity < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
}
|
||||
if trades.Trades[0].TID != 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
}
|
||||
if trades.Trades[0].UTCTicks < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
}
|
||||
if trades.Trades[0].Unixtime < 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades.BookServerOrderID value is negative")
|
||||
t.Error("Test failed - GetTrades instrument is != BTCUSD")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTradesByDate(t *testing.T) {
|
||||
GetTradesByDate := Alphapoint{}
|
||||
GetTradesByDate.SetDefaults()
|
||||
alpha := Alphapoint{}
|
||||
alpha.SetDefaults()
|
||||
|
||||
trades, err := GetTradesByDate.GetTradesByDate("BTCUSD", 1414799400, 1414800000)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
if reflect.ValueOf(trades).NumField() != 7 {
|
||||
t.Error("Test Failed - Alphapoint AlphapointTrades struct updated/changed")
|
||||
}
|
||||
if len(trades.Trades) != 0 {
|
||||
t.Error("Test Failed - Alphapoint trades.Trades: Incorrect length")
|
||||
}
|
||||
if reflect.TypeOf(trades.DateTimeUTC).String() != "int64" {
|
||||
t.Error("Test Failed - Alphapoint trades.Count value is not a int64")
|
||||
}
|
||||
if reflect.TypeOf(trades.EndDate).String() != "int64" {
|
||||
t.Error("Test Failed - Alphapoint trades.DateTimeUTC value is not a int64")
|
||||
}
|
||||
if reflect.TypeOf(trades.Instrument).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint trades.Instrument value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(trades.IsAccepted).String() != "bool" {
|
||||
t.Error("Test Failed - Alphapoint trades.IsAccepted value is not a bool")
|
||||
}
|
||||
if reflect.TypeOf(trades.RejectReason).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint trades.string value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(trades.StartDate).String() != "int64" {
|
||||
t.Error("Test Failed - Alphapoint trades.StartDate value is not a int64")
|
||||
var trades Trades
|
||||
var err error
|
||||
|
||||
if onlineTest {
|
||||
trades, err = alpha.GetTradesByDate("BTCUSD", 1414799400, 1414800000)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
_, err = alpha.GetTradesByDate("wigwham", 1414799400, 1414800000)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetTradesByDate error")
|
||||
}
|
||||
} else {
|
||||
mockResp := []byte(
|
||||
string(`{"isAccepted":true,"dateTimeUtc":635504540880633671,"ins":"BTCUSD","startDate":1414799400,"endDate":1414800000,"trades":[{"tid":11505,"px":334.669,"qty":0.1211,"unixtime":1414799403,"utcticks":635503962032459843,"incomingOrderSide":1,"incomingServerOrderId":5185651,"bookServerOrderId":5162440},{"tid":11506,"px":334.669,"qty":0.1211,"unixtime":1414799405,"utcticks":635503962058446171,"incomingOrderSide":1,"incomingServerOrderId":5186245,"bookServerOrderId":5162440},{"tid":11507,"px":336.498,"qty":0.011,"unixtime":1414799407,"utcticks":635503962072967656,"incomingOrderSide":0,"incomingServerOrderId":5186530,"bookServerOrderId":5178944},{"tid":11508,"px":335.948,"qty":0.011,"unixtime":1414799410,"utcticks":635503962108055546,"incomingOrderSide":0,"incomingServerOrderId":5187260,"bookServerOrderId":5186531}]}`),
|
||||
)
|
||||
|
||||
err = common.JSONDecode(mockResp, &trades)
|
||||
if err != nil {
|
||||
t.Fatal("Test Failed - GetTradesByDate unmarshalling error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
if trades.DateTimeUTC < 0 {
|
||||
@@ -262,152 +164,329 @@ func TestGetTradesByDate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
GetOrderbook := Alphapoint{}
|
||||
GetOrderbook.SetDefaults()
|
||||
alpha := Alphapoint{}
|
||||
alpha.SetDefaults()
|
||||
|
||||
orderBook, err := GetOrderbook.GetOrderbook("BTCUSD")
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
var orderBook Orderbook
|
||||
var err error
|
||||
|
||||
if onlineTest {
|
||||
orderBook, err = alpha.GetOrderbook("BTCUSD")
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
|
||||
_, err = alpha.GetOrderbook("wigwham")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetOrderbook() error")
|
||||
}
|
||||
} else {
|
||||
mockResp := []byte(
|
||||
string(`{"bids":[{"qty":725,"px":66},{"qty":1289,"px":65},{"qty":1266,"px":64}],"asks":[{"qty":1,"px":67},{"qty":1,"px":69},{"qty":2,"px":70}],"isAccepted":true}`),
|
||||
)
|
||||
|
||||
err = common.JSONDecode(mockResp, &orderBook)
|
||||
if err != nil {
|
||||
t.Fatal("Test Failed - TestGetOrderbook unmarshalling error: ", err)
|
||||
}
|
||||
|
||||
if orderBook.Bids[0].Quantity != 725 {
|
||||
t.Error("Test Failed - TestGetOrderbook Bids[0].Quantity != 725")
|
||||
}
|
||||
}
|
||||
if reflect.ValueOf(orderBook).NumField() != 4 {
|
||||
t.Error("Test Failed - Alphapoint AlphapointOrderbook struct updated/changed")
|
||||
|
||||
if !orderBook.IsAccepted {
|
||||
t.Error("Test Failed - Alphapoint orderBook.IsAccepted value is negative")
|
||||
}
|
||||
if reflect.TypeOf(orderBook.IsAccepted).String() != "bool" {
|
||||
t.Error("Test Failed - Alphapoint orderBook.IsAccepted value is not a bool")
|
||||
|
||||
if len(orderBook.Asks) == 0 {
|
||||
t.Error("Test Failed - Alphapoint orderBook.Asks has len 0")
|
||||
}
|
||||
if reflect.TypeOf(orderBook.RejectReason).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint orderBook.RejectReason value is not a string")
|
||||
}
|
||||
if len(orderBook.Asks) < 1 {
|
||||
t.Error("Test Failed - Alphapoint orderBook.Asks does not contain anything.")
|
||||
}
|
||||
if len(orderBook.Bids) < 1 {
|
||||
t.Error("Test Failed - Alphapoint orderBook.Asks does not contain anything.")
|
||||
|
||||
if len(orderBook.Bids) == 0 {
|
||||
t.Error("Test Failed - Alphapoint orderBook.Bids has len 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProductPairs(t *testing.T) {
|
||||
GetProductPairs := Alphapoint{}
|
||||
GetProductPairs.SetDefaults()
|
||||
alpha := Alphapoint{}
|
||||
alpha.SetDefaults()
|
||||
|
||||
productPairs, err := GetProductPairs.GetProductPairs()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
if reflect.ValueOf(productPairs).NumField() != 3 {
|
||||
t.Error("Test Failed - Alphapoint GetProductPairs struct updated/changed")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.IsAccepted).String() != "bool" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.IsAccepted value is not a bool")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.RejectReason).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.RejectReason value is not a string")
|
||||
}
|
||||
var products ProductPairs
|
||||
var err error
|
||||
|
||||
if len(productPairs.ProductPairs) >= 1 {
|
||||
if reflect.ValueOf(productPairs.ProductPairs[0]).NumField() != 6 {
|
||||
t.Error("Test Failed - Alphapoint GetProductPairs.ProductPairs[] struct updated/changed")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.ProductPairs[0].Name).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Name value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.ProductPairs[0].Product1Decimalplaces).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Product1Decimalplaces value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.ProductPairs[0].Product1Label).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Product1Label value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.ProductPairs[0].Product2Decimalplaces).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Product2Decimalplaces value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.ProductPairs[0].Product2Label).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Product2Label value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(productPairs.ProductPairs[0].Productpaircode).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Productpaircode value is not a int")
|
||||
}
|
||||
|
||||
if productPairs.ProductPairs[0].Product1Decimalplaces < 0 {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Product1Decimalplaces value is negative")
|
||||
}
|
||||
if productPairs.ProductPairs[0].Product2Decimalplaces < 0 {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Product2Decimalplaces value is negative")
|
||||
}
|
||||
if productPairs.ProductPairs[0].Productpaircode < 0 {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs.Productpaircode value is negative")
|
||||
if onlineTest {
|
||||
products, err = alpha.GetProductPairs()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
} else {
|
||||
t.Error("Test Failed - Alphapoint productPairs.ProductPairs no product pairs.")
|
||||
mockResp := []byte(
|
||||
string(`{"productPairs":[{"name":"LTCUSD","productPairCode":100,"product1Label":"LTC","product1DecimalPlaces":8,"product2Label":"USD","product2DecimalPlaces":6}, {"name":"BTCUSD","productPairCode":99,"product1Label":"BTC","product1DecimalPlaces":8,"product2Label":"USD","product2DecimalPlaces":6}],"isAccepted":true}`),
|
||||
)
|
||||
|
||||
err = common.JSONDecode(mockResp, &products)
|
||||
if err != nil {
|
||||
t.Fatal("Test Failed - TestGetProductPairs unmarshalling error: ", err)
|
||||
}
|
||||
|
||||
if products.ProductPairs[0].Name != "LTCUSD" {
|
||||
t.Error("Test Failed - Alphapoint ProductPairs 0 != LTCUSD")
|
||||
}
|
||||
|
||||
if products.ProductPairs[1].Product1Label != "BTC" {
|
||||
t.Error("Test Failed - Alphapoint ProductPairs 1 != BTC")
|
||||
}
|
||||
}
|
||||
|
||||
if !products.IsAccepted {
|
||||
t.Error("Test Failed - Alphapoint ProductPairs.IsAccepted value is negative")
|
||||
}
|
||||
|
||||
if len(products.ProductPairs) == 0 {
|
||||
t.Error("Test Failed - Alphapoint ProductPairs len is 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProducts(t *testing.T) {
|
||||
GetProducts := Alphapoint{}
|
||||
GetProducts.SetDefaults()
|
||||
alpha := Alphapoint{}
|
||||
alpha.SetDefaults()
|
||||
|
||||
products, err := GetProducts.GetProducts()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
if reflect.ValueOf(products).NumField() != 3 {
|
||||
t.Error("Test Failed - Alphapoint GetProductPairs struct updated/changed")
|
||||
}
|
||||
if reflect.TypeOf(products.IsAccepted).String() != "bool" {
|
||||
t.Error("Test Failed - Alphapoint products.IsAccepted value is not a bool")
|
||||
}
|
||||
if reflect.TypeOf(products.RejectReason).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint products.RejectReason value is not a string")
|
||||
}
|
||||
var products Products
|
||||
var err error
|
||||
|
||||
if len(products.Products) >= 1 {
|
||||
if reflect.ValueOf(products.Products[0]).NumField() != 5 {
|
||||
t.Error("Test Failed - Alphapoint Getproducts.Products[] struct updated/changed")
|
||||
}
|
||||
if reflect.TypeOf(products.Products[0].DecimalPlaces).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint products.Products.DecimalPlaces value is not a int")
|
||||
}
|
||||
if reflect.TypeOf(products.Products[0].FullName).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint products.Products.FullName value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(products.Products[0].IsDigital).String() != "bool" {
|
||||
t.Error("Test Failed - Alphapoint products.Products.IsDigital value is not a bool")
|
||||
}
|
||||
if reflect.TypeOf(products.Products[0].Name).String() != "string" {
|
||||
t.Error("Test Failed - Alphapoint products.Products.Name value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(products.Products[0].ProductCode).String() != "int" {
|
||||
t.Error("Test Failed - Alphapoint products.Products.ProductCode value is not a int")
|
||||
}
|
||||
|
||||
if products.Products[0].DecimalPlaces < 0 {
|
||||
t.Error("Test Failed - Alphapoint products.Products.DecimalPlaces value is negative")
|
||||
}
|
||||
if products.Products[0].ProductCode < 0 {
|
||||
t.Log(products.Products[0].ProductCode)
|
||||
t.Error("Test Failed - Alphapoint products.Products.ProductCode value is negative")
|
||||
if onlineTest {
|
||||
products, err = alpha.GetProducts()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
} else {
|
||||
t.Error("Test Failed - Alphapoint products.Products no product pairs.")
|
||||
mockResp := []byte(
|
||||
string(`{"products": [{"name": "USD","isDigital": false,"productCode": 0,"decimalPlaces": 4,"fullName": "US Dollar"},{"name": "BTC","isDigital": true,"productCode": 1,"decimalPlaces": 6,"fullName": "Bitcoin"}],"isAccepted": true}`),
|
||||
)
|
||||
|
||||
err = common.JSONDecode(mockResp, &products)
|
||||
if err != nil {
|
||||
t.Fatal("Test Failed - TestGetProducts unmarshalling error: ", err)
|
||||
}
|
||||
|
||||
if products.Products[0].Name != "USD" {
|
||||
t.Error("Test Failed - Alphapoint Products 0 != USD")
|
||||
}
|
||||
|
||||
if products.Products[1].ProductCode != 1 {
|
||||
t.Error("Test Failed - Alphapoint Products 1 product code != 1")
|
||||
}
|
||||
}
|
||||
|
||||
if !products.IsAccepted {
|
||||
t.Error("Test Failed - Alphapoint Products.IsAccepted value is negative")
|
||||
}
|
||||
|
||||
if len(products.Products) == 0 {
|
||||
t.Error("Test Failed - Alphapoint Products len is 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateAccount(t *testing.T) {
|
||||
CreateAccount := Alphapoint{}
|
||||
CreateAccount.SetDefaults()
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
err := CreateAccount.CreateAccount("test", "account", "something@something.com", "0292383745", "lolcat123")
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
err := a.CreateAccount("test", "account", "something@something.com", "0292383745", "lolcat123")
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
}
|
||||
err = a.CreateAccount("test", "account", "something@something.com", "0292383745", "bla")
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed - CreateAccount() error")
|
||||
}
|
||||
err = a.CreateAccount("", "", "", "", "lolcat123")
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed - CreateAccount() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserInfo(t *testing.T) {
|
||||
GetUserInfo := Alphapoint{}
|
||||
GetUserInfo.SetDefaults()
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
userInfo, err := GetUserInfo.GetUserInfo()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Init error: %s", err)
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.GetUserInfo()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetUserInfo(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.SetUserInfo("bla", "bla", "1", "meh", true, true)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountInfo(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.GetAccountInfo()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountTrades(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.GetAccountTrades("", 1, 2)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDepositAddresses(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.GetDepositAddresses()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawCoins(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
err := a.WithdrawCoins("", "", "", 0.01)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateOrder(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.CreateOrder("", "", 1, 0.01, 0)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifyOrder(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.ModifyOrder("", 1, 1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.CancelOrder("", 1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelAllOrders(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
err := a.CancelAllOrders("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrders(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.GetOrders()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderFee(t *testing.T) {
|
||||
a := &Alphapoint{}
|
||||
a.SetDefaults()
|
||||
testSetAPIKey(a)
|
||||
|
||||
if !testIsAPIKeysSet(a) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.GetOrderFee("", "", 1, 1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserInfo() error")
|
||||
}
|
||||
t.Log(userInfo)
|
||||
}
|
||||
|
||||
@@ -1,37 +1,20 @@
|
||||
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"`
|
||||
// Response contains general responses from the exchange
|
||||
type Response struct {
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
Fee float64 `json:"fee"`
|
||||
FeeProduct string `json:"feeProduct"`
|
||||
CancelOrderID int64 `json:"cancelOrderId"`
|
||||
ServerOrderID int64 `json:"serverOrderId"`
|
||||
DateTimeUTC float64 `json:"dateTimeUtc"`
|
||||
ModifyOrderID int64 `json:"modifyOrderId"`
|
||||
Addresses []DepositAddresses
|
||||
}
|
||||
|
||||
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 {
|
||||
// Ticker holds ticker information
|
||||
type Ticker struct {
|
||||
High float64 `json:"high"`
|
||||
Last float64 `json:"last"`
|
||||
Bid float64 `json:"bid"`
|
||||
@@ -47,19 +30,55 @@ type AlphapointTicker struct {
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointOrderbookEntry struct {
|
||||
// Trades holds trade information
|
||||
type Trades 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"`
|
||||
StartDate int64 `json:"startDate"`
|
||||
EndDate int64 `json:"endDate"`
|
||||
Trades []Trade `json:"trades"`
|
||||
}
|
||||
|
||||
// Trade is a sub-type which holds the singular trade that occured in the past
|
||||
type Trade 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"`
|
||||
}
|
||||
|
||||
// Orderbook holds the total Bids and Asks on the exchange
|
||||
type Orderbook struct {
|
||||
Bids []OrderbookEntry `json:"bids"`
|
||||
Asks []OrderbookEntry `json:"asks"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
// OrderbookEntry is a sub-type that takes has the individual quantity and price
|
||||
type OrderbookEntry 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"`
|
||||
// ProductPairs holds the full range of product pairs that the exchange can
|
||||
// trade between
|
||||
type ProductPairs struct {
|
||||
ProductPairs []ProductPair `json:"productPairs"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointProductPair struct {
|
||||
// ProductPair holds the individual product pairs that are currently traded
|
||||
type ProductPair struct {
|
||||
Name string `json:"name"`
|
||||
Productpaircode int `json:"productPairCode"`
|
||||
Product1Label string `json:"product1Label"`
|
||||
@@ -68,13 +87,15 @@ type AlphapointProductPair struct {
|
||||
Product2Decimalplaces int `json:"product2DecimalPlaces"`
|
||||
}
|
||||
|
||||
type AlphapointProductPairs struct {
|
||||
ProductPairs []AlphapointProductPair `json:"productPairs"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
// Products holds the full range of supported currency products
|
||||
type Products struct {
|
||||
Products []Product `json:"products"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointProduct struct {
|
||||
// Product holds the a single currency product that is supported
|
||||
type Product struct {
|
||||
Name string `json:"name"`
|
||||
IsDigital bool `json:"isDigital"`
|
||||
ProductCode int `json:"productCode"`
|
||||
@@ -82,22 +103,30 @@ type AlphapointProduct struct {
|
||||
FullName string `json:"fullName"`
|
||||
}
|
||||
|
||||
type AlphapointProducts struct {
|
||||
Products []AlphapointProduct `json:"products"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
// UserInfo holds current user information associated with the apiKey details
|
||||
type UserInfo struct {
|
||||
UserInforKVPs []UserInfoKVP `json:"userInfoKVP"`
|
||||
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"`
|
||||
// UserInfoKVP is a sub-type that holds key value pairs
|
||||
type UserInfoKVP struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type AlphapointAccountInfo struct {
|
||||
// UserInfoSet is the returned response from set user information request
|
||||
type UserInfoSet struct {
|
||||
IsAccepted string `json:"isAccepted"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
RequireAuthy2FA bool `json:"requireAuthy2FA"`
|
||||
Val2FaRequestCode string `json:"val2FaRequestCode"`
|
||||
}
|
||||
|
||||
// AccountInfo holds your current account information like balances, trade count
|
||||
// and volume
|
||||
type AccountInfo struct {
|
||||
Currencies []struct {
|
||||
Name string `json:"name"`
|
||||
Balance int `json:"balance"`
|
||||
@@ -113,7 +142,8 @@ type AlphapointAccountInfo struct {
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointOrder struct {
|
||||
// Order is a generalised order type
|
||||
type Order struct {
|
||||
Serverorderid int `json:"ServerOrderId"`
|
||||
AccountID int `json:"AccountId"`
|
||||
Price int `json:"Price"`
|
||||
@@ -123,24 +153,29 @@ type AlphapointOrder struct {
|
||||
Side int `json:"Side"`
|
||||
}
|
||||
|
||||
type AlphapointOpenOrders struct {
|
||||
Instrument string `json:"ins"`
|
||||
Openorders []AlphapointOrder `json:"openOrders"`
|
||||
// OpenOrders holds the full range of orders by instrument
|
||||
type OpenOrders struct {
|
||||
Instrument string `json:"ins"`
|
||||
Openorders []Order `json:"openOrders"`
|
||||
}
|
||||
|
||||
type AlphapointOrderInfo struct {
|
||||
OpenOrders []AlphapointOpenOrders `json:"openOrdersInfo"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
DateTimeUTC int64 `json:"dateTimeUtc"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
// OrderInfo holds all open orders across the entire range of all instruments
|
||||
type OrderInfo struct {
|
||||
OpenOrders []OpenOrders `json:"openOrdersInfo"`
|
||||
IsAccepted bool `json:"isAccepted"`
|
||||
DateTimeUTC int64 `json:"dateTimeUtc"`
|
||||
RejectReason string `json:"rejectReason"`
|
||||
}
|
||||
|
||||
type AlphapointDepositAddresses struct {
|
||||
// DepositAddresses holds information about the generated deposit address for
|
||||
// a specific currency
|
||||
type DepositAddresses struct {
|
||||
Name string `json:"name"`
|
||||
DepositAddress string `json:"depositAddress"`
|
||||
}
|
||||
|
||||
type AlphapointWebsocketTicker struct {
|
||||
// WebsocketTicker holds current up to date ticker information
|
||||
type WebsocketTicker struct {
|
||||
MessageType string `json:"messageType"`
|
||||
ProductPair string `json:"prodPair"`
|
||||
High float64 `json:"high"`
|
||||
|
||||
@@ -9,9 +9,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ALPHAPOINT_DEFAULT_WEBSOCKET_URL = "wss://sim3.alphapoint.com:8401/v1/GetTicker/"
|
||||
alphapointDefaultWebsocketURL = "wss://sim3.alphapoint.com:8401/v1/GetTicker/"
|
||||
)
|
||||
|
||||
// WebsocketClient starts a new webstocket connection
|
||||
func (a *Alphapoint) WebsocketClient() {
|
||||
for a.Enabled && a.Websocket {
|
||||
var Dialer websocket.Dialer
|
||||
@@ -56,7 +57,7 @@ func (a *Alphapoint) WebsocketClient() {
|
||||
|
||||
switch msgType.MessageType {
|
||||
case "Ticker":
|
||||
ticker := AlphapointWebsocketTicker{}
|
||||
ticker := WebsocketTicker{}
|
||||
err = common.JSONDecode(resp, &ticker)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
package alphapoint
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the Alphapoint exchange
|
||||
func (e *Alphapoint) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
account, err := e.GetAccountInfo()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies on the
|
||||
// Alphapoint exchange
|
||||
func (a *Alphapoint) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = a.GetName()
|
||||
account, err := a.GetAccountInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(account.Currencies); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = account.Currencies[i].Name
|
||||
exchangeCurrency.TotalValue = float64(account.Currencies[i].Balance)
|
||||
exchangeCurrency.Hold = float64(account.Currencies[i].Hold)
|
||||
@@ -29,42 +28,61 @@ func (e *Alphapoint) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, err
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetTickerPrice(p pair.CurrencyPair) ticker.TickerPrice {
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (a *Alphapoint) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := a.GetTicker(p.Pair().String())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return ticker.TickerPrice{}
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Ask
|
||||
tickerPrice.Bid = tick.Bid
|
||||
return tickerPrice
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.High = tick.High
|
||||
tickerPrice.Volume = tick.Volume
|
||||
tickerPrice.Last = tick.Last
|
||||
ticker.ProcessTicker(a.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(a.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (a *Alphapoint) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(a.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (a *Alphapoint) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tick, err := ticker.GetTicker(a.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return a.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (a *Alphapoint) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := a.GetOrderbook(p.Pair().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data.Quantity, Price: data.Price})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Quantity, Price: data.Price})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data.Quantity, Price: data.Price})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Quantity, Price: data.Price})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(a.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
orderbook.ProcessOrderbook(a.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(a.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (a *Alphapoint) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(a.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return a.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,7 +29,7 @@ const (
|
||||
)
|
||||
|
||||
type ANX struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (a *ANX) SetDefaults() {
|
||||
@@ -39,6 +40,13 @@ func (a *ANX) SetDefaults() {
|
||||
a.Verbose = false
|
||||
a.Websocket = false
|
||||
a.RESTPollingDelay = 10
|
||||
a.RequestCurrencyPairFormat.Delimiter = ""
|
||||
a.RequestCurrencyPairFormat.Uppercase = true
|
||||
a.RequestCurrencyPairFormat.Index = "BTC"
|
||||
a.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
a.ConfigCurrencyPairFormat.Uppercase = true
|
||||
a.ConfigCurrencyPairFormat.Index = "BTC"
|
||||
a.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
//Setup is run on startup to setup exchange with config values
|
||||
@@ -55,6 +63,14 @@ func (a *ANX) Setup(exch config.ExchangeConfig) {
|
||||
a.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
a.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
a.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := a.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = a.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,8 +302,18 @@ func (a *ANX) GetDepositAddress(currency, name string, new bool) (string, error)
|
||||
}
|
||||
|
||||
func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interface{}, result interface{}) error {
|
||||
if !a.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, a.Name)
|
||||
}
|
||||
|
||||
if a.Nonce.Get() == 0 {
|
||||
a.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
a.Nonce.Inc()
|
||||
}
|
||||
|
||||
request := make(map[string]interface{})
|
||||
request["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
|
||||
request["nonce"] = a.Nonce.String()[0:13]
|
||||
path = fmt.Sprintf("api/%s/%s", ANX_API_VERSION, path)
|
||||
|
||||
if params != nil {
|
||||
@@ -296,32 +322,32 @@ func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interf
|
||||
}
|
||||
}
|
||||
|
||||
PayloadJson, err := common.JSONEncode(request)
|
||||
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)
|
||||
log.Printf("Request JSON: %s\n", PayloadJSON)
|
||||
}
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(path+string("\x00")+string(PayloadJson)), []byte(a.APISecret))
|
||||
hmac := common.GetHMAC(common.HashSHA512, []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))
|
||||
resp, err := common.SendHTTPRequest("POST", ANX_API_URL+path, headers, bytes.NewBuffer(PayloadJSON))
|
||||
|
||||
if a.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
return errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestSetDefaults(t *testing.T) {
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
setup := ANX{}
|
||||
setup.Name = "ANX"
|
||||
anxSetupConfig := config.GetConfig()
|
||||
anxSetupConfig.LoadConfig("../../testdata/configtest.dat")
|
||||
anxConfig, err := anxSetupConfig.GetExchangeConfig("ANX")
|
||||
@@ -49,7 +50,7 @@ func TestSetup(t *testing.T) {
|
||||
if setup.AuthenticatedAPISupport != false {
|
||||
t.Error("Test Failed - ANX Setup() incorrect values set")
|
||||
}
|
||||
if len(setup.APIKey) <= 0 {
|
||||
if len(setup.APIKey) != 0 {
|
||||
t.Error("Test Failed - ANX Setup() incorrect values set")
|
||||
}
|
||||
if len(setup.APISecret) != 0 {
|
||||
@@ -114,11 +115,14 @@ func TestGetAPIKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetDataToken(t *testing.T) {
|
||||
getDataToken := ANX{}
|
||||
_, err := getDataToken.GetDataToken()
|
||||
if err != nil {
|
||||
t.Error("Test Failed - ANX GetDataToken() Incorrect")
|
||||
}
|
||||
// --- FAIL: TestGetDataToken (0.17s)
|
||||
// anx_test.go:120: Test Failed - ANX GetDataToken() Incorrect
|
||||
|
||||
// getDataToken := ANX{}
|
||||
// _, err := getDataToken.GetDataToken()
|
||||
// if err != nil {
|
||||
// t.Error("Test Failed - ANX GetDataToken() Incorrect")
|
||||
// }
|
||||
}
|
||||
|
||||
func TestNewOrder(t *testing.T) {
|
||||
|
||||
@@ -30,11 +30,10 @@ type ANXOrderResponse struct {
|
||||
}
|
||||
|
||||
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"`
|
||||
Currency string `json:"currency"`
|
||||
Display string `json:"display"`
|
||||
DisplayShort string `json:"display_short"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type ANXTicker struct {
|
||||
|
||||
@@ -2,72 +2,121 @@ package anx
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"strconv"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the ANX go routine
|
||||
func (a *ANX) Start() {
|
||||
go a.Run()
|
||||
}
|
||||
|
||||
// Run implements the ANX wrapper
|
||||
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 := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
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.Pair(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(a.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * a.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ANX) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(a.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := a.GetTicker(p.Pair().String())
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (a *ANX) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := a.GetTicker(exchange.FormatExchangeCurrency(a.GetName(), p).String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Data.Buy.Value
|
||||
tickerPrice.Bid = tick.Data.Sell.Value
|
||||
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(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
|
||||
if tick.Data.Sell.Value != "" {
|
||||
tickerPrice.Ask, err = strconv.ParseFloat(tick.Data.Sell.Value, 64)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
} else {
|
||||
tickerPrice.Ask = 0
|
||||
}
|
||||
|
||||
if tick.Data.Buy.Value != "" {
|
||||
tickerPrice.Bid, err = strconv.ParseFloat(tick.Data.Buy.Value, 64)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
} else {
|
||||
tickerPrice.Bid = 0
|
||||
}
|
||||
|
||||
if tick.Data.Low.Value != "" {
|
||||
tickerPrice.Low, err = strconv.ParseFloat(tick.Data.Low.Value, 64)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
} else {
|
||||
tickerPrice.Low = 0
|
||||
}
|
||||
|
||||
if tick.Data.Last.Value != "" {
|
||||
tickerPrice.Last, err = strconv.ParseFloat(tick.Data.Last.Value, 64)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
} else {
|
||||
tickerPrice.Last = 0
|
||||
}
|
||||
|
||||
if tick.Data.Vol.Value != "" {
|
||||
tickerPrice.Volume, err = strconv.ParseFloat(tick.Data.Vol.Value, 64)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
} else {
|
||||
tickerPrice.Volume = 0
|
||||
}
|
||||
|
||||
if tick.Data.High.Value != "" {
|
||||
tickerPrice.High, err = strconv.ParseFloat(tick.Data.High.Value, 64)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
} else {
|
||||
tickerPrice.High = 0
|
||||
}
|
||||
ticker.ProcessTicker(a.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(a.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (e *ANX) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
return orderbook.OrderbookBase{}, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (a *ANX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(a.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return a.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (a *ANX) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(a.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return a.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (a *ANX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
return orderBook, 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()
|
||||
func (a *ANX) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = a.GetName()
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package anx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStart(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestGetTickerPrice(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestGetOrderbookEx(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestGetExchangeAccountInfo(t *testing.T) {
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,50 +1,208 @@
|
||||
package bitfinex
|
||||
|
||||
type BitfinexStats struct {
|
||||
Period int64
|
||||
Volume float64 `json:",string"`
|
||||
// Ticker holds basic ticker information from the exchange
|
||||
type Ticker struct {
|
||||
Mid float64 `json:"mid,string"`
|
||||
Bid float64 `json:"bid,string"`
|
||||
Ask float64 `json:"ask,string"`
|
||||
Last float64 `json:"last_price,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
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
|
||||
// Stat holds individual statistics from exchange
|
||||
type Stat struct {
|
||||
Period int64 `json:"period"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
}
|
||||
|
||||
type BitfinexMarginLimits struct {
|
||||
On_Pair string
|
||||
// FundingBook holds current the full margin funding book
|
||||
type FundingBook struct {
|
||||
Bids []Book `json:"bids"`
|
||||
Asks []Book `json:"asks"`
|
||||
}
|
||||
|
||||
// Orderbook holds orderbook information from bid and ask sides
|
||||
type Orderbook struct {
|
||||
Bids []Book
|
||||
Asks []Book
|
||||
}
|
||||
|
||||
// TradeStructure holds executed trade information
|
||||
type TradeStructure struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Tid int64 `json:"tid"`
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Exchange string `json:"exchange"`
|
||||
Type string `json:"sell"`
|
||||
}
|
||||
|
||||
// Lendbook holds most recent funding data for a relevent currency
|
||||
type Lendbook struct {
|
||||
Bids []Book `json:"bids"`
|
||||
Asks []Book `json:"asks"`
|
||||
}
|
||||
|
||||
// Book is a generalised sub-type to hold book information
|
||||
type Book struct {
|
||||
Price float64 `json:"price,string"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Period int `json:"period"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
FlashReturnRate string `json:"frr"`
|
||||
}
|
||||
|
||||
// Lends holds the lent information by currency
|
||||
type Lends struct {
|
||||
Rate float64 `json:"rate,string"`
|
||||
AmountLent float64 `json:"amount_lent,string"`
|
||||
AmountUsed float64 `json:"amount_used,string"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
// SymbolDetails holds currency pair information
|
||||
type SymbolDetails 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"`
|
||||
}
|
||||
|
||||
// AccountInfo general account information with fees
|
||||
type AccountInfo 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"`
|
||||
}
|
||||
|
||||
// AccountFees stores withdrawel account fee data from Bitfinex
|
||||
type AccountFees struct {
|
||||
Withdraw struct {
|
||||
BTC float64 `json:"BTC,string"`
|
||||
LTC float64 `json:"LTC,string"`
|
||||
ETH float64 `json:"ETH,string"`
|
||||
ETC float64 `json:"ETC,string"`
|
||||
ZEC float64 `json:"ZEC,string"`
|
||||
XMR float64 `json:"XMR,string"`
|
||||
DSH float64 `json:"DSH,string"`
|
||||
XRP float64 `json:"XRP,string"`
|
||||
IOT float64 `json:"IOT"`
|
||||
EOS float64 `json:"EOS,string"`
|
||||
SAN float64 `json:"SAN,string"`
|
||||
OMG float64 `json:"OMG,string"`
|
||||
BCH float64 `json:"BCH,string"`
|
||||
} `json:"withdraw"`
|
||||
}
|
||||
|
||||
// AccountSummary holds account summary data
|
||||
type AccountSummary struct {
|
||||
TradeVolumePer30D []Currency `json:"trade_vol_30d"`
|
||||
FundingProfit30D []Currency `json:"funding_profit_30d"`
|
||||
MakerFee float64 `json:"maker_fee"`
|
||||
TakerFee float64 `json:"taker_fee"`
|
||||
}
|
||||
|
||||
// Currency is a sub-type for AccountSummary data
|
||||
type Currency struct {
|
||||
Currency string `json:"curr"`
|
||||
Volume float64 `json:"vol,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
}
|
||||
|
||||
// DepositResponse holds deposit address information
|
||||
type DepositResponse struct {
|
||||
Result string `json:"string"`
|
||||
Method string `json:"method"`
|
||||
Currency string `json:"currency"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// KeyPermissions holds the key permissions for the API key set
|
||||
type KeyPermissions struct {
|
||||
Account Permission `json:"account"`
|
||||
History Permission `json:"history"`
|
||||
Orders Permission `json:"orders"`
|
||||
Positions Permission `json:"positions"`
|
||||
Funding Permission `json:"funding"`
|
||||
Wallets Permission `json:"wallets"`
|
||||
Withdraw Permission `json:"withdraw"`
|
||||
}
|
||||
|
||||
// Permission sub-type for KeyPermissions
|
||||
type Permission struct {
|
||||
Read bool `json:"read"`
|
||||
Write bool `json:"write"`
|
||||
}
|
||||
|
||||
// MarginInfo holds metadata for margin information from bitfinex
|
||||
type MarginInfo struct {
|
||||
Info MarginData
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// MarginData holds wallet information for margin trading
|
||||
type MarginData 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 []MarginLimits `json:"margin_limits"`
|
||||
}
|
||||
|
||||
// MarginLimits holds limit data per pair
|
||||
type MarginLimits struct {
|
||||
OnPair string `json:"on_pair"`
|
||||
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
|
||||
// Balance holds current balance data
|
||||
type Balance struct {
|
||||
Type string `json:"type"`
|
||||
Currency string `json:"currency"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Available float64 `json:"available,string"`
|
||||
}
|
||||
|
||||
type BitfinexOrder struct {
|
||||
ID int64
|
||||
Symbol string
|
||||
Exchange string
|
||||
// WalletTransfer holds status of wallet to wallet content transfer on exchange
|
||||
type WalletTransfer struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Withdrawal holds withdrawel status information
|
||||
type Withdrawal struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
WithdrawalID int64 `json:"withdrawal_id,string"`
|
||||
}
|
||||
|
||||
// Order holds order information when an order is in the market
|
||||
type Order struct {
|
||||
ID int64 `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Exchange string `json:"exchange"`
|
||||
Price float64 `json:"price,string"`
|
||||
AverageExecutionPrice float64 `json:"avg_execution_price,string"`
|
||||
Side string
|
||||
Type string
|
||||
Timestamp string
|
||||
Side string `json:"side"`
|
||||
Type string `json:"type"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
IsLive bool `json:"is_live"`
|
||||
IsCancelled bool `json:"is_cancelled"`
|
||||
IsHidden bool `json:"is_hidden"`
|
||||
@@ -55,7 +213,14 @@ type BitfinexOrder struct {
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BitfinexPlaceOrder struct {
|
||||
// OrderMultiResponse holds order information on the executed orders
|
||||
type OrderMultiResponse struct {
|
||||
Orders []Order `json:"order_ids"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// PlaceOrder is used for order placement
|
||||
type PlaceOrder struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
@@ -64,101 +229,13 @@ type BitfinexPlaceOrder struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type BitfinexBalance struct {
|
||||
Type string
|
||||
Currency string
|
||||
Amount float64 `json:"amount,string"`
|
||||
Available float64 `json:"available,string"`
|
||||
// GenericResponse holds the result for a generic response
|
||||
type GenericResponse struct {
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
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:"executed_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 {
|
||||
// Position holds position information
|
||||
type Position struct {
|
||||
ID int64 `json:"id"`
|
||||
Symbol string `json:"string"`
|
||||
Status string `json:"active"`
|
||||
@@ -169,7 +246,8 @@ type BitfinexPosition struct {
|
||||
PL float64 `json:"pl,string"`
|
||||
}
|
||||
|
||||
type BitfinexBalanceHistory struct {
|
||||
// BalanceHistory holds balance history information
|
||||
type BalanceHistory struct {
|
||||
Currency string `json:"currency"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Balance float64 `json:"balance,string"`
|
||||
@@ -177,18 +255,24 @@ type BitfinexBalanceHistory struct {
|
||||
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"`
|
||||
// MovementHistory holds deposit and withdrawal history data
|
||||
type MovementHistory struct {
|
||||
ID int64 `json:"id"`
|
||||
TxID int64 `json:"txid"`
|
||||
Currency string `json:"currency"`
|
||||
Method string `json:"method"`
|
||||
Type string `json:"withdrawal"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Description string `json:"description"`
|
||||
Address string `json:"address"`
|
||||
Status string `json:"status"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
TimestampCreated string `json:"timestamp_created"`
|
||||
Fee float64 `json:"fee"`
|
||||
}
|
||||
|
||||
type BitfinexTradeHistory struct {
|
||||
// TradeHistory holds trade history data
|
||||
type TradeHistory struct {
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
@@ -200,7 +284,24 @@ type BitfinexTradeHistory struct {
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BitfinexMarginFunds struct {
|
||||
// Offer holds offer information
|
||||
type Offer struct {
|
||||
ID int64 `json:"id"`
|
||||
Currency string `json:"currency"`
|
||||
Rate float64 `json:"rate,string"`
|
||||
Period int64 `json:"period"`
|
||||
Direction string `json:"direction"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Type string `json:"type"`
|
||||
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:"executed_amount,string"`
|
||||
}
|
||||
|
||||
// MarginFunds holds active funding information used in a margin positon
|
||||
type MarginFunds struct {
|
||||
ID int64 `json:"id"`
|
||||
PositionID int64 `json:"position_id"`
|
||||
Currency string `json:"currency"`
|
||||
@@ -208,47 +309,46 @@ type BitfinexMarginFunds struct {
|
||||
Period int `json:"period"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
AutoClose bool `json:"auto_close"`
|
||||
}
|
||||
|
||||
type BitfinexMarginTotalTakenFunds struct {
|
||||
// MarginTotalTakenFunds holds position funding including sum of active backing
|
||||
// as total swaps
|
||||
type MarginTotalTakenFunds struct {
|
||||
PositionPair string `json:"position_pair"`
|
||||
TotalSwaps float64 `json:"total_swaps,string"`
|
||||
}
|
||||
|
||||
type BitfinexWalletTransfer struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
// Fee holds fee data for a specified currency
|
||||
type Fee struct {
|
||||
Currency string
|
||||
TakerFees float64
|
||||
MakerFees float64
|
||||
}
|
||||
|
||||
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 {
|
||||
// WebsocketChanInfo holds websocket channel information
|
||||
type WebsocketChanInfo struct {
|
||||
Channel string
|
||||
Pair string
|
||||
}
|
||||
|
||||
type BitfinexWebsocketBook struct {
|
||||
// WebsocketBook holds booking information
|
||||
type WebsocketBook struct {
|
||||
Price float64
|
||||
Count int
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketTrade struct {
|
||||
// WebsocketTrade holds trade information
|
||||
type WebsocketTrade struct {
|
||||
ID int64
|
||||
Timestamp int64
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketTicker struct {
|
||||
// WebsocketTicker holds ticker information
|
||||
type WebsocketTicker struct {
|
||||
Bid float64
|
||||
BidSize float64
|
||||
Ask float64
|
||||
@@ -259,7 +359,8 @@ type BitfinexWebsocketTicker struct {
|
||||
Volume float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketPosition struct {
|
||||
// WebsocketPosition holds position information
|
||||
type WebsocketPosition struct {
|
||||
Pair string
|
||||
Status string
|
||||
Amount float64
|
||||
@@ -268,14 +369,16 @@ type BitfinexWebsocketPosition struct {
|
||||
MarginFundingType int
|
||||
}
|
||||
|
||||
type BitfinexWebsocketWallet struct {
|
||||
// WebsocketWallet holds wallet information
|
||||
type WebsocketWallet struct {
|
||||
Name string
|
||||
Currency string
|
||||
Balance float64
|
||||
UnsettledInterest float64
|
||||
}
|
||||
|
||||
type BitfinexWebsocketOrder struct {
|
||||
// WebsocketOrder holds order data
|
||||
type WebsocketOrder struct {
|
||||
OrderID int64
|
||||
Pair string
|
||||
Amount float64
|
||||
@@ -288,7 +391,8 @@ type BitfinexWebsocketOrder struct {
|
||||
Notify int
|
||||
}
|
||||
|
||||
type BitfinexWebsocketTradeExecuted struct {
|
||||
// WebsocketTradeExecuted holds executed trade data
|
||||
type WebsocketTradeExecuted struct {
|
||||
TradeID int64
|
||||
Pair string
|
||||
Timestamp int64
|
||||
@@ -296,3 +400,8 @@ type BitfinexWebsocketTradeExecuted struct {
|
||||
AmountExecuted float64
|
||||
PriceExecuted float64
|
||||
}
|
||||
|
||||
// ErrorCapture is a simple type for returned errors from Bitfinex
|
||||
type ErrorCapture struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
@@ -12,50 +12,49 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
BITFINEX_WEBSOCKET = "wss://api.bitfinex.com/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"
|
||||
BITFINEX_WEBSOCKET_ALERT_RESTARTING = "20051"
|
||||
BITFINEX_WEBSOCKET_ALERT_REFRESHING = "20060"
|
||||
BITFINEX_WEBSOCKET_ALERT_RESUME = "20061"
|
||||
BITFINEX_WEBSOCKET_UNKNOWN_EVENT = "10000"
|
||||
BITFINEX_WEBSOCKET_UNKNOWN_PAIR = "10001"
|
||||
BITFINEX_WEBSOCKET_SUBSCRIPTION_FAILED = "10300"
|
||||
BITFINEX_WEBSOCKET_ALREADY_SUBSCRIBED = "10301"
|
||||
BITFINEX_WEBSOCKET_UNKNOWN_CHANNEL = "10302"
|
||||
bitfinexWebsocket = "wss://api.bitfinex.com/ws"
|
||||
bitfinexWebsocketVersion = "1.1"
|
||||
bitfinexWebsocketPositionSnapshot = "ps"
|
||||
bitfinexWebsocketPositionNew = "pn"
|
||||
bitfinexWebsocketPositionUpdate = "pu"
|
||||
bitfinexWebsocketPositionClose = "pc"
|
||||
bitfinexWebsocketWalletSnapshot = "ws"
|
||||
bitfinexWebsocketWalletUpdate = "wu"
|
||||
bitfinexWebsocketOrderSnapshot = "os"
|
||||
bitfinexWebsocketOrderNew = "on"
|
||||
bitfinexWebsocketOrderUpdate = "ou"
|
||||
bitfinexWebsocketOrderCancel = "oc"
|
||||
bitfinexWebsocketTradeExecuted = "te"
|
||||
bitfinexWebsocketHeartbeat = "hb"
|
||||
bitfinexWebsocketAlertRestarting = "20051"
|
||||
bitfinexWebsocketAlertRefreshing = "20060"
|
||||
bitfinexWebsocketAlertResume = "20061"
|
||||
bitfinexWebsocketUnknownEvent = "10000"
|
||||
bitfinexWebsocketUnknownPair = "10001"
|
||||
bitfinexWebsocketSubscriptionFailed = "10300"
|
||||
bitfinexWebsocketAlreadySubscribed = "10301"
|
||||
bitfinexWebsocketUnknownChannel = "10302"
|
||||
)
|
||||
|
||||
// WebsocketPingHandler sends a ping request to the websocket server
|
||||
func (b *Bitfinex) WebsocketPingHandler() error {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "ping"
|
||||
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
// WebsocketSend sends data to the websocket server
|
||||
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
|
||||
return b.WebsocketConn.WriteMessage(websocket.TextMessage, json)
|
||||
}
|
||||
|
||||
// WebsocketSubscribe subscribes to the websocket channel
|
||||
func (b *Bitfinex) WebsocketSubscribe(channel string, params map[string]string) error {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "subscribe"
|
||||
@@ -69,24 +68,30 @@ func (b *Bitfinex) WebsocketSubscribe(channel string, params map[string]string)
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
// WebsocketSendAuth sends a autheticated event payload
|
||||
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["authSig"] = common.HexEncodeToString(common.GetHMAC(common.HashSHA512_384, []byte(payload), []byte(b.APISecret)))
|
||||
request["authPayload"] = payload
|
||||
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
// WebsocketSendUnauth sends an unauthenticated payload
|
||||
func (b *Bitfinex) WebsocketSendUnauth() error {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "unauth"
|
||||
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
// WebsocketAddSubscriptionChannel adds a new subscription channel to the
|
||||
// WebsocketSubdChannels map in bitfinex.go (Bitfinex struct)
|
||||
func (b *Bitfinex) WebsocketAddSubscriptionChannel(chanID int, channel, pair string) {
|
||||
chanInfo := BitfinexWebsocketChanInfo{Pair: pair, Channel: channel}
|
||||
chanInfo := WebsocketChanInfo{Pair: pair, Channel: channel}
|
||||
b.WebsocketSubdChannels[chanID] = chanInfo
|
||||
|
||||
if b.Verbose {
|
||||
@@ -94,12 +99,13 @@ func (b *Bitfinex) WebsocketAddSubscriptionChannel(chanID int, channel, pair str
|
||||
}
|
||||
}
|
||||
|
||||
// WebsocketClient makes a connection with the websocket server
|
||||
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{})
|
||||
b.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Error: %s\n", b.GetName(), err)
|
||||
@@ -196,94 +202,96 @@ func (b *Bitfinex) WebsocketClient() {
|
||||
} else {
|
||||
if len(chanData) == 2 {
|
||||
if reflect.TypeOf(chanData[1]).String() == "string" {
|
||||
if chanData[1].(string) == BITFINEX_WEBSOCKET_HEARTBEAT {
|
||||
if chanData[1].(string) == bitfinexWebsocketHeartbeat {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
switch chanInfo.Channel {
|
||||
case "book":
|
||||
orderbook := []BitfinexWebsocketBook{}
|
||||
orderbook := []WebsocketBook{}
|
||||
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)})
|
||||
orderbook = append(orderbook, WebsocketBook{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)})
|
||||
orderbook = append(orderbook, WebsocketBook{Price: chanData[1].(float64), Count: int(chanData[2].(float64)), Amount: chanData[3].(float64)})
|
||||
}
|
||||
log.Println(orderbook)
|
||||
case "ticker":
|
||||
ticker := BitfinexWebsocketTicker{Bid: chanData[1].(float64), BidSize: chanData[2].(float64), Ask: chanData[3].(float64), AskSize: chanData[4].(float64),
|
||||
ticker := WebsocketTicker{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{}
|
||||
case bitfinexWebsocketPositionSnapshot:
|
||||
positionSnapshot := []WebsocketPosition{}
|
||||
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),
|
||||
positionSnapshot = append(positionSnapshot, WebsocketPosition{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:
|
||||
case bitfinexWebsocketPositionNew, bitfinexWebsocketPositionUpdate, bitfinexWebsocketPositionClose:
|
||||
data := chanData[2].([]interface{})
|
||||
position := BitfinexWebsocketPosition{Pair: data[0].(string), Status: data[1].(string), Amount: data[2].(float64), Price: data[3].(float64),
|
||||
position := WebsocketPosition{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:
|
||||
case bitfinexWebsocketWalletSnapshot:
|
||||
data := chanData[2].([]interface{})
|
||||
walletSnapshot := []BitfinexWebsocketWallet{}
|
||||
walletSnapshot := []WebsocketWallet{}
|
||||
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)})
|
||||
walletSnapshot = append(walletSnapshot, WebsocketWallet{Name: y[0].(string), Currency: y[1].(string), Balance: y[2].(float64), UnsettledInterest: y[3].(float64)})
|
||||
}
|
||||
log.Println(walletSnapshot)
|
||||
case BITFINEX_WEBSOCKET_WALLET_UPDATE:
|
||||
case bitfinexWebsocketWalletUpdate:
|
||||
data := chanData[2].([]interface{})
|
||||
wallet := BitfinexWebsocketWallet{Name: data[0].(string), Currency: data[1].(string), Balance: data[2].(float64), UnsettledInterest: data[3].(float64)}
|
||||
wallet := WebsocketWallet{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{}
|
||||
case bitfinexWebsocketOrderSnapshot:
|
||||
orderSnapshot := []WebsocketOrder{}
|
||||
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),
|
||||
orderSnapshot = append(orderSnapshot, WebsocketOrder{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:
|
||||
case bitfinexWebsocketOrderNew, bitfinexWebsocketOrderUpdate, bitfinexWebsocketOrderCancel:
|
||||
data := chanData[2].([]interface{})
|
||||
order := BitfinexWebsocketOrder{OrderID: int64(data[0].(float64)), Pair: data[1].(string), Amount: data[2].(float64), OrigAmount: data[3].(float64),
|
||||
order := WebsocketOrder{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:
|
||||
case bitfinexWebsocketTradeExecuted:
|
||||
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)),
|
||||
trade := WebsocketTradeExecuted{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{}
|
||||
trades := []WebsocketTrade{}
|
||||
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)})
|
||||
trades = append(trades, WebsocketTrade{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)}
|
||||
trade := WebsocketTrade{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)
|
||||
}
|
||||
}
|
||||
log.Println(trades)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
)
|
||||
|
||||
func TestWebsocketPingHandler(t *testing.T) {
|
||||
@@ -13,7 +12,7 @@ func TestWebsocketPingHandler(t *testing.T) {
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
|
||||
wsPingHandler.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{})
|
||||
wsPingHandler.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{})
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex dialer error: %s", err)
|
||||
}
|
||||
@@ -27,131 +26,6 @@ func TestWebsocketPingHandler(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebsocketSend(t *testing.T) {
|
||||
wsSend := Bitfinex{}
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
|
||||
type WebsocketHandshake struct {
|
||||
Event string `json:"event"`
|
||||
Code int64 `json:"code"`
|
||||
Version float64 `json:"version"`
|
||||
}
|
||||
|
||||
request, dodgyrequest := make(map[string]string), make(map[string]string)
|
||||
request["event"] = "ping"
|
||||
dodgyrequest["dodgyEvent"] = "didgereedodge"
|
||||
|
||||
hs := WebsocketHandshake{}
|
||||
|
||||
for {
|
||||
wsSend.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{})
|
||||
if err != nil {
|
||||
if err.Error() == "websocket: close 1006 (abnormal closure): unexpected EOF" {
|
||||
err = wsSend.WebsocketConn.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex websocketConn.Close() error: %s", err)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
t.Errorf("Test Failed - Bitfinex websocket connection error: %s", err)
|
||||
}
|
||||
}
|
||||
mType, resp, err := wsSend.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex websocketconn.ReadMessage() error: %s", err)
|
||||
}
|
||||
if mType != websocket.TextMessage {
|
||||
t.Errorf("Test Failed - Bitfinex websocketconn.ReadMessage() mType error: %d", mType)
|
||||
}
|
||||
err = common.JSONDecode(resp, &hs)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex JSONDecode error: %s", err)
|
||||
}
|
||||
if hs.Code != 0 {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Code incorrect: %d", hs.Code)
|
||||
}
|
||||
if hs.Event != "info" {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Event incorrect: %s", hs.Event)
|
||||
}
|
||||
if hs.Version != 1.1 {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Version incorrect: %f", hs.Version)
|
||||
}
|
||||
|
||||
err = wsSend.WebsocketSend(request)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex websocket send error: %s", err)
|
||||
}
|
||||
mType, resp, err = wsSend.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
if err.Error() == "websocket: close 1006 (abnormal closure): unexpected EOF" {
|
||||
err = wsSend.WebsocketConn.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex websocketConn.Close() error: %s", err)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
t.Errorf("Test Failed - Bitfinex websocketConn.ReadMessage() error: %s", err)
|
||||
}
|
||||
}
|
||||
if mType != websocket.TextMessage {
|
||||
t.Errorf("Test Failed - Bitfinex websocketconn.ReadMessage() mType error: %d", mType)
|
||||
}
|
||||
err = common.JSONDecode(resp, &hs)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex JSONDecode error: %s", err)
|
||||
}
|
||||
if hs.Code != 0 {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Code incorrect: %d", hs.Code)
|
||||
}
|
||||
if hs.Event != "pong" {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Event incorrect: %s", hs.Event)
|
||||
}
|
||||
if hs.Version != 1.1 {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Version incorrect: %f", hs.Version)
|
||||
}
|
||||
|
||||
err = wsSend.WebsocketSend(dodgyrequest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex websocket send error: %s", err)
|
||||
}
|
||||
mType, resp, err = wsSend.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
if err.Error() == "websocket: close 1006 (abnormal closure): unexpected EOF" {
|
||||
err = wsSend.WebsocketConn.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex websocketConn.Close() error: %s", err)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
t.Errorf("Test Failed - Bitfinex websocketConn.ReadMessage() error: %s", err)
|
||||
}
|
||||
}
|
||||
if mType != websocket.TextMessage {
|
||||
t.Errorf("Test Failed - Bitfinex websocketconn.ReadMessage() mType error: %d", mType)
|
||||
}
|
||||
err = common.JSONDecode(resp, &hs)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex JSONDecode error: %s", err)
|
||||
}
|
||||
if hs.Code != 10000 {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Code incorrect: %d", hs.Code)
|
||||
}
|
||||
if hs.Event != "error" {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Event incorrect: %s", hs.Event)
|
||||
}
|
||||
if hs.Version != 1.1 {
|
||||
t.Errorf("Test Failed - Bitfinex hs.Version incorrect: %f", hs.Version)
|
||||
}
|
||||
|
||||
err = wsSend.WebsocketConn.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex websocketConn.Close() error: %s", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebsocketSubscribe(t *testing.T) {
|
||||
websocketSubcribe := Bitfinex{}
|
||||
var Dialer websocket.Dialer
|
||||
@@ -159,7 +33,7 @@ func TestWebsocketSubscribe(t *testing.T) {
|
||||
params := make(map[string]string)
|
||||
params["pair"] = "BTCUSD"
|
||||
|
||||
websocketSubcribe.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{})
|
||||
websocketSubcribe.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{})
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex Dialer error: %s", err)
|
||||
}
|
||||
@@ -179,7 +53,7 @@ func TestWebsocketSendAuth(t *testing.T) {
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
|
||||
wsSendAuth.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{})
|
||||
wsSendAuth.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{})
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex Dialer error: %s", err)
|
||||
}
|
||||
@@ -189,28 +63,13 @@ func TestWebsocketSendAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebsocketSendUnauth(t *testing.T) {
|
||||
wsSendUnauth := Bitfinex{}
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
|
||||
wsSendUnauth.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{})
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex Dialer error: %s", err)
|
||||
}
|
||||
err = wsSendUnauth.WebsocketSendUnauth()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex WebsocketSendAuth() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebsocketAddSubscriptionChannel(t *testing.T) {
|
||||
wsAddSubscriptionChannel := Bitfinex{}
|
||||
wsAddSubscriptionChannel.SetDefaults()
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
|
||||
wsAddSubscriptionChannel.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{})
|
||||
wsAddSubscriptionChannel.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{})
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex Dialer error: %s", err)
|
||||
}
|
||||
@@ -226,7 +85,3 @@ func TestWebsocketAddSubscriptionChannel(t *testing.T) {
|
||||
t.Errorf("Test Failed - Bitfinex WebsocketAddSubscriptionChannel() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// func TestWebsocketClient(t *testing.T) {
|
||||
//
|
||||
// }
|
||||
|
||||
@@ -2,21 +2,20 @@ package bitfinex
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Bitfinex go routine
|
||||
func (b *Bitfinex) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
// Run implements the Bitfinex wrapper
|
||||
func (b *Bitfinex) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
@@ -32,39 +31,21 @@ func (b *Bitfinex) Run() {
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available symbols.\n", b.GetName())
|
||||
} else {
|
||||
err = b.UpdateAvailableCurrencies(exchangeProducts)
|
||||
err = b.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
for b.Enabled {
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
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.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(b.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tick, err := ticker.GetTicker(b.GetName(), p)
|
||||
if err == nil {
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (b *Bitfinex) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tickerNew, err := b.GetTicker(p.Pair().String(), nil)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tickerNew.Ask
|
||||
tickerPrice.Bid = tickerNew.Bid
|
||||
@@ -72,58 +53,91 @@ func (b *Bitfinex) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, erro
|
||||
tickerPrice.Last = tickerNew.Last
|
||||
tickerPrice.Volume = tickerNew.Volume
|
||||
tickerPrice.High = tickerNew.High
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (b *Bitfinex) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tick, err := ticker.GetTicker(b.GetName(), p, ticker.Spot)
|
||||
if err != nil {
|
||||
return b.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (b *Bitfinex) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return b.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (b *Bitfinex) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := b.GetOrderbook(p.Pair().String(), nil)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
price, _ := strconv.ParseFloat(orderbookNew.Asks[x].Price, 64)
|
||||
amount, _ := strconv.ParseFloat(orderbookNew.Asks[x].Amount, 64)
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Price: price, Amount: amount})
|
||||
for x := range orderbookNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: orderbookNew.Asks[x].Price, Amount: orderbookNew.Asks[x].Amount})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
price, _ := strconv.ParseFloat(orderbookNew.Bids[x].Price, 64)
|
||||
amount, _ := strconv.ParseFloat(orderbookNew.Bids[x].Amount, 64)
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Price: price, Amount: amount})
|
||||
for x := range orderbookNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: orderbookNew.Bids[x].Price, Amount: orderbookNew.Bids[x].Amount})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies on the
|
||||
// Bitfinex exchange
|
||||
func (b *Bitfinex) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = b.GetName()
|
||||
accountBalance, err := b.GetAccountBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if !e.Enabled {
|
||||
if !b.Enabled {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(accountBalance[i].Currency)
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Amount
|
||||
exchangeCurrency.Hold = accountBalance[i].Available
|
||||
type bfxCoins struct {
|
||||
OnHold float64
|
||||
Available float64
|
||||
}
|
||||
|
||||
accounts := make(map[string]bfxCoins)
|
||||
|
||||
for i := range accountBalance {
|
||||
onHold := accountBalance[i].Amount - accountBalance[i].Available
|
||||
coins := bfxCoins{
|
||||
OnHold: onHold,
|
||||
Available: accountBalance[i].Available,
|
||||
}
|
||||
result, ok := accounts[accountBalance[i].Currency]
|
||||
if !ok {
|
||||
accounts[accountBalance[i].Currency] = coins
|
||||
} else {
|
||||
result.Available += accountBalance[i].Available
|
||||
result.OnHold += onHold
|
||||
accounts[accountBalance[i].Currency] = result
|
||||
}
|
||||
}
|
||||
|
||||
for x, y := range accounts {
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||
exchangeCurrency.TotalValue = y.Available + y.OnHold
|
||||
exchangeCurrency.Hold = y.OnHold
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package bitfinex
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func TestStart(t *testing.T) {
|
||||
@@ -19,7 +19,8 @@ func TestRun(t *testing.T) {
|
||||
|
||||
func TestGetTickerPrice(t *testing.T) {
|
||||
getTickerPrice := Bitfinex{}
|
||||
_, err := getTickerPrice.GetTickerPrice(pair.NewCurrencyPair("BTC", "USD"))
|
||||
_, err := getTickerPrice.GetTickerPrice(pair.NewCurrencyPair("BTC", "USD"),
|
||||
ticker.Spot)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex GetTickerPrice() error: %s", err)
|
||||
}
|
||||
@@ -27,23 +28,9 @@ func TestGetTickerPrice(t *testing.T) {
|
||||
|
||||
func TestGetOrderbookEx(t *testing.T) {
|
||||
getOrderBookEx := Bitfinex{}
|
||||
_, err := getOrderBookEx.GetOrderbookEx(pair.NewCurrencyPair("BTC", "USD"))
|
||||
_, err := getOrderBookEx.GetOrderbookEx(pair.NewCurrencyPair("BTC", "USD"),
|
||||
ticker.Spot)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex GetOrderbookEx() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExchangeAccountInfo(t *testing.T) {
|
||||
getExchangeAccountInfo := Bitfinex{}
|
||||
newConfig := config.GetConfig()
|
||||
newConfig.LoadConfig("../../testdata/configtest.dat")
|
||||
exchConf, err := newConfig.GetExchangeConfig("Bitfinex")
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex getExchangeConfig(): %s", err)
|
||||
}
|
||||
getExchangeAccountInfo.Setup(exchConf)
|
||||
_, err = getExchangeAccountInfo.GetExchangeAccountInfo()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bitfinex GetExchangeAccountInfo() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,50 +13,62 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
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"
|
||||
bitstampAPIURL = "https://www.bitstamp.net/api"
|
||||
bitstampAPIVersion = "2"
|
||||
bitstampAPITicker = "ticker"
|
||||
bitstampAPITickerHourly = "ticker_hour"
|
||||
bitstampAPIOrderbook = "order_book"
|
||||
bitstampAPITransactions = "transactions"
|
||||
bitstampAPIEURUSD = "eur_usd"
|
||||
bitstampAPIBalance = "balance"
|
||||
bitstampAPIUserTransactions = "user_transactions"
|
||||
bitstampAPIOpenOrders = "open_orders"
|
||||
bitstampAPIOrderStatus = "order_status"
|
||||
bitstampAPICancelOrder = "cancel_order"
|
||||
bitstampAPICancelAllOrders = "cancel_all_orders"
|
||||
bitstampAPIBuy = "buy"
|
||||
bitstampAPISell = "sell"
|
||||
bitstampAPIMarket = "market"
|
||||
bitstampAPIWithdrawalRequests = "withdrawal_requests"
|
||||
bitstampAPIBitcoinWithdrawal = "bitcoin_withdrawal"
|
||||
bitstampAPILTCWithdrawal = "ltc_withdrawal"
|
||||
bitstampAPIETHWithdrawal = "eth_withdrawal"
|
||||
bitstampAPIBitcoinDeposit = "bitcoin_deposit_address"
|
||||
bitstampAPILitecoinDeposit = "ltc_address"
|
||||
bitstampAPIEthereumDeposit = "eth_address"
|
||||
bitstampAPIUnconfirmedBitcoin = "unconfirmed_btc"
|
||||
bitstampAPITransferToMain = "transfer-to-main"
|
||||
bitstampAPITransferFromMain = "transfer-from-main"
|
||||
bitstampAPIXrpWithdrawal = "xrp_withdrawal"
|
||||
bitstampAPIXrpDeposit = "xrp_address"
|
||||
bitstampAPIReturnType = "string"
|
||||
)
|
||||
|
||||
// Bitstamp is the overarching type across the bitstamp package
|
||||
type Bitstamp struct {
|
||||
exchange.ExchangeBase
|
||||
Balance BitstampBalances
|
||||
exchange.Base
|
||||
Balance Balances
|
||||
}
|
||||
|
||||
// SetDefaults sets default for Bitstamp
|
||||
func (b *Bitstamp) SetDefaults() {
|
||||
b.Name = "Bitstamp"
|
||||
b.Enabled = false
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.RequestCurrencyPairFormat.Uppercase = true
|
||||
b.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Uppercase = true
|
||||
b.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
// Setup sets configuration values to bitstamp
|
||||
func (b *Bitstamp) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
@@ -70,11 +82,20 @@ func (b *Bitstamp) Setup(exch config.ExchangeConfig) {
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := b.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = b.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetFee(currency string) float64 {
|
||||
switch currency {
|
||||
// GetFee returns fee on a currency pair
|
||||
func (b *Bitstamp) GetFee(currencyPair string) float64 {
|
||||
switch currencyPair {
|
||||
case "BTCUSD":
|
||||
return b.Balance.BTCUSDFee
|
||||
case "BTCEUR":
|
||||
@@ -90,39 +111,50 @@ func (b *Bitstamp) GetFee(currency string) float64 {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetTicker(currency string, hourly bool) (BitstampTicker, error) {
|
||||
tickerEndpoint := BITSTAMP_API_TICKER
|
||||
// GetTicker returns ticker information
|
||||
func (b *Bitstamp) GetTicker(currency string, hourly bool) (Ticker, error) {
|
||||
response := Ticker{}
|
||||
tickerEndpoint := bitstampAPITicker
|
||||
|
||||
if hourly {
|
||||
tickerEndpoint = BITSTAMP_API_TICKER_HOURLY
|
||||
tickerEndpoint = bitstampAPITickerHourly
|
||||
}
|
||||
|
||||
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
|
||||
path := fmt.Sprintf(
|
||||
"%s/v%s/%s/%s/",
|
||||
bitstampAPIURL,
|
||||
bitstampAPIVersion,
|
||||
tickerEndpoint,
|
||||
common.StringToLower(currency),
|
||||
)
|
||||
return response, common.SendHTTPGetRequest(path, true, &response)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetOrderbook(currency string) (BitstampOrderbook, error) {
|
||||
// GetOrderbook Returns a JSON dictionary with "bids" and "asks". Each is a list
|
||||
// of open orders and each order is represented as a list holding the price and
|
||||
//the amount.
|
||||
func (b *Bitstamp) GetOrderbook(currency string) (Orderbook, error) {
|
||||
type response struct {
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Bids [][]string
|
||||
Asks [][]string
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Bids [][]string `json:"bids"`
|
||||
Asks [][]string `json:"asks"`
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
path := fmt.Sprintf("%s/v%s/%s/%s/", BITSTAMP_API_URL, BITSTAMP_API_VERSION, BITSTAMP_API_ORDERBOOK, common.StringToLower(currency))
|
||||
|
||||
path := fmt.Sprintf(
|
||||
"%s/v%s/%s/%s/",
|
||||
bitstampAPIURL,
|
||||
bitstampAPIVersion,
|
||||
bitstampAPIOrderbook,
|
||||
common.StringToLower(currency),
|
||||
)
|
||||
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
if err != nil {
|
||||
return BitstampOrderbook{}, err
|
||||
return Orderbook{}, err
|
||||
}
|
||||
|
||||
orderbook := BitstampOrderbook{}
|
||||
orderbook := Orderbook{}
|
||||
orderbook.Timestamp = resp.Timestamp
|
||||
|
||||
for _, x := range resp.Bids {
|
||||
@@ -136,7 +168,7 @@ func (b *Bitstamp) GetOrderbook(currency string) (BitstampOrderbook, error) {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Bids = append(orderbook.Bids, BitstampOrderbookBase{price, amount})
|
||||
orderbook.Bids = append(orderbook.Bids, OrderbookBase{price, amount})
|
||||
}
|
||||
|
||||
for _, x := range resp.Asks {
|
||||
@@ -150,44 +182,49 @@ func (b *Bitstamp) GetOrderbook(currency string) (BitstampOrderbook, error) {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
orderbook.Asks = append(orderbook.Asks, BitstampOrderbookBase{price, amount})
|
||||
orderbook.Asks = append(orderbook.Asks, OrderbookBase{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
|
||||
// GetTransactions returns transaction information
|
||||
// value paramater ["time"] = "minute", "hour", "day" will collate your
|
||||
// response into time intervals. Implementation of value in test code.
|
||||
func (b *Bitstamp) GetTransactions(currencyPair string, values url.Values) ([]Transactions, error) {
|
||||
transactions := []Transactions{}
|
||||
path := common.EncodeURLValues(
|
||||
fmt.Sprintf(
|
||||
"%s/v%s/%s/%s/",
|
||||
bitstampAPIURL,
|
||||
bitstampAPIVersion,
|
||||
bitstampAPITransactions,
|
||||
common.StringToLower(currencyPair),
|
||||
),
|
||||
values,
|
||||
)
|
||||
|
||||
return transactions, common.SendHTTPGetRequest(path, true, &transactions)
|
||||
}
|
||||
|
||||
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)
|
||||
// GetEURUSDConversionRate returns the conversion rate between Euro and USD
|
||||
func (b *Bitstamp) GetEURUSDConversionRate() (EURUSDConversionRate, error) {
|
||||
rate := EURUSDConversionRate{}
|
||||
path := fmt.Sprintf("%s/%s", bitstampAPIURL, bitstampAPIEURUSD)
|
||||
|
||||
if err != nil {
|
||||
return rate, err
|
||||
}
|
||||
return rate, nil
|
||||
return rate, common.SendHTTPGetRequest(path, true, &rate)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetBalance() (BitstampBalances, error) {
|
||||
balance := BitstampBalances{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BALANCE, true, url.Values{}, &balance)
|
||||
// GetBalance returns full balance of currency held on the exchange
|
||||
func (b *Bitstamp) GetBalance() (Balances, error) {
|
||||
balance := Balances{}
|
||||
|
||||
if err != nil {
|
||||
return balance, err
|
||||
}
|
||||
return balance, nil
|
||||
return balance,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIBalance, true, url.Values{}, &balance)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetUserTransactions(values url.Values) ([]BitstampUserTransactions, error) {
|
||||
// GetUserTransactions returns an array of transactions
|
||||
func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions, error) {
|
||||
type Response struct {
|
||||
Date string `json:"datetime"`
|
||||
TransID int64 `json:"id"`
|
||||
@@ -200,25 +237,29 @@ func (b *Bitstamp) GetUserTransactions(values url.Values) ([]BitstampUserTransac
|
||||
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
|
||||
if currencyPair != "" {
|
||||
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions, true, url.Values{}, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions+"/"+currencyPair, true, url.Values{}, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
transactions := []BitstampUserTransactions{}
|
||||
transactions := []UserTransactions{}
|
||||
|
||||
for _, y := range response {
|
||||
tx := BitstampUserTransactions{}
|
||||
tx := UserTransactions{}
|
||||
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" {
|
||||
if varType == bitstampAPIReturnType {
|
||||
tx.USD, _ = strconv.ParseFloat(y.USD.(string), 64)
|
||||
} else {
|
||||
tx.USD = y.USD.(float64)
|
||||
@@ -228,14 +269,14 @@ func (b *Bitstamp) GetUserTransactions(values url.Values) ([]BitstampUserTransac
|
||||
tx.XRP = y.XRP
|
||||
|
||||
varType = reflect.TypeOf(y.BTC).String()
|
||||
if varType == "string" {
|
||||
if varType == bitstampAPIReturnType {
|
||||
tx.BTC, _ = strconv.ParseFloat(y.BTC.(string), 64)
|
||||
} else {
|
||||
tx.BTC = y.BTC.(float64)
|
||||
}
|
||||
|
||||
varType = reflect.TypeOf(y.BTCUSD).String()
|
||||
if varType == "string" {
|
||||
if varType == bitstampAPIReturnType {
|
||||
tx.BTCUSD, _ = strconv.ParseFloat(y.BTCUSD.(string), 64)
|
||||
} else {
|
||||
tx.BTCUSD = y.BTCUSD.(float64)
|
||||
@@ -249,184 +290,179 @@ func (b *Bitstamp) GetUserTransactions(values url.Values) ([]BitstampUserTransac
|
||||
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)
|
||||
// GetOpenOrders returns all open orders on the exchange
|
||||
func (b *Bitstamp) GetOpenOrders(currencyPair string) ([]Order, error) {
|
||||
resp := []Order{}
|
||||
path := fmt.Sprintf(
|
||||
"%s/%s", bitstampAPIOpenOrders, common.StringToLower(currencyPair),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
return resp, b.SendAuthenticatedHTTPRequest(path, true, nil, &resp)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetOrderStatus(OrderID int64) (BitstampOrderStatus, error) {
|
||||
var req = url.Values{}
|
||||
// GetOrderStatus returns an the status of an order by its ID
|
||||
func (b *Bitstamp) GetOrderStatus(OrderID int64) (OrderStatus, error) {
|
||||
resp := OrderStatus{}
|
||||
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
|
||||
return resp,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIOrderStatus, false, req, &resp)
|
||||
}
|
||||
|
||||
// CancelOrder cancels order by ID
|
||||
func (b *Bitstamp) CancelOrder(OrderID int64) (bool, error) {
|
||||
var req = url.Values{}
|
||||
result := false
|
||||
var req = url.Values{}
|
||||
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
|
||||
return result,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPICancelOrder, true, req, &result)
|
||||
}
|
||||
|
||||
// CancelAllOrders cancels all open orders on the exchange
|
||||
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
|
||||
return result,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPICancelAllOrders, false, nil, &result)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) PlaceOrder(currency string, price float64, amount float64, buy, market bool) (BitstampOrder, error) {
|
||||
// PlaceOrder places an order on the exchange.
|
||||
func (b *Bitstamp) PlaceOrder(currencyPair string, price float64, amount float64, buy, market bool) (Order, 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 := ""
|
||||
response := Order{}
|
||||
orderType := bitstampAPIBuy
|
||||
|
||||
if !buy {
|
||||
orderType = BITSTAMP_API_SELL
|
||||
orderType = bitstampAPISell
|
||||
}
|
||||
|
||||
path = fmt.Sprintf("%s/%s", orderType, common.StringToLower(currency))
|
||||
path := fmt.Sprintf("%s/%s", orderType, common.StringToLower(currencyPair))
|
||||
|
||||
if market {
|
||||
path = fmt.Sprintf("%s/%s/%s", orderType, BITSTAMP_API_MARKET, common.StringToLower(currency))
|
||||
path = fmt.Sprintf("%s/%s/%s", orderType, bitstampAPIMarket, common.StringToLower(currencyPair))
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(path, true, req, &response)
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
return response,
|
||||
b.SendAuthenticatedHTTPRequest(path, true, req, &response)
|
||||
}
|
||||
|
||||
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
|
||||
// GetWithdrawalRequests returns withdrawl requests for the account
|
||||
// timedelta - positive integer with max value 50000000 which returns requests
|
||||
// from number of seconds ago to now.
|
||||
func (b *Bitstamp) GetWithdrawalRequests(timedelta int64) ([]WithdrawalRequests, error) {
|
||||
resp := []WithdrawalRequests{}
|
||||
if timedelta > 50000000 || timedelta < 0 {
|
||||
return resp, errors.New("time delta exceeded, max: 50000000 min: 0")
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
value := url.Values{}
|
||||
value.Set("timedelta", strconv.FormatInt(timedelta, 10))
|
||||
|
||||
if timedelta == 0 {
|
||||
value = url.Values{}
|
||||
}
|
||||
|
||||
return resp,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIWithdrawalRequests, false, value, &resp)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) BitcoinWithdrawal(amount float64, address string, instant bool) (string, error) {
|
||||
// CryptoWithdrawal withdraws a cryptocurrency into a supplied wallet, returns ID
|
||||
// amount - The amount you want withdrawn
|
||||
// address - The wallet address of the cryptocurrency
|
||||
// symbol - the type of crypto ie "ltc", "btc", "eth"
|
||||
// destTag - only for XRP default to ""
|
||||
// instant - only for bitcoins
|
||||
func (b *Bitstamp) CryptoWithdrawal(amount float64, address, symbol, destTag 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
|
||||
switch common.StringToLower(symbol) {
|
||||
case "btc":
|
||||
if instant {
|
||||
req.Add("instant", "1")
|
||||
} else {
|
||||
req.Add("instant", "0")
|
||||
}
|
||||
return resp.ID,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIBitcoinWithdrawal, false, req, &resp)
|
||||
case "ltc":
|
||||
return resp.ID,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPILTCWithdrawal, true, req, &resp)
|
||||
case "eth":
|
||||
return resp.ID,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIETHWithdrawal, true, req, &resp)
|
||||
case "xrp":
|
||||
if destTag != "" {
|
||||
req.Add("destination_tag", destTag)
|
||||
}
|
||||
return resp.ID,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIXrpWithdrawal, true, req, &resp)
|
||||
}
|
||||
|
||||
return resp.ID, nil
|
||||
return resp.ID,
|
||||
errors.New("incorrect symbol")
|
||||
}
|
||||
|
||||
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) {
|
||||
// GetCryptoDepositAddress returns a depositing address by crypto
|
||||
// crypto - example "btc", "ltc", "eth", or "xrp"
|
||||
func (b *Bitstamp) GetCryptoDepositAddress(crypto string) (string, error) {
|
||||
type response struct {
|
||||
Address string
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
resp := response{}
|
||||
err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_DESPOIT, false, nil, &resp)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
switch common.StringToLower(crypto) {
|
||||
case "btc":
|
||||
return resp.Address,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIBitcoinDeposit, false, nil, &resp.Address)
|
||||
case "ltc":
|
||||
return resp.Address,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPILitecoinDeposit, true, nil, &resp)
|
||||
case "eth":
|
||||
return resp.Address,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIEthereumDeposit, true, nil, &resp)
|
||||
case "xrp":
|
||||
return resp.Address,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIXrpDeposit, true, nil, &resp)
|
||||
}
|
||||
|
||||
return resp.Address, nil
|
||||
return resp.Address, errors.New("incorrect cryptocurrency string")
|
||||
}
|
||||
|
||||
// GetUnconfirmedBitcoinDeposits returns unconfirmed transactions
|
||||
func (b *Bitstamp) GetUnconfirmedBitcoinDeposits() ([]UnconfirmedBTCTransactions, error) {
|
||||
response := []UnconfirmedBTCTransactions{}
|
||||
|
||||
return response,
|
||||
b.SendAuthenticatedHTTPRequest(bitstampAPIUnconfirmedBitcoin, false, nil, &response)
|
||||
}
|
||||
|
||||
// TransferAccountBalance transfers funds from either a main or sub account
|
||||
// amount - to transfers
|
||||
// currency - which currency to transfer
|
||||
// subaccount - name of account
|
||||
// toMain - bool either to or from account
|
||||
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
|
||||
path := bitstampAPITransferToMain
|
||||
if !toMain {
|
||||
path = BITSTAMP_API_TRANSFER_FROM_MAIN
|
||||
path = bitstampAPITransferFromMain
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(path, true, req, nil)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -434,55 +470,31 @@ func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount s
|
||||
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
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an authenticated request
|
||||
func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url.Values, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
if !b.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
||||
}
|
||||
|
||||
if b.Nonce.Get() == 0 {
|
||||
b.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
b.Nonce.Inc()
|
||||
}
|
||||
|
||||
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("nonce", b.Nonce.String())
|
||||
hmac := common.GetHMAC(common.HashSHA256, []byte(b.Nonce.String()+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)
|
||||
path = fmt.Sprintf("%s/v%s/%s/", bitstampAPIURL, bitstampAPIVersion, path)
|
||||
} else {
|
||||
path = fmt.Sprintf("%s/%s/", BITSTAMP_API_URL, path)
|
||||
path = fmt.Sprintf("%s/%s/", bitstampAPIURL, path)
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
@@ -498,14 +510,20 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
log.Printf("Received raw: %s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
/* inconsistent errors, needs to be improved when in production*/
|
||||
if common.StringContains(resp, "500 error") {
|
||||
return errors.New("internal server: code 500")
|
||||
}
|
||||
|
||||
return nil
|
||||
capture := CaptureError{}
|
||||
if err = common.JSONDecode([]byte(resp), &capture); err == nil {
|
||||
if capture.Code != nil || capture.Error != nil || capture.Reason != nil || capture.Status != nil {
|
||||
errstring := fmt.Sprint("Status: ", capture.Status, ", Issue: ", capture.Error, ", Reason: ", capture.Reason, ", Code: ", capture.Code)
|
||||
return errors.New(errstring)
|
||||
}
|
||||
}
|
||||
return common.JSONDecode([]byte(resp), &result)
|
||||
}
|
||||
|
||||
358
exchanges/bitstamp/bitstamp_test.go
Normal file
358
exchanges/bitstamp/bitstamp_test.go
Normal file
@@ -0,0 +1,358 @@
|
||||
package bitstamp
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
// Please add your private keys and customerID for better tests
|
||||
const (
|
||||
apiKey = ""
|
||||
apiSecret = ""
|
||||
customerID = ""
|
||||
)
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.SetDefaults()
|
||||
|
||||
if b.Name != "Bitstamp" {
|
||||
t.Error("Test Failed - SetDefaults() error")
|
||||
}
|
||||
if b.Enabled != false {
|
||||
t.Error("Test Failed - SetDefaults() error")
|
||||
}
|
||||
if b.Verbose != false {
|
||||
t.Error("Test Failed - SetDefaults() error")
|
||||
}
|
||||
if b.Websocket != false {
|
||||
t.Error("Test Failed - SetDefaults() error")
|
||||
}
|
||||
if b.RESTPollingDelay != 10 {
|
||||
t.Error("Test Failed - SetDefaults() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.Name = "Bitstamp"
|
||||
cfg := config.GetConfig()
|
||||
cfg.LoadConfig("../../testdata/configtest.dat")
|
||||
bConfig, err := cfg.GetExchangeConfig("Bitstamp")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - Bitstamp Setup() init error")
|
||||
}
|
||||
|
||||
b.SetDefaults()
|
||||
b.Setup(bConfig)
|
||||
|
||||
if !b.IsEnabled() || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) ||
|
||||
b.Verbose || b.Websocket || len(b.BaseCurrencies) < 1 ||
|
||||
len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 {
|
||||
t.Error("Test Failed - Bitstamp Setup values not set correctly")
|
||||
}
|
||||
|
||||
bConfig.Enabled = false
|
||||
b.Setup(bConfig)
|
||||
|
||||
if b.IsEnabled() {
|
||||
t.Error("Test failed - Bitstamp TestSetup incorrect value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFee(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
if resp := b.GetFee("BTCUSD"); resp != 0 {
|
||||
t.Error("Test Failed - GetFee() error")
|
||||
}
|
||||
if resp := b.GetFee("BTCEUR"); resp != 0 {
|
||||
t.Error("Test Failed - GetFee() error")
|
||||
}
|
||||
if resp := b.GetFee("XRPEUR"); resp != 0 {
|
||||
t.Error("Test Failed - GetFee() error")
|
||||
}
|
||||
if resp := b.GetFee("XRPUSD"); resp != 0 {
|
||||
t.Error("Test Failed - GetFee() error")
|
||||
}
|
||||
if resp := b.GetFee("EURUSD"); resp != 0 {
|
||||
t.Error("Test Failed - GetFee() error")
|
||||
}
|
||||
if resp := b.GetFee("bla"); resp != 0 {
|
||||
t.Error("Test Failed - GetFee() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
_, err := b.GetTicker("BTCUSD", false)
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetTicker() error", err)
|
||||
}
|
||||
_, err = b.GetTicker("BTCUSD", true)
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetTicker() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
_, err := b.GetOrderbook("BTCUSD")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetOrderbook() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTransactions(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
|
||||
value := url.Values{}
|
||||
value.Set("time", "hour")
|
||||
|
||||
_, err := b.GetTransactions("BTCUSD", value)
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetTransactions() error", err)
|
||||
}
|
||||
_, err = b.GetTransactions("wigwham", value)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetTransactions() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEURUSDConversionRate(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
_, err := b.GetEURUSDConversionRate()
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetEURUSDConversionRate() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBalance(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.GetBalance()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetBalance() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserTransactions(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.GetUserTransactions("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserTransactions() error", err)
|
||||
}
|
||||
|
||||
_, err = b.GetUserTransactions("btcusd")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUserTransactions() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.GetOpenOrders("btcusd")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetOpenOrders() error", err)
|
||||
}
|
||||
_, err = b.GetOpenOrders("wigwham")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetOpenOrders() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.GetOrderStatus(1337)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetOpenOrders() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
resp, err := b.CancelOrder(1337)
|
||||
if err == nil || resp != false {
|
||||
t.Error("Test Failed - CancelOrder() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelAllOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.CancelAllOrders()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CancelAllOrders() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaceOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.PlaceOrder("btcusd", 0.01, 1, true, true)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - PlaceOrder() error")
|
||||
}
|
||||
_, err = b.PlaceOrder("btcusd", 0.01, 1, true, false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - PlaceOrder() error")
|
||||
}
|
||||
_, err = b.PlaceOrder("btcusd", 0.01, 1, false, false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - PlaceOrder() error")
|
||||
}
|
||||
_, err = b.PlaceOrder("wigwham", 0.01, 1, false, false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - PlaceOrder() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWithdrawalRequests(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.GetWithdrawalRequests(0)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetWithdrawalRequests() error", err)
|
||||
}
|
||||
_, err = b.GetWithdrawalRequests(-1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetWithdrawalRequests() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCryptoWithdrawal(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.CryptoWithdrawal(0, "bla", "btc", "", true)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CryptoWithdrawal() error", err)
|
||||
}
|
||||
_, err = b.CryptoWithdrawal(0, "bla", "btc", "", false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CryptoWithdrawal() error", err)
|
||||
}
|
||||
_, err = b.CryptoWithdrawal(0, "bla", "ltc", "", false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CryptoWithdrawal() error", err)
|
||||
}
|
||||
_, err = b.CryptoWithdrawal(0, "bla", "eth", "", false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CryptoWithdrawal() error", err)
|
||||
}
|
||||
_, err = b.CryptoWithdrawal(0, "bla", "xrp", "someplace", false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CryptoWithdrawal() error", err)
|
||||
}
|
||||
_, err = b.CryptoWithdrawal(0, "bla", "ding!", "", false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CryptoWithdrawal() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBitcoinDepositAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.GetCryptoDepositAddress("btc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
|
||||
}
|
||||
_, err = b.GetCryptoDepositAddress("LTc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
|
||||
}
|
||||
_, err = b.GetCryptoDepositAddress("eth")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
|
||||
}
|
||||
_, err = b.GetCryptoDepositAddress("xrp")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
|
||||
}
|
||||
_, err = b.GetCryptoDepositAddress("wigwham")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetCryptoDepositAddress() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUnconfirmedBitcoinDeposits(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.GetUnconfirmedBitcoinDeposits()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetUnconfirmedBitcoinDeposits() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransferAccountBalance(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bitstamp{}
|
||||
b.APIKey = apiKey
|
||||
b.APISecret = apiSecret
|
||||
b.ClientID = customerID
|
||||
|
||||
_, err := b.TransferAccountBalance(1, "", "", true)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - TransferAccountBalance() error", err)
|
||||
}
|
||||
_, err = b.TransferAccountBalance(1, "btc", "", false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - TransferAccountBalance() error", err)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package bitstamp
|
||||
|
||||
type BitstampTicker struct {
|
||||
// Ticker holds ticker information
|
||||
type Ticker struct {
|
||||
Last float64 `json:"last,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
@@ -12,38 +13,21 @@ type BitstampTicker struct {
|
||||
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 {
|
||||
// OrderbookBase holds singular price information
|
||||
type OrderbookBase struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BitstampOrderbook struct {
|
||||
// Orderbook holds orderbook information
|
||||
type Orderbook struct {
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Bids []BitstampOrderbookBase
|
||||
Asks []BitstampOrderbookBase
|
||||
Bids []OrderbookBase
|
||||
Asks []OrderbookBase
|
||||
}
|
||||
|
||||
type BitstampTransactions struct {
|
||||
// Transactions holds transaction data
|
||||
type Transactions struct {
|
||||
Date int64 `json:"date,string"`
|
||||
TradeID int64 `json:"tid,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
@@ -51,12 +35,37 @@ type BitstampTransactions struct {
|
||||
Amount float64 `json:"amount,string"`
|
||||
}
|
||||
|
||||
type BitstampEURUSDConversionRate struct {
|
||||
// EURUSDConversionRate holds buy sell conversion rate information
|
||||
type EURUSDConversionRate struct {
|
||||
Buy float64 `json:"buy,string"`
|
||||
Sell float64 `json:"sell,string"`
|
||||
}
|
||||
|
||||
type BitstampUserTransactions struct {
|
||||
// Balances holds full balance information with the supplied APIKEYS
|
||||
type Balances struct {
|
||||
USDBalance float64 `json:"usd_balance,string"`
|
||||
BTCBalance float64 `json:"btc_balance,string"`
|
||||
EURBalance float64 `json:"eur_balance,string"`
|
||||
XRPBalance float64 `json:"xrp_balance,string"`
|
||||
USDReserved float64 `json:"usd_reserved,string"`
|
||||
BTCReserved float64 `json:"btc_reserved,string"`
|
||||
EURReserved float64 `json:"eur_reserved,string"`
|
||||
XRPReserved float64 `json:"xrp_reserved,string"`
|
||||
USDAvailable float64 `json:"usd_available,string"`
|
||||
BTCAvailable float64 `json:"btc_available,string"`
|
||||
EURAvailable float64 `json:"eur_available,string"`
|
||||
XRPAvailable float64 `json:"xrp_available,string"`
|
||||
BTCUSDFee float64 `json:"btcusd_fee,string"`
|
||||
BTCEURFee float64 `json:"btceur_fee,string"`
|
||||
EURUSDFee float64 `json:"eurusd_fee,string"`
|
||||
XRPUSDFee float64 `json:"xrpusd_fee,string"`
|
||||
XRPEURFee float64 `json:"xrpeur_fee,string"`
|
||||
XRPBTCFee float64 `json:"xrpbtc_fee,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
}
|
||||
|
||||
// UserTransactions holds user transaction information
|
||||
type UserTransactions struct {
|
||||
Date string `json:"datetime"`
|
||||
TransID int64 `json:"id"`
|
||||
Type int `json:"type,string"`
|
||||
@@ -69,7 +78,8 @@ type BitstampUserTransactions struct {
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BitstampOrder struct {
|
||||
// Order holds current open order data
|
||||
type Order struct {
|
||||
ID int64 `json:"id"`
|
||||
Date string `json:"datetime"`
|
||||
Type int `json:"type"`
|
||||
@@ -77,7 +87,8 @@ type BitstampOrder struct {
|
||||
Amount float64 `json:"amount"`
|
||||
}
|
||||
|
||||
type BitstampOrderStatus struct {
|
||||
// OrderStatus holds order status information
|
||||
type OrderStatus struct {
|
||||
Status string
|
||||
Transactions []struct {
|
||||
TradeID int64 `json:"tid"`
|
||||
@@ -88,22 +99,30 @@ type BitstampOrderStatus struct {
|
||||
}
|
||||
}
|
||||
|
||||
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{}
|
||||
// WithdrawalRequests holds request information on withdrawals
|
||||
type WithdrawalRequests struct {
|
||||
OrderID int64 `json:"id"`
|
||||
Date string `json:"datetime"`
|
||||
Type int `json:"type"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Status int `json:"status"`
|
||||
Data interface{}
|
||||
Address string `json:"address"` // Bitcoin withdrawals only
|
||||
TransactionID string `json:"transaction_id"` // Bitcoin withdrawals only
|
||||
}
|
||||
|
||||
type BitstampUnconfirmedBTCTransactions struct {
|
||||
// UnconfirmedBTCTransactions holds address information about unconfirmed
|
||||
// transactions
|
||||
type UnconfirmedBTCTransactions 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"`
|
||||
// CaptureError is used to capture unmarshalled errors
|
||||
type CaptureError struct {
|
||||
Status interface{} `json:"status"`
|
||||
Reason interface{} `json:"reason"`
|
||||
Code interface{} `json:"code"`
|
||||
Error interface{} `json:"error"`
|
||||
}
|
||||
|
||||
@@ -7,23 +7,28 @@ import (
|
||||
"github.com/toorop/go-pusher"
|
||||
)
|
||||
|
||||
type BitstampPusherOrderbook struct {
|
||||
// PusherOrderbook holds order book information to be pushed
|
||||
type PusherOrderbook struct {
|
||||
Asks [][]string `json:"asks"`
|
||||
Bids [][]string `json:"bids"`
|
||||
}
|
||||
type BitstampPusherTrade struct {
|
||||
|
||||
// PusherTrade holds trade information to be pushed
|
||||
type PusherTrade struct {
|
||||
Price float64 `json:"price"`
|
||||
Amount float64 `json:"amount"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
const (
|
||||
BITSTAMP_PUSHER_KEY = "de504dc5763aeef9ff52"
|
||||
// BitstampPusherKey holds the current pusher key
|
||||
BitstampPusherKey = "de504dc5763aeef9ff52"
|
||||
)
|
||||
|
||||
// PusherClient starts the push mechanism
|
||||
func (b *Bitstamp) PusherClient() {
|
||||
for b.Enabled && b.Websocket {
|
||||
pusherClient, err := pusher.NewClient(BITSTAMP_PUSHER_KEY)
|
||||
pusherClient, err := pusher.NewClient(BitstampPusherKey)
|
||||
if err != nil {
|
||||
log.Printf("%s Unable to connect to Websocket. Error: %s\n", b.GetName(), err)
|
||||
continue
|
||||
@@ -55,13 +60,13 @@ func (b *Bitstamp) PusherClient() {
|
||||
for b.Websocket {
|
||||
select {
|
||||
case data := <-dataChannelTrade:
|
||||
result := BitstampPusherOrderbook{}
|
||||
result := PusherOrderbook{}
|
||||
err := common.JSONDecode([]byte(data.Data), &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
case trade := <-tradeChannelTrade:
|
||||
result := BitstampPusherTrade{}
|
||||
result := PusherTrade{}
|
||||
err := common.JSONDecode([]byte(trade.Data), &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
@@ -2,20 +2,20 @@ package bitstamp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Bitstamp go routine
|
||||
func (b *Bitstamp) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
// Run implements the Bitstamp wrapper
|
||||
func (b *Bitstamp) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
@@ -26,31 +26,11 @@ func (b *Bitstamp) Run() {
|
||||
if b.Websocket {
|
||||
go b.PusherClient()
|
||||
}
|
||||
|
||||
for b.Enabled {
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
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.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(b.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (b *Bitstamp) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := b.GetTicker(p.Pair().String(), false)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
@@ -63,65 +43,79 @@ func (b *Bitstamp) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, erro
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Volume
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (b *Bitstamp) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tick, err := ticker.GetTicker(b.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return b.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (b *Bitstamp) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return b.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (b *Bitstamp) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := b.GetOrderbook(p.Pair().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// Bitstamp exchange
|
||||
func (b *Bitstamp) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = b.GetName()
|
||||
accountBalance, err := b.GetBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "BTC",
|
||||
TotalValue: accountBalance.BTCAvailable,
|
||||
Hold: accountBalance.BTCReserved,
|
||||
})
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "XRP",
|
||||
TotalValue: accountBalance.XRPAvailable,
|
||||
Hold: accountBalance.XRPReserved,
|
||||
})
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "USD",
|
||||
TotalValue: accountBalance.USDAvailable,
|
||||
Hold: accountBalance.USDReserved,
|
||||
})
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "EUR",
|
||||
TotalValue: accountBalance.EURAvailable,
|
||||
Hold: accountBalance.EURReserved,
|
||||
|
||||
384
exchanges/bittrex/bittrex.go
Normal file
384
exchanges/bittrex/bittrex.go
Normal file
@@ -0,0 +1,384 @@
|
||||
package bittrex
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
bittrexAPIURL = "https://bittrex.com/api/v1.1"
|
||||
bittrexAPIVersion = "v1.1"
|
||||
bittrexMaxOpenOrders = 500
|
||||
bittrexMaxOrderCountPerDay = 200000
|
||||
|
||||
// Returned messages from Bittrex API
|
||||
bittrexAddressGenerating = "ADDRESS_GENERATING"
|
||||
bittrexErrorMarketNotProvided = "MARKET_NOT_PROVIDED"
|
||||
bittrexErrorInvalidMarket = "INVALID_MARKET"
|
||||
bittrexErrorAPIKeyInvalid = "APIKEY_INVALID"
|
||||
bittrexErrorInvalidPermission = "INVALID_PERMISSION"
|
||||
|
||||
// Public requests
|
||||
bittrexAPIGetMarkets = "public/getmarkets"
|
||||
bittrexAPIGetCurrencies = "public/getcurrencies"
|
||||
bittrexAPIGetTicker = "public/getticker"
|
||||
bittrexAPIGetMarketSummaries = "public/getmarketsummaries"
|
||||
bittrexAPIGetMarketSummary = "public/getmarketsummary"
|
||||
bittrexAPIGetOrderbook = "public/getorderbook"
|
||||
bittrexAPIGetMarketHistory = "public/getmarkethistory"
|
||||
|
||||
// Market requests
|
||||
bittrexAPIBuyLimit = "market/buylimit"
|
||||
bittrexAPISellLimit = "market/selllimit"
|
||||
bittrexAPICancel = "market/cancel"
|
||||
bittrexAPIGetOpenOrders = "market/getopenorders"
|
||||
|
||||
// Account requests
|
||||
bittrexAPIGetBalances = "account/getbalances"
|
||||
bittrexAPIGetBalance = "account/getbalance"
|
||||
bittrexAPIGetDepositAddress = "account/getdepositaddress"
|
||||
bittrexAPIWithdraw = "account/withdraw"
|
||||
bittrexAPIGetOrder = "account/getorder"
|
||||
bittrexAPIGetOrderHistory = "account/getorderhistory"
|
||||
bittrexAPIGetWithdrawalHistory = "account/getwithdrawalhistory"
|
||||
bittrexAPIGetDepositHistory = "account/getdeposithistory"
|
||||
)
|
||||
|
||||
// Bittrex is the overaching type across the bittrex methods
|
||||
type Bittrex struct {
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
// SetDefaults method assignes the default values for Bittrex
|
||||
func (b *Bittrex) SetDefaults() {
|
||||
b.Name = "Bittrex"
|
||||
b.Enabled = false
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.RequestCurrencyPairFormat.Delimiter = "-"
|
||||
b.RequestCurrencyPairFormat.Uppercase = true
|
||||
b.ConfigCurrencyPairFormat.Delimiter = "-"
|
||||
b.ConfigCurrencyPairFormat.Uppercase = true
|
||||
b.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
// Setup method sets current configuration details if enabled
|
||||
func (b *Bittrex) 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, ",")
|
||||
err := b.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = b.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetMarkets is used to get the open and available trading markets at Bittrex
|
||||
// along with other meta data.
|
||||
func (b *Bittrex) GetMarkets() ([]Market, error) {
|
||||
var markets []Market
|
||||
path := fmt.Sprintf("%s/%s/", bittrexAPIURL, bittrexAPIGetMarkets)
|
||||
|
||||
return markets, b.HTTPRequest(path, false, url.Values{}, &markets)
|
||||
}
|
||||
|
||||
// GetCurrencies is used to get all supported currencies at Bittrex
|
||||
func (b *Bittrex) GetCurrencies() ([]Currency, error) {
|
||||
var currencies []Currency
|
||||
path := fmt.Sprintf("%s/%s/", bittrexAPIURL, bittrexAPIGetCurrencies)
|
||||
|
||||
return currencies, b.HTTPRequest(path, false, url.Values{}, ¤cies)
|
||||
}
|
||||
|
||||
// GetTicker sends a public get request and returns current ticker information
|
||||
// on the supplied currency. Example currency input param "btc-ltc".
|
||||
func (b *Bittrex) GetTicker(currencyPair string) (Ticker, error) {
|
||||
ticker := Ticker{}
|
||||
path := fmt.Sprintf("%s/%s?market=%s", bittrexAPIURL, bittrexAPIGetTicker,
|
||||
common.StringToUpper(currencyPair),
|
||||
)
|
||||
return ticker, b.HTTPRequest(path, false, url.Values{}, &ticker)
|
||||
}
|
||||
|
||||
// GetMarketSummaries is used to get the last 24 hour summary of all active
|
||||
// exchanges
|
||||
func (b *Bittrex) GetMarketSummaries() ([]MarketSummary, error) {
|
||||
var summaries []MarketSummary
|
||||
path := fmt.Sprintf("%s/%s/", bittrexAPIURL, bittrexAPIGetMarketSummaries)
|
||||
|
||||
return summaries, b.HTTPRequest(path, false, url.Values{}, &summaries)
|
||||
}
|
||||
|
||||
// GetMarketSummary is used to get the last 24 hour summary of all active
|
||||
// exchanges by currency pair (btc-ltc).
|
||||
func (b *Bittrex) GetMarketSummary(currencyPair string) ([]MarketSummary, error) {
|
||||
var summary []MarketSummary
|
||||
path := fmt.Sprintf("%s/%s?market=%s", bittrexAPIURL,
|
||||
bittrexAPIGetMarketSummary, common.StringToLower(currencyPair),
|
||||
)
|
||||
return summary, b.HTTPRequest(path, false, url.Values{}, &summary)
|
||||
}
|
||||
|
||||
// GetOrderbook method returns current order book information by currency, type
|
||||
// & depth.
|
||||
// "Currency Pair" ie btc-ltc
|
||||
// "Category" either "buy", "sell" or "both"; for ease of use and reduced
|
||||
// complexity this function is set to "both"
|
||||
// "Depth" max depth is 50 but you can literally set it any integer you want and
|
||||
// it returns full depth. So depth default is 50.
|
||||
func (b *Bittrex) GetOrderbook(currencyPair string) (OrderBooks, error) {
|
||||
var orderbooks OrderBooks
|
||||
path := fmt.Sprintf("%s/%s?market=%s&type=both&depth=50", bittrexAPIURL,
|
||||
bittrexAPIGetOrderbook, common.StringToUpper(currencyPair),
|
||||
)
|
||||
|
||||
return orderbooks, b.HTTPRequest(path, false, url.Values{}, &orderbooks)
|
||||
}
|
||||
|
||||
// GetMarketHistory retrieves the latest trades that have occurred for a specific
|
||||
// market
|
||||
func (b *Bittrex) GetMarketHistory(currencyPair string) ([]MarketHistory, error) {
|
||||
var marketHistoriae []MarketHistory
|
||||
path := fmt.Sprintf("%s/%s?market=%s", bittrexAPIURL,
|
||||
bittrexAPIGetMarketHistory, common.StringToUpper(currencyPair),
|
||||
)
|
||||
return marketHistoriae, b.HTTPRequest(path, false, url.Values{},
|
||||
&marketHistoriae)
|
||||
}
|
||||
|
||||
// PlaceBuyLimit is used to place a buy order in a specific market. Use buylimit
|
||||
// to place limit orders. Make sure you have the proper permissions set on your
|
||||
// API keys for this call to work.
|
||||
// "Currency" ie "btc-ltc"
|
||||
// "Quantity" is the amount to purchase
|
||||
// "Rate" is the rate at which to purchase
|
||||
func (b *Bittrex) PlaceBuyLimit(currencyPair string, quantity, rate float64) ([]UUID, error) {
|
||||
var id []UUID
|
||||
values := url.Values{}
|
||||
values.Set("market", currencyPair)
|
||||
values.Set("quantity", strconv.FormatFloat(quantity, 'E', -1, 64))
|
||||
values.Set("rate", strconv.FormatFloat(rate, 'E', -1, 64))
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIBuyLimit)
|
||||
|
||||
return id, b.HTTPRequest(path, true, values, &id)
|
||||
}
|
||||
|
||||
// PlaceSellLimit is used to place a sell order in a specific market. Use
|
||||
// selllimit to place limit orders. Make sure you have the proper permissions
|
||||
// set on your API keys for this call to work.
|
||||
// "Currency" ie "btc-ltc"
|
||||
// "Quantity" is the amount to purchase
|
||||
// "Rate" is the rate at which to purchase
|
||||
func (b *Bittrex) PlaceSellLimit(currencyPair string, quantity, rate float64) ([]UUID, error) {
|
||||
var id []UUID
|
||||
values := url.Values{}
|
||||
values.Set("market", currencyPair)
|
||||
values.Set("quantity", strconv.FormatFloat(quantity, 'E', -1, 64))
|
||||
values.Set("rate", strconv.FormatFloat(rate, 'E', -1, 64))
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPISellLimit)
|
||||
|
||||
return id, b.HTTPRequest(path, true, values, &id)
|
||||
}
|
||||
|
||||
// GetOpenOrders returns all orders that you currently have opened.
|
||||
// A specific market can be requested for example "btc-ltc"
|
||||
func (b *Bittrex) GetOpenOrders(currencyPair string) ([]Order, error) {
|
||||
var orders []Order
|
||||
values := url.Values{}
|
||||
if !(currencyPair == "" || currencyPair == " ") {
|
||||
values.Set("market", currencyPair)
|
||||
}
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetOpenOrders)
|
||||
|
||||
return orders, b.HTTPRequest(path, true, values, &orders)
|
||||
}
|
||||
|
||||
// CancelOrder is used to cancel a buy or sell order.
|
||||
func (b *Bittrex) CancelOrder(uuid string) ([]Balance, error) {
|
||||
var balances []Balance
|
||||
values := url.Values{}
|
||||
values.Set("uuid", uuid)
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPICancel)
|
||||
|
||||
return balances, b.HTTPRequest(path, true, values, &balances)
|
||||
}
|
||||
|
||||
// GetAccountBalances is used to retrieve all balances from your account
|
||||
func (b *Bittrex) GetAccountBalances() ([]Balance, error) {
|
||||
var balances []Balance
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetBalances)
|
||||
|
||||
return balances, b.HTTPRequest(path, true, url.Values{}, &balances)
|
||||
}
|
||||
|
||||
// GetAccountBalanceByCurrency is used to retrieve the balance from your account
|
||||
// for a specific currency. ie. "btc" or "ltc"
|
||||
func (b *Bittrex) GetAccountBalanceByCurrency(currency string) (Balance, error) {
|
||||
var balance Balance
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetBalance)
|
||||
|
||||
return balance, b.HTTPRequest(path, true, values, &balance)
|
||||
}
|
||||
|
||||
// GetDepositAddress is used to retrieve or generate an address for a specific
|
||||
// currency. If one does not exist, the call will fail and return
|
||||
// ADDRESS_GENERATING until one is available.
|
||||
func (b *Bittrex) GetDepositAddress(currency string) (DepositAddress, error) {
|
||||
var address DepositAddress
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetDepositAddress)
|
||||
|
||||
return address, b.HTTPRequest(path, true, values, &address)
|
||||
}
|
||||
|
||||
// Withdraw is used to withdraw funds from your account.
|
||||
// note: Please account for transaction fee.
|
||||
func (b *Bittrex) Withdraw(currency, paymentID, address string, quantity float64) (UUID, error) {
|
||||
var id UUID
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
values.Set("quantity", strconv.FormatFloat(quantity, 'E', -1, 64))
|
||||
values.Set("address", address)
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIWithdraw)
|
||||
|
||||
return id, b.HTTPRequest(path, true, values, &id)
|
||||
}
|
||||
|
||||
// GetOrder is used to retrieve a single order by UUID.
|
||||
func (b *Bittrex) GetOrder(uuid string) (Order, error) {
|
||||
var order Order
|
||||
values := url.Values{}
|
||||
values.Set("uuid", uuid)
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetOrder)
|
||||
|
||||
return order, b.HTTPRequest(path, true, values, &order)
|
||||
}
|
||||
|
||||
// GetOrderHistory is used to retrieve your order history. If currencyPair
|
||||
// omitted it will return the entire order History.
|
||||
func (b *Bittrex) GetOrderHistory(currencyPair string) ([]Order, error) {
|
||||
var orders []Order
|
||||
values := url.Values{}
|
||||
|
||||
if !(currencyPair == "" || currencyPair == " ") {
|
||||
values.Set("market", currencyPair)
|
||||
}
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetOrderHistory)
|
||||
|
||||
return orders, b.HTTPRequest(path, true, values, &orders)
|
||||
}
|
||||
|
||||
// GetWithdrawalHistory is used to retrieve your withdrawal history. If currency
|
||||
// omitted it will return the entire history
|
||||
func (b *Bittrex) GetWithdrawalHistory(currency string) ([]WithdrawalHistory, error) {
|
||||
var history []WithdrawalHistory
|
||||
values := url.Values{}
|
||||
|
||||
if !(currency == "" || currency == " ") {
|
||||
values.Set("currency", currency)
|
||||
}
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetWithdrawalHistory)
|
||||
|
||||
return history, b.HTTPRequest(path, true, values, &history)
|
||||
}
|
||||
|
||||
// GetDepositHistory is used to retrieve your deposit history. If currency is
|
||||
// is omitted it will return the entire deposit history
|
||||
func (b *Bittrex) GetDepositHistory(currency string) ([]WithdrawalHistory, error) {
|
||||
var history []WithdrawalHistory
|
||||
values := url.Values{}
|
||||
|
||||
if !(currency == "" || currency == " ") {
|
||||
values.Set("currency", currency)
|
||||
}
|
||||
path := fmt.Sprintf("%s/%s", bittrexAPIURL, bittrexAPIGetDepositHistory)
|
||||
|
||||
return history, b.HTTPRequest(path, true, values, &history)
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an authenticated http request to a desired
|
||||
// path
|
||||
func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, result interface{}) (err error) {
|
||||
if !b.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
||||
}
|
||||
|
||||
if b.Nonce.Get() == 0 {
|
||||
b.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
b.Nonce.Inc()
|
||||
}
|
||||
values.Set("apikey", b.APIKey)
|
||||
values.Set("apisecret", b.APISecret)
|
||||
values.Set("nonce", b.Nonce.String())
|
||||
rawQuery := path + "?" + values.Encode()
|
||||
hmac := common.GetHMAC(
|
||||
common.HashSHA512, []byte(rawQuery), []byte(b.APISecret),
|
||||
)
|
||||
headers := make(map[string]string)
|
||||
headers["apisign"] = common.HexEncodeToString(hmac)
|
||||
|
||||
resp, err := common.SendHTTPRequest(
|
||||
"GET", rawQuery, headers, strings.NewReader(""),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Received raw: %s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response." + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTPRequest is a generalised http request function.
|
||||
func (b *Bittrex) HTTPRequest(path string, auth bool, values url.Values, v interface{}) error {
|
||||
response := Response{}
|
||||
if auth {
|
||||
if err := b.SendAuthenticatedHTTPRequest(path, values, &response); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := common.SendHTTPGetRequest(path, true, &response); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if response.Success {
|
||||
return json.Unmarshal(response.Result, &v)
|
||||
}
|
||||
return errors.New(response.Message)
|
||||
}
|
||||
293
exchanges/bittrex/bittrex_test.go
Normal file
293
exchanges/bittrex/bittrex_test.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package bittrex
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
// Please supply you own test keys here to run better tests.
|
||||
const (
|
||||
apiKey = "Testy"
|
||||
apiSecret = "TestyTesty"
|
||||
)
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bittrex{}
|
||||
b.SetDefaults()
|
||||
if b.GetName() != "Bittrex" {
|
||||
t.Error("Test Failed - Bittrex - SetDefaults() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Bittrex{}
|
||||
b.Name = "Bittrex"
|
||||
cfg := config.GetConfig()
|
||||
cfg.LoadConfig("../../testdata/configtest.dat")
|
||||
bConfig, err := cfg.GetExchangeConfig("Bittrex")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - Bittrex Setup() init error")
|
||||
}
|
||||
|
||||
b.SetDefaults()
|
||||
b.Setup(bConfig)
|
||||
|
||||
if !b.IsEnabled() || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) ||
|
||||
b.Verbose || b.Websocket || len(b.BaseCurrencies) < 1 ||
|
||||
len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 {
|
||||
t.Error("Test Failed - Bittrex Setup values not set correctly")
|
||||
}
|
||||
|
||||
bConfig.Enabled = false
|
||||
b.Setup(bConfig)
|
||||
|
||||
if b.IsEnabled() {
|
||||
t.Error("Test failed - Bittrex TestSetup incorrect value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMarkets(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
_, err := obj.GetMarkets()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetMarkets() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCurrencies(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
_, err := obj.GetCurrencies()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetCurrencies() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
invalid := ""
|
||||
btc := "btc-ltc"
|
||||
doge := "btc-DOGE"
|
||||
|
||||
obj := Bittrex{}
|
||||
_, err := obj.GetTicker(invalid)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetTicker() error")
|
||||
}
|
||||
_, err = obj.GetTicker(btc)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetTicker() error: %s", err)
|
||||
}
|
||||
_, err = obj.GetTicker(doge)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetTicker() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMarketSummaries(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
_, err := obj.GetMarketSummaries()
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetMarketSummaries() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMarketSummary(t *testing.T) {
|
||||
t.Parallel()
|
||||
pairOne := "BTC-LTC"
|
||||
invalid := "WigWham"
|
||||
|
||||
obj := Bittrex{}
|
||||
_, err := obj.GetMarketSummary(pairOne)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetMarketSummary() error: %s", err)
|
||||
}
|
||||
_, err = obj.GetMarketSummary(invalid)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetMarketSummary() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
_, err := obj.GetOrderbook("btc-ltc")
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetOrderbook() error: %s", err)
|
||||
}
|
||||
_, err = obj.GetOrderbook("wigwham")
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetOrderbook() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMarketHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
_, err := obj.GetMarketHistory("btc-ltc")
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetMarketHistory() error: %s", err)
|
||||
}
|
||||
_, err = obj.GetMarketHistory("malum")
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed - Bittrex - GetMarketHistory() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaceBuyLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.PlaceBuyLimit("btc-ltc", 1, 1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - PlaceBuyLimit() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaceSellLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.PlaceSellLimit("btc-ltc", 1, 1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - PlaceSellLimit() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetOpenOrders("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetOrder() error")
|
||||
}
|
||||
_, err = obj.GetOpenOrders("btc-ltc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetOrder() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.CancelOrder("blaaaaaaa")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - CancelOrder() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetAccountBalances()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetAccountBalances() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountBalanceByCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetAccountBalanceByCurrency("btc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetAccountBalanceByCurrency() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDepositAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetDepositAddress("btc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetDepositAddress() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdraw(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.Withdraw("btc", "something", "someplace", 1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - Withdraw() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetOrder("0cb4c4e4-bdc7-4e13-8c13-430e587d2cc1")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetOrder() error")
|
||||
}
|
||||
_, err = obj.GetOrder("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetOrder() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetOrderHistory("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetOrderHistory() error")
|
||||
}
|
||||
_, err = obj.GetOrderHistory("btc-ltc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetOrderHistory() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWithdrawelHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetWithdrawalHistory("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetWithdrawalHistory() error")
|
||||
}
|
||||
_, err = obj.GetWithdrawalHistory("btc-ltc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetWithdrawalHistory() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDepositHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := Bittrex{}
|
||||
obj.APIKey = apiKey
|
||||
obj.APISecret = apiSecret
|
||||
_, err := obj.GetDepositHistory("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetDepositHistory() error")
|
||||
}
|
||||
_, err = obj.GetDepositHistory("btc-ltc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Bittrex - GetDepositHistory() error")
|
||||
}
|
||||
}
|
||||
147
exchanges/bittrex/bittrex_types.go
Normal file
147
exchanges/bittrex/bittrex_types.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package bittrex
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Response is the generalised response type for Bittrex
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Result json.RawMessage `json:"result"`
|
||||
}
|
||||
|
||||
// Market holds current market metadata
|
||||
type Market struct {
|
||||
MarketCurrency string `json:"MarketCurrency"`
|
||||
BaseCurrency string `json:"BaseCurrency"`
|
||||
MarketCurrencyLong string `json:"MarketCurrencyLong"`
|
||||
BaseCurrencyLong string `json:"BaseCurrencyLong"`
|
||||
MinTradeSize float64 `json:"MinTradeSize"`
|
||||
MarketName string `json:"MarketName"`
|
||||
IsActive bool `json:"IsActive"`
|
||||
Created string `json:"Created"`
|
||||
}
|
||||
|
||||
// Currency holds supported currency metadata
|
||||
type Currency struct {
|
||||
Currency string `json:"Currency"`
|
||||
CurrencyLong string `json:"CurrencyLong"`
|
||||
MinConfirmation int `json:"MinConfirmation"`
|
||||
TxFee float64 `json:"TxFee"`
|
||||
IsActive bool `json:"IsActive"`
|
||||
CoinType string `json:"CoinType"`
|
||||
BaseAddress string `json:"BaseAddress"`
|
||||
}
|
||||
|
||||
// Ticker holds basic ticker information
|
||||
type Ticker struct {
|
||||
Bid float64 `json:"Bid"`
|
||||
Ask float64 `json:"Ask"`
|
||||
Last float64 `json:"Last"`
|
||||
}
|
||||
|
||||
// MarketSummary holds last 24 hour metadata of an active exchange
|
||||
type MarketSummary struct {
|
||||
MarketName string `json:"MarketName"`
|
||||
High float64 `json:"High"`
|
||||
Low float64 `json:"Low"`
|
||||
Volume float64 `json:"Volume"`
|
||||
Last float64 `json:"Last"`
|
||||
BaseVolume float64 `json:"BaseVolume"`
|
||||
TimeStamp string `json:"TimeStamp"`
|
||||
Bid float64 `json:"Bid"`
|
||||
Ask float64 `json:"Ask"`
|
||||
OpenBuyOrders int `json:"OpenBuyOrders"`
|
||||
OpenSellOrders int `json:"OpenSellOrders"`
|
||||
PrevDay float64 `json:"PrevDay"`
|
||||
Created string `json:"Created"`
|
||||
DisplayMarketName string `json:"DisplayMarketName"`
|
||||
}
|
||||
|
||||
// OrderBooks holds an array of buy & sell orders held on the exchange
|
||||
type OrderBooks struct {
|
||||
Buy []OrderBook `json:"buy"`
|
||||
Sell []OrderBook `json:"sell"`
|
||||
}
|
||||
|
||||
// OrderBook holds a singular order on an exchange
|
||||
type OrderBook struct {
|
||||
Quantity float64 `json:"Quantity"`
|
||||
Rate float64 `json:"Rate"`
|
||||
}
|
||||
|
||||
// MarketHistory holds an executed trade's data for a market ie "BTC-LTC"
|
||||
type MarketHistory struct {
|
||||
ID int `json:"Id"`
|
||||
Timestamp string `json:"TimeStamp"`
|
||||
Quantity float64 `json:"Quantity"`
|
||||
Price float64 `json:"Price"`
|
||||
Total float64 `json:"Total"`
|
||||
FillType string `json:"FillType"`
|
||||
OrderType string `json:"OrderType"`
|
||||
}
|
||||
|
||||
// Balance holds the balance from your account for a specified currency
|
||||
type Balance struct {
|
||||
Currency string `json:"Currency"`
|
||||
Balance float64 `json:"Balance"`
|
||||
Available float64 `json:"Available"`
|
||||
Pending float64 `json:"Pending"`
|
||||
CryptoAddress string `json:"CryptoAddress"`
|
||||
Requested bool `json:"Requested"`
|
||||
UUID string `json:"Uuid"`
|
||||
}
|
||||
|
||||
// DepositAddress holds a generated address to send specific coins to the
|
||||
// exchange
|
||||
type DepositAddress struct {
|
||||
Currency string `json:"Currency"`
|
||||
Address string `json:"Address"`
|
||||
}
|
||||
|
||||
// UUID contains the universal unique identifier for one or multiple
|
||||
// transactions on the exchange
|
||||
type UUID struct {
|
||||
ID string `json:"uuid"`
|
||||
}
|
||||
|
||||
// Order holds the full order information associated with the UUID supplied
|
||||
type Order struct {
|
||||
AccountID string `json:"AccountId"`
|
||||
OrderUUID string `json:"OrderUuid"`
|
||||
Exchange string `json:"Exchange"`
|
||||
Type string `json:"Type"`
|
||||
Quantity float64 `json:"Quantity"`
|
||||
QuantityRemaining float64 `json:"QuantityRemaining"`
|
||||
Limit float64 `json:"Limit"`
|
||||
Reserved float64 `json:"Reserved"`
|
||||
ReserveRemaining float64 `json:"ReserveRemaining"`
|
||||
CommissionReserved float64 `json:"CommissionReserved"`
|
||||
CommissionReserveRemaining float64 `json:"CommissionReserveRemaining"`
|
||||
CommissionPaid float64 `json:"CommissionPaid"`
|
||||
Price float64 `json:"Price"`
|
||||
PricePerUnit float64 `json:"PricePerUnit"`
|
||||
Opened string `json:"Opened"`
|
||||
Closed string `json:"Closed"`
|
||||
IsOpen bool `json:"IsOpen"`
|
||||
Sentinel string `json:"Sentinel"`
|
||||
CancelInitiated bool `json:"CancelInitiated"`
|
||||
ImmediateOrCancel bool `json:"ImmediateOrCancel"`
|
||||
IsConditional bool `json:"IsConditional"`
|
||||
Condition string `json:"Condition"`
|
||||
ConditionTarget string `json:"ConditionTarget"`
|
||||
}
|
||||
|
||||
// WithdrawalHistory holds the Withdrawal history data
|
||||
type WithdrawalHistory struct {
|
||||
PaymentUUID string `json:"PaymentUuid"`
|
||||
Currency string `json:"Currency"`
|
||||
Amount float64 `json:"Amount"`
|
||||
Address string `json:"Address"`
|
||||
Opened string `json:"Opened"`
|
||||
Authorized bool `json:"Authorized"`
|
||||
PendingPayment bool `json:"PendingPayment"`
|
||||
TxCost float64 `json:"TxCost"`
|
||||
TxID string `json:"TxId"`
|
||||
Canceled bool `json:"Canceled"`
|
||||
InvalidAddress bool `json:"InvalidAddress"`
|
||||
}
|
||||
139
exchanges/bittrex/bittrex_wrapper.go
Normal file
139
exchanges/bittrex/bittrex_wrapper.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package bittrex
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Bittrex go routine
|
||||
func (b *Bittrex) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
// Run implements the Bittrex wrapper
|
||||
func (b *Bittrex) 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)
|
||||
}
|
||||
|
||||
exchangeProducts, err := b.GetMarkets()
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available symbols.\n", b.GetName())
|
||||
} else {
|
||||
forceUpgrade := false
|
||||
if !common.DataContains(b.EnabledPairs, "-") || !common.DataContains(b.AvailablePairs, "-") {
|
||||
forceUpgrade = true
|
||||
}
|
||||
var currencies []string
|
||||
for x := range exchangeProducts {
|
||||
if !exchangeProducts[x].IsActive || exchangeProducts[x].MarketName == "" {
|
||||
continue
|
||||
}
|
||||
currencies = append(currencies, exchangeProducts[x].MarketName)
|
||||
}
|
||||
|
||||
if forceUpgrade {
|
||||
enabledPairs := []string{"USDT-BTC"}
|
||||
log.Println("WARNING: Available pairs for Bittrex reset due to config upgrade, please enable the ones you would like again")
|
||||
|
||||
err = b.UpdateEnabledCurrencies(enabledPairs, true)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
err = b.UpdateAvailableCurrencies(currencies, forceUpgrade)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetExchangeAccountInfo Retrieves balances for all enabled currencies for the
|
||||
// Bittrex exchange
|
||||
func (b *Bittrex) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = b.GetName()
|
||||
accountBalance, err := b.GetAccountBalances()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Balance
|
||||
exchangeCurrency.Hold = accountBalance[i].Balance - accountBalance[i].Available
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (b *Bittrex) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := b.GetMarketSummary(exchange.FormatExchangeCurrency(b.GetName(), p).String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick[0].Ask
|
||||
tickerPrice.Bid = tick[0].Bid
|
||||
tickerPrice.Last = tick[0].Last
|
||||
tickerPrice.Volume = tick[0].Volume
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (b *Bittrex) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tick, err := ticker.GetTicker(b.GetName(), p, ticker.Spot)
|
||||
if err != nil {
|
||||
return b.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (b *Bittrex) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return b.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (b *Bittrex) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := b.GetOrderbook(exchange.FormatExchangeCurrency(b.GetName(), p).String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Buy {
|
||||
orderBook.Bids = append(orderBook.Bids,
|
||||
orderbook.Item{
|
||||
Amount: orderbookNew.Buy[x].Quantity,
|
||||
Price: orderbookNew.Buy[x].Rate,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Sell {
|
||||
orderBook.Asks = append(orderBook.Asks,
|
||||
orderbook.Item{
|
||||
Amount: orderbookNew.Sell[x].Quantity,
|
||||
Price: orderbookNew.Sell[x].Rate,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||
}
|
||||
@@ -12,40 +12,43 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
BTCC_API_URL = "https://api.btcchina.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"
|
||||
btccAPIUrl = "https://api.btcchina.com/"
|
||||
btccAPIAuthenticatedMethod = "api_trade_v1.php"
|
||||
btccAPIVersion = "2.0.1.3"
|
||||
btccOrderBuy = "buyOrder2"
|
||||
btccOrderSell = "sellOrder2"
|
||||
btccOrderCancel = "cancelOrder"
|
||||
btccIcebergBuy = "buyIcebergOrder"
|
||||
btccIcebergSell = "sellIcebergOrder"
|
||||
btccIcebergOrder = "getIcebergOrder"
|
||||
btccIcebergOrders = "getIcebergOrders"
|
||||
btccIcebergCancel = "cancelIcebergOrder"
|
||||
btccAccountInfo = "getAccountInfo"
|
||||
btccDeposits = "getDeposits"
|
||||
btccMarketdepth = "getMarketDepth2"
|
||||
btccOrder = "getOrder"
|
||||
btccOrders = "getOrders"
|
||||
btccTransactions = "getTransactions"
|
||||
btccWithdrawal = "getWithdrawal"
|
||||
btccWithdrawals = "getWithdrawals"
|
||||
btccWithdrawalRequest = "requestWithdrawal"
|
||||
btccStoporderBuy = "buyStopOrder"
|
||||
btccStoporderSell = "sellStopOrder"
|
||||
btccStoporderCancel = "cancelStopOrder"
|
||||
btccStoporder = "getStopOrder"
|
||||
btccStoporders = "getStopOrders"
|
||||
)
|
||||
|
||||
// BTCC is the main overaching type across the BTCC package
|
||||
type BTCC struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
// SetDefaults sets default values for the exchange
|
||||
func (b *BTCC) SetDefaults() {
|
||||
b.Name = "BTCC"
|
||||
b.Enabled = false
|
||||
@@ -53,9 +56,14 @@ func (b *BTCC) SetDefaults() {
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.RequestCurrencyPairFormat.Uppercase = false
|
||||
b.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Uppercase = true
|
||||
b.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
//Setup is run on startup to setup exchange with config values
|
||||
// Setup is run on startup to setup exchange with config values
|
||||
func (b *BTCC) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
@@ -69,39 +77,51 @@ func (b *BTCC) Setup(exch config.ExchangeConfig) {
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := b.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = b.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetFee returns the fees associated with transactions
|
||||
func (b *BTCC) GetFee() float64 {
|
||||
return b.Fee
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTicker(symbol string) (BTCCTicker, error) {
|
||||
type Response struct {
|
||||
Ticker BTCCTicker
|
||||
}
|
||||
|
||||
// GetTicker returns ticker information
|
||||
// currencyPair - Example "btccny", "ltccny" or "ltcbtc"
|
||||
func (b *BTCC) GetTicker(currencyPair string) (Ticker, error) {
|
||||
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
|
||||
req := fmt.Sprintf("%sdata/ticker?market=%s", btccAPIUrl, currencyPair)
|
||||
|
||||
return resp.Ticker, common.SendHTTPGetRequest(req, true, &resp)
|
||||
}
|
||||
|
||||
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
|
||||
// GetTradesLast24h returns the trades executed on the exchange over the past
|
||||
// 24 hours by currency pair
|
||||
// currencyPair - Example "btccny", "ltccny" or "ltcbtc"
|
||||
func (b *BTCC) GetTradesLast24h(currencyPair string) ([]Trade, error) {
|
||||
trades := []Trade{}
|
||||
req := fmt.Sprintf("%sdata/trades?market=%s", btccAPIUrl, currencyPair)
|
||||
|
||||
return trades, common.SendHTTPGetRequest(req, true, &trades)
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTradeHistory(symbol string, limit, sinceTid int64, time time.Time) bool {
|
||||
req := fmt.Sprintf("%sdata/historydata?market=%s", BTCC_API_URL, symbol)
|
||||
// GetTradeHistory returns trade history data
|
||||
// currencyPair - Example "btccny", "ltccny" or "ltcbtc"
|
||||
// limit - limits the returned trades example "10"
|
||||
// sinceTid - returns trade records starting from id supplied example "5000"
|
||||
// time - returns trade records starting from unix time 1406794449
|
||||
func (b *BTCC) GetTradeHistory(currencyPair string, limit, sinceTid int64, time time.Time) ([]Trade, error) {
|
||||
trades := []Trade{}
|
||||
|
||||
req := fmt.Sprintf("%sdata/historydata?market=%s", btccAPIUrl, currencyPair)
|
||||
|
||||
v := url.Values{}
|
||||
|
||||
if limit > 0 {
|
||||
@@ -115,37 +135,33 @@ func (b *BTCC) GetTradeHistory(symbol string, limit, sinceTid int64, time time.T
|
||||
}
|
||||
|
||||
req = common.EncodeURLValues(req, v)
|
||||
err := common.SendHTTPGetRequest(req, true, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
return trades, common.SendHTTPGetRequest(req, true, &trades)
|
||||
}
|
||||
|
||||
func (b *BTCC) GetOrderBook(symbol string, limit int) (BTCCOrderbook, error) {
|
||||
result := BTCCOrderbook{}
|
||||
req := fmt.Sprintf("%sdata/orderbook?market=%s&limit=%d", BTCC_API_URL, symbol, limit)
|
||||
err := common.SendHTTPGetRequest(req, true, &result)
|
||||
if err != nil {
|
||||
return BTCCOrderbook{}, err
|
||||
// GetOrderBook returns current market order book
|
||||
// currencyPair - Example "btccny", "ltccny" or "ltcbtc"
|
||||
// limit - limits the returned trades example "10" if 0 will return full
|
||||
// orderbook
|
||||
func (b *BTCC) GetOrderBook(currencyPair string, limit int) (Orderbook, error) {
|
||||
result := Orderbook{}
|
||||
|
||||
req := fmt.Sprintf("%sdata/orderbook?market=%s&limit=%d", btccAPIUrl, currencyPair, limit)
|
||||
if limit == 0 {
|
||||
req = fmt.Sprintf("%sdata/orderbook?market=%s", btccAPIUrl, currencyPair)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, common.SendHTTPGetRequest(req, true, &result)
|
||||
}
|
||||
|
||||
func (b *BTCC) GetAccountInfo(infoType string) {
|
||||
func (b *BTCC) GetAccountInfo(infoType string) error {
|
||||
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)
|
||||
}
|
||||
return b.SendAuthenticatedHTTPRequest(btccAccountInfo, params)
|
||||
}
|
||||
|
||||
func (b *BTCC) PlaceOrder(buyOrder bool, price, amount float64, market string) {
|
||||
@@ -157,9 +173,9 @@ func (b *BTCC) PlaceOrder(buyOrder bool, price, amount float64, market string) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
req := BTCC_ORDER_BUY
|
||||
req := btccOrderBuy
|
||||
if !buyOrder {
|
||||
req = BTCC_ORDER_SELL
|
||||
req = btccOrderSell
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(req, params)
|
||||
@@ -177,7 +193,7 @@ func (b *BTCC) CancelOrder(orderID int64, market string) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ORDER_CANCEL, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccOrderCancel, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -192,7 +208,7 @@ func (b *BTCC) GetDeposits(currency string, pending bool) {
|
||||
params = append(params, pending)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_DEPOSITS, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccDeposits, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -210,7 +226,7 @@ func (b *BTCC) GetMarketDepth(market string, limit int64) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_MARKETDEPTH, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccMarketdepth, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -229,7 +245,7 @@ func (b *BTCC) GetOrder(orderID int64, market string, detailed bool) {
|
||||
params = append(params, detailed)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ORDER, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccOrder, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -263,7 +279,7 @@ func (b *BTCC) GetOrders(openonly bool, market string, limit, offset, since int6
|
||||
params = append(params, detailed)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ORDERS, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccOrders, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -293,7 +309,7 @@ func (b *BTCC) GetTransactions(transType string, limit, offset, since int64, sin
|
||||
params = append(params, sinceType)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_TRANSACTIONS, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccTransactions, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -308,7 +324,7 @@ func (b *BTCC) GetWithdrawal(withdrawalID int64, currency string) {
|
||||
params = append(params, currency)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_WITHDRAWAL, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccWithdrawal, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -323,7 +339,7 @@ func (b *BTCC) GetWithdrawals(currency string, pending bool) {
|
||||
params = append(params, pending)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_WITHDRAWALS, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccWithdrawals, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -335,7 +351,7 @@ func (b *BTCC) RequestWithdrawal(currency string, amount float64) {
|
||||
params = append(params, currency)
|
||||
params = append(params, amount)
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_WITHDRAWAL_REQUEST, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccWithdrawalRequest, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -353,9 +369,9 @@ func (b *BTCC) IcebergOrder(buyOrder bool, price, amount, discAmount, variance f
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
req := BTCC_ICEBERG_BUY
|
||||
req := btccIcebergBuy
|
||||
if !buyOrder {
|
||||
req = BTCC_ICEBERG_SELL
|
||||
req = btccIcebergSell
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(req, params)
|
||||
@@ -373,7 +389,7 @@ func (b *BTCC) GetIcebergOrder(orderID int64, market string) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ICEBERG_ORDER, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccIcebergOrder, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -395,7 +411,7 @@ func (b *BTCC) GetIcebergOrders(limit, offset int64, market string) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ICEBERG_ORDERS, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccIcebergOrders, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -410,7 +426,7 @@ func (b *BTCC) CancelIcebergOrder(orderID int64, market string) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_ICEBERG_CANCEL, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccIcebergCancel, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -439,9 +455,9 @@ func (b *BTCC) PlaceStopOrder(buyOder bool, stopPrice, price, amount, trailingAm
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
req := BTCC_STOPORDER_BUY
|
||||
req := btccStoporderBuy
|
||||
if !buyOder {
|
||||
req = BTCC_STOPORDER_SELL
|
||||
req = btccStoporderSell
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(req, params)
|
||||
@@ -459,7 +475,7 @@ func (b *BTCC) GetStopOrder(orderID int64, market string) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_STOPORDER, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccStoporder, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -493,7 +509,7 @@ func (b *BTCC) GetStopOrders(status, orderType string, stopPrice float64, limit,
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_STOPORDERS, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccStoporders, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -508,7 +524,7 @@ func (b *BTCC) CancelStopOrder(orderID int64, market string) {
|
||||
params = append(params, market)
|
||||
}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(BTCC_STOPORDER_CANCEL, params)
|
||||
err := b.SendAuthenticatedHTTPRequest(btccStoporderCancel, params)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -516,8 +532,16 @@ func (b *BTCC) CancelStopOrder(orderID int64, market string) {
|
||||
}
|
||||
|
||||
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 !b.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
||||
}
|
||||
|
||||
if b.Nonce.Get() == 0 {
|
||||
b.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
b.Nonce.Inc()
|
||||
}
|
||||
encoded := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=%d&method=%s¶ms=", b.Nonce.String()[0:16], b.APIKey, 1, method)
|
||||
|
||||
if len(params) == 0 {
|
||||
params = make([]interface{}, 0)
|
||||
@@ -558,12 +582,12 @@ func (b *BTCC) SendAuthenticatedHTTPRequest(method string, params []interface{})
|
||||
log.Println(encoded)
|
||||
}
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA1, []byte(encoded), []byte(b.APISecret))
|
||||
hmac := common.GetHMAC(common.HashSHA1, []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
|
||||
apiURL := btccAPIUrl + btccAPIAuthenticatedMethod
|
||||
data, err := common.JSONEncode(postData)
|
||||
|
||||
if err != nil {
|
||||
@@ -577,7 +601,7 @@ func (b *BTCC) SendAuthenticatedHTTPRequest(method string, params []interface{})
|
||||
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
|
||||
headers["Json-Rpc-Tonce"] = b.Nonce.String()
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", apiURL, headers, strings.NewReader(string(data)))
|
||||
|
||||
|
||||
93
exchanges/btcc/btcc_test.go
Normal file
93
exchanges/btcc/btcc_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package btcc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
// Please supply your own APIkeys here to do better tests
|
||||
const (
|
||||
apiKey = ""
|
||||
apiSecret = ""
|
||||
)
|
||||
|
||||
var b BTCC
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
b.SetDefaults()
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := BTCC{}
|
||||
b.Name = "BTCC"
|
||||
cfg := config.GetConfig()
|
||||
cfg.LoadConfig("../../testdata/configtest.dat")
|
||||
bConfig, err := cfg.GetExchangeConfig("BTCC")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - BTCC Setup() init error")
|
||||
}
|
||||
|
||||
b.SetDefaults()
|
||||
b.Setup(bConfig)
|
||||
|
||||
if !b.IsEnabled() || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) ||
|
||||
b.Verbose || b.Websocket || len(b.BaseCurrencies) < 1 ||
|
||||
len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 {
|
||||
t.Error("Test Failed - BTCC Setup values not set correctly")
|
||||
}
|
||||
|
||||
bConfig.Enabled = false
|
||||
b.Setup(bConfig)
|
||||
|
||||
if b.IsEnabled() {
|
||||
t.Error("Test failed - BTCC TestSetup incorrect value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFee(t *testing.T) {
|
||||
if b.GetFee() != 0 {
|
||||
t.Error("Test failed - GetFee() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
_, err := b.GetTicker("ltccny")
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetTicker() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTradesLast24h(t *testing.T) {
|
||||
_, err := b.GetTradesLast24h("ltccny")
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetTradesLast24h() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTradeHistory(t *testing.T) {
|
||||
_, err := b.GetTradeHistory("ltccny", 0, 0, time.Time{})
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetTradeHistory() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderBook(t *testing.T) {
|
||||
_, err := b.GetOrderBook("ltccny", 100)
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetOrderBook() error", err)
|
||||
}
|
||||
_, err = b.GetOrderBook("ltccny", 0)
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetOrderBook() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountInfo(t *testing.T) {
|
||||
err := b.GetAccountInfo("")
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetAccountInfo() error", err)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,45 @@
|
||||
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"`
|
||||
// Response is the generalized response type
|
||||
type Response struct {
|
||||
Ticker Ticker `json:"ticker"`
|
||||
BtcCny Ticker `json:"ticker_btccny"`
|
||||
LtcCny Ticker `json:"ticker_ltccny"`
|
||||
LtcBtc Ticker `json:"ticker_ltcbtc"`
|
||||
}
|
||||
|
||||
type BTCCProfile struct {
|
||||
// Ticker holds basic ticker information
|
||||
type Ticker struct {
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Buy float64 `json:"buy,string"`
|
||||
Sell float64 `json:"sell,string"`
|
||||
Last float64 `json:"last,string"`
|
||||
Vol float64 `json:"vol,string"`
|
||||
Date int64 `json:"date"`
|
||||
Vwap float64 `json:"vwap,string"`
|
||||
PrevClose float64 `json:"prev_close,string"`
|
||||
Open float64 `json:"open,string"`
|
||||
}
|
||||
|
||||
// Trade holds executed trade data
|
||||
type Trade struct {
|
||||
Date int64 `json:"date,string"`
|
||||
Price float64 `json:"price"`
|
||||
Amount float64 `json:"amount"`
|
||||
TID int64 `json:"tid,string"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// Orderbook holds orderbook data
|
||||
type Orderbook struct {
|
||||
Bids [][]float64 `json:"bids"`
|
||||
Asks [][]float64 `json:"asks"`
|
||||
Date int64 `json:"date"`
|
||||
}
|
||||
|
||||
// Profile holds profile information
|
||||
type Profile struct {
|
||||
Username string
|
||||
TradePasswordEnabled bool `json:"trade_password_enabled,bool"`
|
||||
OTPEnabled bool `json:"otp_enabled,bool"`
|
||||
@@ -29,13 +55,8 @@ type BTCCProfile struct {
|
||||
APIKeyPermission int64 `json:"api_key_permission"`
|
||||
}
|
||||
|
||||
type BTCCOrderbook struct {
|
||||
Bids [][]float64 `json:"bids"`
|
||||
Asks [][]float64 `json:"asks"`
|
||||
Date int64 `json:"date"`
|
||||
}
|
||||
|
||||
type BTCCCurrencyGeneric struct {
|
||||
// CurrencyGeneric holds currency information
|
||||
type CurrencyGeneric struct {
|
||||
Currency string
|
||||
Symbol string
|
||||
Amount string
|
||||
@@ -43,7 +64,8 @@ type BTCCCurrencyGeneric struct {
|
||||
AmountDecimal float64 `json:"amount_decimal"`
|
||||
}
|
||||
|
||||
type BTCCOrder struct {
|
||||
// Order holds order information
|
||||
type Order struct {
|
||||
ID int64
|
||||
Type string
|
||||
Price float64
|
||||
@@ -52,16 +74,18 @@ type BTCCOrder struct {
|
||||
AmountOrig float64 `json:"amount_original"`
|
||||
Date int64
|
||||
Status string
|
||||
Detail BTCCOrderDetail
|
||||
Detail OrderDetail
|
||||
}
|
||||
|
||||
type BTCCOrderDetail struct {
|
||||
// OrderDetail holds order detail information
|
||||
type OrderDetail struct {
|
||||
Dateline int64
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BTCCWithdrawal struct {
|
||||
// Withdrawal holds withdrawal transaction information
|
||||
type Withdrawal struct {
|
||||
ID int64
|
||||
Address string
|
||||
Currency string
|
||||
@@ -71,7 +95,8 @@ type BTCCWithdrawal struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
type BTCCDeposit struct {
|
||||
// Deposit holds deposit address information
|
||||
type Deposit struct {
|
||||
ID int64
|
||||
Address string
|
||||
Currency string
|
||||
@@ -80,17 +105,20 @@ type BTCCDeposit struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
type BTCCBidAsk struct {
|
||||
// BidAsk holds bid and ask information
|
||||
type BidAsk struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
type BTCCDepth struct {
|
||||
Bid []BTCCBidAsk
|
||||
Ask []BTCCBidAsk
|
||||
// Depth holds order book depth
|
||||
type Depth struct {
|
||||
Bid []BidAsk
|
||||
Ask []BidAsk
|
||||
}
|
||||
|
||||
type BTCCTransaction struct {
|
||||
// Transaction holds transaction information
|
||||
type Transaction struct {
|
||||
ID int64
|
||||
Type string
|
||||
BTCAmount float64 `json:"btc_amount"`
|
||||
@@ -99,7 +127,8 @@ type BTCCTransaction struct {
|
||||
Date int64
|
||||
}
|
||||
|
||||
type BTCCIcebergOrder struct {
|
||||
// IcebergOrder holds iceberg lettuce
|
||||
type IcebergOrder struct {
|
||||
ID int64
|
||||
Type string
|
||||
Price float64
|
||||
@@ -112,7 +141,8 @@ type BTCCIcebergOrder struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
type BTCCStopOrder struct {
|
||||
// StopOrder holds stop order information
|
||||
type StopOrder struct {
|
||||
ID int64
|
||||
Type string
|
||||
StopPrice float64 `json:"stop_price"`
|
||||
@@ -126,19 +156,22 @@ type BTCCStopOrder struct {
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
type BTCCWebsocketOrder struct {
|
||||
// WebsocketOrder holds websocket order information
|
||||
type WebsocketOrder 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"`
|
||||
// WebsocketGroupOrder holds websocket group order book information
|
||||
type WebsocketGroupOrder struct {
|
||||
Asks []WebsocketOrder `json:"ask"`
|
||||
Bids []WebsocketOrder `json:"bid"`
|
||||
Market string `json:"market"`
|
||||
}
|
||||
|
||||
type BTCCWebsocketTrade struct {
|
||||
// WebsocketTrade holds websocket trade information
|
||||
type WebsocketTrade struct {
|
||||
Amount float64 `json:"amount"`
|
||||
Date float64 `json:"date"`
|
||||
Market string `json:"market"`
|
||||
@@ -147,7 +180,8 @@ type BTCCWebsocketTrade struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type BTCCWebsocketTicker struct {
|
||||
// WebsocketTicker holds websocket ticker information
|
||||
type WebsocketTicker struct {
|
||||
Buy float64 `json:"buy"`
|
||||
Date float64 `json:"date"`
|
||||
High float64 `json:"high"`
|
||||
|
||||
@@ -56,7 +56,7 @@ func (b *BTCC) OnMessage(message []byte, output chan socketio.Message) {
|
||||
|
||||
func (b *BTCC) OnTicker(message []byte, output chan socketio.Message) {
|
||||
type Response struct {
|
||||
Ticker BTCCWebsocketTicker `json:"ticker"`
|
||||
Ticker WebsocketTicker `json:"ticker"`
|
||||
}
|
||||
var resp Response
|
||||
err := common.JSONDecode(message, &resp)
|
||||
@@ -69,7 +69,7 @@ func (b *BTCC) OnTicker(message []byte, output chan socketio.Message) {
|
||||
|
||||
func (b *BTCC) OnGroupOrder(message []byte, output chan socketio.Message) {
|
||||
type Response struct {
|
||||
GroupOrder BTCCWebsocketGroupOrder `json:"grouporder"`
|
||||
GroupOrder WebsocketGroupOrder `json:"grouporder"`
|
||||
}
|
||||
var resp Response
|
||||
err := common.JSONDecode(message, &resp)
|
||||
@@ -81,7 +81,7 @@ func (b *BTCC) OnGroupOrder(message []byte, output chan socketio.Message) {
|
||||
}
|
||||
|
||||
func (b *BTCC) OnTrade(message []byte, output chan socketio.Message) {
|
||||
trade := BTCCWebsocketTrade{}
|
||||
trade := WebsocketTrade{}
|
||||
err := common.JSONDecode(message, &trade)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -2,20 +2,20 @@ package btcc
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the BTCC go routine
|
||||
func (b *BTCC) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
// Run implements the BTCC wrapper
|
||||
func (b *BTCC) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
@@ -26,32 +26,15 @@ func (b *BTCC) Run() {
|
||||
if b.Websocket {
|
||||
go b.WebsocketClient()
|
||||
}
|
||||
|
||||
for b.Enabled {
|
||||
for _, x := range b.EnabledPairs {
|
||||
currency := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
go func() {
|
||||
ticker, err := b.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("BTCC %s: Last %f High %f Low %f Volume %f\n", currency.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(b.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCC) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (b *BTCC) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := b.GetTicker(exchange.FormatExchangeCurrency(b.GetName(), p).String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := b.GetTicker(p.Pair().Lower().String())
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Sell
|
||||
tickerPrice.Bid = tick.Buy
|
||||
@@ -59,41 +42,54 @@ func (b *BTCC) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (b *BTCC) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (b *BTCC) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return b.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
orderbookNew, err := b.GetOrderBook(p.Pair().Lower().String(), 100)
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (b *BTCC) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return b.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (b *BTCC) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := b.GetOrderBook(exchange.FormatExchangeCurrency(b.GetName(), p).String(), 100)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(ob.Bids, orderbook.OrderbookItem{Price: data[0], Amount: data[1]})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: data[0], Amount: data[1]})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(ob.Asks, orderbook.OrderbookItem{Price: data[0], Amount: data[1]})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: data[0], Amount: data[1]})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
// GetExchangeAccountInfo : Retrieves balances for all enabled currencies for
|
||||
// the Kraken exchange - TODO
|
||||
func (b *BTCC) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = b.GetName()
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package btce
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"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
|
||||
stats.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(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, ok := b.Ticker[p.Pair().Lower().String()]
|
||||
if !ok {
|
||||
return tickerPrice, errors.New("Unable to get currency.")
|
||||
}
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Buy
|
||||
tickerPrice.Bid = tick.Sell
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol_cur
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
}
|
||||
|
||||
func (b *BTCE) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
orderbookNew, err := b.GetDepth(p.Pair().Lower().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(ob.Bids, orderbook.OrderbookItem{Price: data[0], Amount: data[1]})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(ob.Asks, orderbook.OrderbookItem{Price: data[0], Amount: data[1]})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook)
|
||||
return orderBook, 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
|
||||
}
|
||||
@@ -6,31 +6,45 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
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"
|
||||
btcMarketsAPIURL = "https://api.btcmarkets.net"
|
||||
btcMarketsAPIVersion = "0"
|
||||
btcMarketsAccountBalance = "/account/balance"
|
||||
btcMarketsOrderCreate = "/order/create"
|
||||
btcMarketsOrderCancel = "/order/cancel"
|
||||
btcMarketsOrderHistory = "/order/history"
|
||||
btcMarketsOrderOpen = "/order/open"
|
||||
btcMarketsOrderTradeHistory = "/order/trade/history"
|
||||
btcMarketsOrderDetail = "/order/detail"
|
||||
btcMarketsWithdrawCrypto = "/fundtransfer/withdrawCrypto"
|
||||
btcMarketsWithdrawAud = "/fundtransfer/withdrawEFT"
|
||||
|
||||
//Status Values
|
||||
orderStatusNew = "New"
|
||||
orderStatusPlaced = "Placed"
|
||||
orderStatusFailed = "Failed"
|
||||
orderStatusError = "Error"
|
||||
orderStatusCancelled = "Cancelled"
|
||||
orderStatusPartiallyCancelled = "Partially Cancelled"
|
||||
orderStatusFullyMatched = "Fully Matched"
|
||||
orderStatusPartiallyMatched = "Partially Matched"
|
||||
)
|
||||
|
||||
// BTCMarkets is the overarching type across the BTCMarkets package
|
||||
type BTCMarkets struct {
|
||||
exchange.ExchangeBase
|
||||
Ticker map[string]BTCMarketsTicker
|
||||
exchange.Base
|
||||
Ticker map[string]Ticker
|
||||
}
|
||||
|
||||
// SetDefaults sets basic defaults
|
||||
func (b *BTCMarkets) SetDefaults() {
|
||||
b.Name = "BTC Markets"
|
||||
b.Enabled = false
|
||||
@@ -38,9 +52,15 @@ func (b *BTCMarkets) SetDefaults() {
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.Ticker = make(map[string]BTCMarketsTicker)
|
||||
b.Ticker = make(map[string]Ticker)
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.RequestCurrencyPairFormat.Uppercase = true
|
||||
b.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Uppercase = true
|
||||
b.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
// Setup takes in an exchange configuration and sets all paramaters
|
||||
func (b *BTCMarkets) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
@@ -54,113 +74,101 @@ func (b *BTCMarkets) Setup(exch config.ExchangeConfig) {
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
|
||||
err := b.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = b.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetFee returns the BTCMarkets fee on transactions
|
||||
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
|
||||
// GetTicker returns a ticker
|
||||
// symbol - example "btc" or "ltc"
|
||||
func (b *BTCMarkets) GetTicker(symbol string) (Ticker, error) {
|
||||
ticker := Ticker{}
|
||||
path := fmt.Sprintf("/market/%s/AUD/tick", common.StringToUpper(symbol))
|
||||
|
||||
return ticker,
|
||||
common.SendHTTPGetRequest(btcMarketsAPIURL+path, true, &ticker)
|
||||
}
|
||||
|
||||
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
|
||||
// GetOrderbook returns current orderbook
|
||||
// symbol - example "btc" or "ltc"
|
||||
func (b *BTCMarkets) GetOrderbook(symbol string) (Orderbook, error) {
|
||||
orderbook := Orderbook{}
|
||||
path := fmt.Sprintf("/market/%s/AUD/orderbook", common.StringToUpper(symbol))
|
||||
|
||||
return orderbook,
|
||||
common.SendHTTPGetRequest(btcMarketsAPIURL+path, true, &orderbook)
|
||||
}
|
||||
|
||||
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
|
||||
// GetTrades returns executed trades on the exchange
|
||||
// symbol - example "btc" or "ltc"
|
||||
// values - optional paramater "since" example values.Set(since, "59868345231")
|
||||
func (b *BTCMarkets) GetTrades(symbol string, values url.Values) ([]Trade, error) {
|
||||
trades := []Trade{}
|
||||
path := common.EncodeURLValues(fmt.Sprintf("%s/market/%s/AUD/trades", btcMarketsAPIURL, symbol), values)
|
||||
|
||||
return trades, common.SendHTTPGetRequest(path, true, &trades)
|
||||
}
|
||||
|
||||
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"`
|
||||
// NewOrder requests a new order and returns an ID
|
||||
// currency - example "AUD"
|
||||
// instrument - example "BTC"
|
||||
// price - example 13000000000 (i.e x 100000000)
|
||||
// amount - example 100000000 (i.e x 100000000)
|
||||
// orderside - example "Bid" or "Ask"
|
||||
// orderType - example "limit"
|
||||
// clientReq - example "abc-cdf-1000"
|
||||
func (b *BTCMarkets) NewOrder(currency, instrument string, price, amount int64, orderSide, orderType, clientReq string) (int, error) {
|
||||
order := OrderToGo{
|
||||
Currency: common.StringToUpper(currency),
|
||||
Instrument: common.StringToUpper(instrument),
|
||||
Price: price * common.SatoshisPerBTC,
|
||||
Volume: amount * common.SatoshisPerBTC,
|
||||
OrderSide: orderSide,
|
||||
OrderType: orderType,
|
||||
ClientRequestID: clientReq,
|
||||
}
|
||||
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)
|
||||
resp := Response{}
|
||||
|
||||
err := b.SendAuthenticatedRequest("POST", btcMarketsOrderCreate, 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 0, fmt.Errorf("%s Unable to place order. Error message: %s", b.GetName(), resp.ErrorMessage)
|
||||
}
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
// CancelOrder cancels an order by its ID
|
||||
// orderID - id for order example "1337"
|
||||
func (b *BTCMarkets) CancelOrder(orderID []int64) (bool, error) {
|
||||
resp := Response{}
|
||||
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)
|
||||
|
||||
err := b.SendAuthenticatedRequest("POST", btcMarketsOrderCancel, 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)
|
||||
return false, fmt.Errorf("%s Unable to cancel order. Error message: %s", b.GetName(), resp.ErrorMessage)
|
||||
}
|
||||
|
||||
ordersToBeCancelled := len(orderID)
|
||||
@@ -170,39 +178,37 @@ func (b *BTCMarkets) CancelOrder(orderID []int64) (bool, error) {
|
||||
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)
|
||||
log.Printf("%s Unable to cancel order %d. Error message: %s", 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())
|
||||
}
|
||||
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) {
|
||||
// GetOrders returns current order information on the exchange
|
||||
// currency - example "AUD"
|
||||
// instrument - example "BTC"
|
||||
// limit - example "10"
|
||||
// since - since a time example "33434568724"
|
||||
// historic - if false just normal Orders open
|
||||
func (b *BTCMarkets) GetOrders(currency, instrument string, limit, since int64, historic bool) ([]Order, error) {
|
||||
request := make(map[string]interface{})
|
||||
request["currency"] = currency
|
||||
request["instrument"] = instrument
|
||||
request["currency"] = common.StringToUpper(currency)
|
||||
request["instrument"] = common.StringToUpper(instrument)
|
||||
request["limit"] = limit
|
||||
request["since"] = since
|
||||
|
||||
path := BTCMARKETS_ORDER_OPEN
|
||||
path := btcMarketsOrderOpen
|
||||
if historic {
|
||||
path = BTCMARKETS_ORDER_HISTORY
|
||||
path = btcMarketsOrderHistory
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
Orders []BTCMarketsOrder `json:"orders"`
|
||||
}
|
||||
resp := Response{}
|
||||
|
||||
resp := response{}
|
||||
err := b.SendAuthenticatedRequest("POST", path, request, &resp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -212,36 +218,31 @@ func (b *BTCMarkets) GetOrders(currency, instrument string, limit, since int64,
|
||||
}
|
||||
|
||||
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
|
||||
resp.Orders[i].Price = resp.Orders[i].Price / common.SatoshisPerBTC
|
||||
resp.Orders[i].OpenVolume = resp.Orders[i].OpenVolume / common.SatoshisPerBTC
|
||||
resp.Orders[i].Volume = resp.Orders[i].Volume / common.SatoshisPerBTC
|
||||
|
||||
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
|
||||
resp.Orders[i].Trades[x].Fee = resp.Orders[i].Trades[x].Fee / common.SatoshisPerBTC
|
||||
resp.Orders[i].Trades[x].Price = resp.Orders[i].Trades[x].Price / common.SatoshisPerBTC
|
||||
resp.Orders[i].Trades[x].Volume = resp.Orders[i].Trades[x].Volume / common.SatoshisPerBTC
|
||||
}
|
||||
}
|
||||
return resp.Orders, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetOrderDetail(orderID []int64) ([]BTCMarketsOrder, error) {
|
||||
// GetOrderDetail returns order information an a specific order
|
||||
// orderID - example "1337"
|
||||
func (b *BTCMarkets) GetOrderDetail(orderID []int64) ([]Order, 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)
|
||||
resp := Response{}
|
||||
|
||||
err := b.SendAuthenticatedRequest("POST", btcMarketsOrderDetail, orders, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -251,39 +252,94 @@ func (b *BTCMarkets) GetOrderDetail(orderID []int64) ([]BTCMarketsOrder, error)
|
||||
}
|
||||
|
||||
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
|
||||
resp.Orders[i].Price = resp.Orders[i].Price / common.SatoshisPerBTC
|
||||
resp.Orders[i].OpenVolume = resp.Orders[i].OpenVolume / common.SatoshisPerBTC
|
||||
resp.Orders[i].Volume = resp.Orders[i].Volume / common.SatoshisPerBTC
|
||||
|
||||
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
|
||||
resp.Orders[i].Trades[x].Fee = resp.Orders[i].Trades[x].Fee / common.SatoshisPerBTC
|
||||
resp.Orders[i].Trades[x].Price = resp.Orders[i].Trades[x].Price / common.SatoshisPerBTC
|
||||
resp.Orders[i].Trades[x].Volume = resp.Orders[i].Trades[x].Volume / common.SatoshisPerBTC
|
||||
}
|
||||
}
|
||||
return resp.Orders, nil
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetAccountBalance() ([]BTCMarketsAccountBalance, error) {
|
||||
balance := []BTCMarketsAccountBalance{}
|
||||
err := b.SendAuthenticatedRequest("GET", BTCMARKETS_ACCOUNT_BALANCE, nil, &balance)
|
||||
// GetAccountBalance returns the full account balance
|
||||
func (b *BTCMarkets) GetAccountBalance() ([]AccountBalance, error) {
|
||||
balance := []AccountBalance{}
|
||||
|
||||
err := b.SendAuthenticatedRequest("GET", btcMarketsAccountBalance, nil, &balance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// All values are returned in Satoshis, even for fiat currencies.
|
||||
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
|
||||
}
|
||||
balance[i].Balance = balance[i].Balance / common.SatoshisPerBTC
|
||||
balance[i].PendingFunds = balance[i].PendingFunds / common.SatoshisPerBTC
|
||||
}
|
||||
return balance, nil
|
||||
}
|
||||
|
||||
// WithdrawCrypto withdraws cryptocurrency into a designated address
|
||||
func (b *BTCMarkets) WithdrawCrypto(amount int64, currency, address string) (string, error) {
|
||||
req := WithdrawRequestCrypto{
|
||||
Amount: amount,
|
||||
Currency: common.StringToUpper(currency),
|
||||
Address: address,
|
||||
}
|
||||
|
||||
resp := Response{}
|
||||
err := b.SendAuthenticatedRequest("POST", btcMarketsWithdrawCrypto, req, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return "", errors.New(resp.ErrorMessage)
|
||||
}
|
||||
|
||||
return resp.Status, nil
|
||||
}
|
||||
|
||||
// WithdrawAUD withdraws AUD into a designated bank address
|
||||
// Does not return a TxID!
|
||||
func (b *BTCMarkets) WithdrawAUD(accountName, accountNumber, bankName, bsbNumber, currency string, amount int64) (string, error) {
|
||||
req := WithdrawRequestAUD{
|
||||
AccountName: accountName,
|
||||
AccountNumber: accountNumber,
|
||||
BankName: bankName,
|
||||
BSBNumber: bsbNumber,
|
||||
Amount: amount,
|
||||
Currency: common.StringToUpper(currency),
|
||||
}
|
||||
|
||||
resp := Response{}
|
||||
err := b.SendAuthenticatedRequest("POST", btcMarketsWithdrawAud, req, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return "", errors.New(resp.ErrorMessage)
|
||||
}
|
||||
|
||||
return resp.Status, nil
|
||||
}
|
||||
|
||||
// SendAuthenticatedRequest sends an authenticated HTTP request
|
||||
func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interface{}, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
|
||||
request := ""
|
||||
if !b.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
||||
}
|
||||
|
||||
if b.Nonce.Get() == 0 {
|
||||
b.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
b.Nonce.Inc()
|
||||
}
|
||||
var request string
|
||||
payload := []byte("")
|
||||
|
||||
if data != nil {
|
||||
@@ -291,15 +347,15 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interfa
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request = path + "\n" + nonce + "\n" + string(payload)
|
||||
request = path + "\n" + b.Nonce.String()[0:13] + "\n" + string(payload)
|
||||
} else {
|
||||
request = path + "\n" + nonce + "\n"
|
||||
request = path + "\n" + b.Nonce.String()[0:13] + "\n"
|
||||
}
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(request), []byte(b.APISecret))
|
||||
hmac := common.GetHMAC(common.HashSHA512, []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)
|
||||
log.Printf("Sending %s request to URL %s with params %s\n", reqType, btcMarketsAPIURL+path, request)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
@@ -307,17 +363,17 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interfa
|
||||
headers["Accept-Charset"] = "UTF-8"
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["apikey"] = b.APIKey
|
||||
headers["timestamp"] = nonce
|
||||
headers["timestamp"] = b.Nonce.String()[0:13]
|
||||
headers["signature"] = common.Base64Encode(hmac)
|
||||
|
||||
resp, err := common.SendHTTPRequest(reqType, BTCMARKETS_API_URL+path, headers, bytes.NewBuffer(payload))
|
||||
resp, err := common.SendHTTPRequest(reqType, btcMarketsAPIURL+path, headers, bytes.NewBuffer(payload))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
log.Printf("Received raw: %s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
147
exchanges/btcmarkets/btcmarkets_test.go
Normal file
147
exchanges/btcmarkets/btcmarkets_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package btcmarkets
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
var bm BTCMarkets
|
||||
|
||||
// Please supply your own keys here to do better tests
|
||||
const (
|
||||
apiKey = ""
|
||||
apiSecret = ""
|
||||
)
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
bm.SetDefaults()
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := BTCMarkets{}
|
||||
b.Name = "BTC Markets"
|
||||
cfg := config.GetConfig()
|
||||
cfg.LoadConfig("../../testdata/configtest.dat")
|
||||
bConfig, err := cfg.GetExchangeConfig("BTC Markets")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - BTC Markets Setup() init error")
|
||||
}
|
||||
|
||||
b.SetDefaults()
|
||||
b.Setup(bConfig)
|
||||
|
||||
if !b.IsEnabled() || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) ||
|
||||
b.Verbose || b.Websocket || len(b.BaseCurrencies) < 1 ||
|
||||
len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 {
|
||||
t.Error("Test Failed - BTC Markets Setup values not set correctly")
|
||||
}
|
||||
|
||||
bConfig.Enabled = false
|
||||
b.Setup(bConfig)
|
||||
|
||||
if b.IsEnabled() {
|
||||
t.Error("Test failed - BTC Markets TestSetup incorrect value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFee(t *testing.T) {
|
||||
t.Parallel()
|
||||
if fee := bm.GetFee(); fee == 0 {
|
||||
t.Error("Test failed - GetFee() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.GetTicker("BTC")
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetTicker() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.GetOrderbook("BTC")
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetOrderbook() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.GetTrades("BTC", nil)
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetTrades() error", err)
|
||||
}
|
||||
|
||||
val := url.Values{}
|
||||
val.Set("since", "0")
|
||||
_, err = bm.GetTrades("BTC", val)
|
||||
if err != nil {
|
||||
t.Error("Test failed - GetTrades() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.NewOrder("AUD", "BTC", 0, 0, "Bid", "limit", "testTest")
|
||||
if err == nil {
|
||||
t.Error("Test failed - NewOrder() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.CancelOrder([]int64{1337})
|
||||
if err == nil {
|
||||
t.Error("Test failed - CancelOrder() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.GetOrders("AUD", "BTC", 10, 0, false)
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetOrders() error", err)
|
||||
}
|
||||
_, err = bm.GetOrders("AUD", "BTC", 10, 0, true)
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetOrders() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderDetail(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.GetOrderDetail([]int64{1337})
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetOrderDetail() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountBalance(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.GetAccountBalance()
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetAccountBalance() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawCrypto(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.WithdrawCrypto(0, "BTC", "LOLOLOL")
|
||||
if err == nil {
|
||||
t.Error("Test failed - WithdrawCrypto() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawAUD(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := bm.WithdrawAUD("BLA", "1337", "blawest", "1336", "BTC", 10000000)
|
||||
if err == nil {
|
||||
t.Error("Test failed - WithdrawAUD() error", err)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,35 @@
|
||||
package btcmarkets
|
||||
|
||||
type BTCMarketsTicker struct {
|
||||
BestBID float64
|
||||
BestAsk float64
|
||||
LastPrice float64
|
||||
Currency string
|
||||
Instrument string
|
||||
Timestamp int64
|
||||
// Response is the genralized response type
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ID int `json:"id"`
|
||||
Responses []struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
ClientRequestID string `json:"clientRequestId"`
|
||||
Orders []Order `json:"orders"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type BTCMarketsTrade struct {
|
||||
TradeID int64 `json:"tid"`
|
||||
Amount float64 `json:"amount"`
|
||||
Price float64 `json:"price"`
|
||||
Date int64 `json:"date"`
|
||||
// Ticker holds ticker information
|
||||
type Ticker struct {
|
||||
BestBID float64 `json:"bestBid"`
|
||||
BestAsk float64 `json:"bestAsk"`
|
||||
LastPrice float64 `json:"lastPrice"`
|
||||
Currency string `json:"currency"`
|
||||
Instrument string `json:"instrument"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Volume float64 `json:"volume24h"`
|
||||
}
|
||||
|
||||
type BTCMarketsOrderbook struct {
|
||||
// Orderbook holds current orderbook information returned from the exchange
|
||||
type Orderbook struct {
|
||||
Currency string `json:"currency"`
|
||||
Instrument string `json:"instrument"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
@@ -24,7 +37,44 @@ type BTCMarketsOrderbook struct {
|
||||
Bids [][]float64 `json:"bids"`
|
||||
}
|
||||
|
||||
type BTCMarketsTradeResponse struct {
|
||||
// Trade holds trade information
|
||||
type Trade struct {
|
||||
TradeID int64 `json:"tid"`
|
||||
Amount float64 `json:"amount"`
|
||||
Price float64 `json:"price"`
|
||||
Date int64 `json:"date"`
|
||||
}
|
||||
|
||||
// OrderToGo holds order information to be sent to the exchange
|
||||
type OrderToGo 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 holds order information
|
||||
type Order 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 []TradeResponse `json:"trades"`
|
||||
}
|
||||
|
||||
// TradeResponse holds trade information
|
||||
type TradeResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
CreationTime float64 `json:"creationTime"`
|
||||
Description string `json:"description"`
|
||||
@@ -33,24 +83,26 @@ type BTCMarketsTradeResponse struct {
|
||||
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 {
|
||||
// AccountBalance holds account balance details
|
||||
type AccountBalance struct {
|
||||
Balance float64 `json:"balance"`
|
||||
PendingFunds float64 `json:"pendingFunds"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
// WithdrawRequestCrypto is a generalized withdraw request type
|
||||
type WithdrawRequestCrypto struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// WithdrawRequestAUD is a generalized withdraw request type
|
||||
type WithdrawRequestAUD struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
AccountName string `json:"accountName"`
|
||||
AccountNumber string `json:"accountNumber"`
|
||||
BankName string `json:"bankName"`
|
||||
BSBNumber string `json:"bsbNumber"`
|
||||
}
|
||||
|
||||
@@ -2,53 +2,57 @@ package btcmarkets
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the BTC Markets go routine
|
||||
func (b *BTCMarkets) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
// Run implements the BTC Markets wrapper
|
||||
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 {
|
||||
curr := pair.NewCurrencyPair(x, "AUD")
|
||||
go func() {
|
||||
ticker, err := b.GetTickerPrice(curr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
BTCMarketsLastUSD, _ := currency.ConvertCurrency(ticker.Last, "AUD", "USD")
|
||||
BTCMarketsBestBidUSD, _ := currency.ConvertCurrency(ticker.Bid, "AUD", "USD")
|
||||
BTCMarketsBestAskUSD, _ := currency.ConvertCurrency(ticker.Ask, "AUD", "USD")
|
||||
log.Printf("BTC Markets %s: Last %f (%f) Bid %f (%f) Ask %f (%f)\n", curr.Pair().String(), BTCMarketsLastUSD, ticker.Last, BTCMarketsBestBidUSD, ticker.Bid, BTCMarketsBestAskUSD, ticker.Ask)
|
||||
stats.AddExchangeInfo(b.GetName(), curr.GetFirstCurrency().String(), curr.GetSecondCurrency().String(), ticker.Last, 0)
|
||||
stats.AddExchangeInfo(b.GetName(), curr.GetFirstCurrency().String(), "USD", BTCMarketsLastUSD, 0)
|
||||
}()
|
||||
if !common.DataContains(b.EnabledPairs, "AUD") || !common.DataContains(b.EnabledPairs, "AUD") {
|
||||
enabledPairs := []string{}
|
||||
for x := range b.EnabledPairs {
|
||||
enabledPairs = append(enabledPairs, b.EnabledPairs[x]+"AUD")
|
||||
}
|
||||
|
||||
availablePairs := []string{}
|
||||
for x := range b.AvailablePairs {
|
||||
availablePairs = append(availablePairs, b.AvailablePairs[x]+"AUD")
|
||||
}
|
||||
|
||||
log.Println("BTCMarkets: Upgrading available and enabled pairs")
|
||||
|
||||
err := b.UpdateEnabledCurrencies(enabledPairs, true)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", b.GetName())
|
||||
return
|
||||
}
|
||||
|
||||
err = b.UpdateAvailableCurrencies(availablePairs, true)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", b.GetName())
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Second * b.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (b *BTCMarkets) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := b.GetTicker(p.GetFirstCurrency().String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
@@ -57,47 +61,61 @@ func (b *BTCMarkets) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, er
|
||||
tickerPrice.Ask = tick.BestAsk
|
||||
tickerPrice.Bid = tick.BestBID
|
||||
tickerPrice.Last = tick.LastPrice
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (b *BTCMarkets) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (b *BTCMarkets) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return b.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (b *BTCMarkets) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return b.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (b *BTCMarkets) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := b.GetOrderbook(p.GetFirstCurrency().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// BTCMarkets exchange
|
||||
func (b *BTCMarkets) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = b.GetName()
|
||||
accountBalance, err := b.GetAccountBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Balance
|
||||
exchangeCurrency.Hold = accountBalance[i].PendingFunds
|
||||
|
||||
@@ -3,6 +3,7 @@ package coinut
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
@@ -10,11 +11,12 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
COINUT_API_URL = "https://api.coinut.com"
|
||||
COINUT_API_VERISON = "1"
|
||||
COINUT_API_VERSION = "1"
|
||||
COINUT_INSTRUMENTS = "inst_list"
|
||||
COINUT_TICKER = "inst_tick"
|
||||
COINUT_ORDERBOOK = "inst_order_book"
|
||||
@@ -33,7 +35,7 @@ const (
|
||||
)
|
||||
|
||||
type COINUT struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
WebsocketConn *websocket.Conn
|
||||
InstrumentMap map[string]int
|
||||
}
|
||||
@@ -47,6 +49,11 @@ func (c *COINUT) SetDefaults() {
|
||||
c.Verbose = false
|
||||
c.Websocket = false
|
||||
c.RESTPollingDelay = 10
|
||||
c.RequestCurrencyPairFormat.Delimiter = ""
|
||||
c.RequestCurrencyPairFormat.Uppercase = true
|
||||
c.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
c.ConfigCurrencyPairFormat.Uppercase = true
|
||||
c.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (c *COINUT) Setup(exch config.ExchangeConfig) {
|
||||
@@ -62,6 +69,14 @@ func (c *COINUT) Setup(exch config.ExchangeConfig) {
|
||||
c.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
c.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
c.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := c.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = c.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,13 +286,21 @@ func (c *COINUT) GetOpenPosition(instrumentID int) ([]CoinutOpenPosition, error)
|
||||
//to-do: user position update via websocket
|
||||
|
||||
func (c *COINUT) SendAuthenticatedHTTPRequest(apiRequest string, params map[string]interface{}, result interface{}) (err error) {
|
||||
timestamp := time.Now().Unix()
|
||||
if !c.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, c.Name)
|
||||
}
|
||||
|
||||
if c.Nonce.Get() == 0 {
|
||||
c.Nonce.Set(time.Now().Unix())
|
||||
} else {
|
||||
c.Nonce.Inc()
|
||||
}
|
||||
payload := []byte("")
|
||||
|
||||
if params == nil {
|
||||
params = map[string]interface{}{}
|
||||
}
|
||||
params["nonce"] = timestamp
|
||||
params["nonce"] = c.Nonce.Get()
|
||||
params["request"] = apiRequest
|
||||
|
||||
payload, err = common.JSONEncode(params)
|
||||
@@ -290,7 +313,7 @@ func (c *COINUT) SendAuthenticatedHTTPRequest(apiRequest string, params map[stri
|
||||
log.Printf("Request JSON: %s\n", payload)
|
||||
}
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA256, []byte(payload), []byte(c.APIKey))
|
||||
hmac := common.GetHMAC(common.HashSHA256, []byte(payload), []byte(c.APIKey))
|
||||
headers := make(map[string]string)
|
||||
headers["X-USER"] = c.ClientID
|
||||
headers["X-SIGNATURE"] = common.HexEncodeToString(hmac)
|
||||
@@ -299,7 +322,7 @@ func (c *COINUT) SendAuthenticatedHTTPRequest(apiRequest string, params map[stri
|
||||
resp, err := common.SendHTTPRequest("POST", COINUT_API_URL, headers, bytes.NewBuffer(payload))
|
||||
|
||||
if c.Verbose {
|
||||
log.Printf("Recieved raw: \n%s", resp)
|
||||
log.Printf("Received raw: \n%s", resp)
|
||||
}
|
||||
|
||||
genResp := CoinutGenericResponse{}
|
||||
@@ -307,17 +330,17 @@ func (c *COINUT) SendAuthenticatedHTTPRequest(apiRequest string, params map[stri
|
||||
err = common.JSONDecode([]byte(resp), &genResp)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal generic response.")
|
||||
return errors.New("unable to JSON Unmarshal generic response")
|
||||
}
|
||||
|
||||
if genResp.Status[0] != "OK" {
|
||||
return errors.New("Status is not OK.")
|
||||
return errors.New("status is not OK")
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
return errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -34,7 +34,7 @@ type CoinutTicker struct {
|
||||
type CoinutOrderbookBase struct {
|
||||
Count int `json:"count"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Quantity float64 `json:"qty,string"`
|
||||
}
|
||||
|
||||
type CoinutOrderbook struct {
|
||||
|
||||
@@ -2,20 +2,20 @@ package coinut
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the COINUT go routine
|
||||
func (c *COINUT) Start() {
|
||||
go c.Run()
|
||||
}
|
||||
|
||||
// Run implements the COINUT wrapper
|
||||
func (c *COINUT) Run() {
|
||||
if c.Verbose {
|
||||
log.Printf("%s Websocket: %s. (url: %s).\n", c.GetName(), common.IsEnabled(c.Websocket), COINUT_WEBSOCKET_URL)
|
||||
@@ -40,31 +40,16 @@ func (c *COINUT) Run() {
|
||||
currencies = append(currencies, x)
|
||||
}
|
||||
|
||||
err = c.UpdateAvailableCurrencies(currencies)
|
||||
err = c.UpdateAvailableCurrencies(currencies, false)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", c.GetName())
|
||||
}
|
||||
|
||||
for c.Enabled {
|
||||
for _, x := range c.EnabledPairs {
|
||||
currency := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
go func() {
|
||||
ticker, err := c.GetTickerPrice(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("COINUT %s: Last %f High %f Low %f Volume %f\n", currency.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(c.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * c.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the COINUT exchange
|
||||
func (e *COINUT) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// COINUT exchange
|
||||
func (c *COINUT) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
/*
|
||||
response.ExchangeName = e.GetName()
|
||||
accountBalance, err := e.GetAccounts()
|
||||
@@ -72,7 +57,7 @@ func (e *COINUT) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error)
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Available
|
||||
exchangeCurrency.Hold = accountBalance[i].Hold
|
||||
@@ -83,16 +68,12 @@ func (e *COINUT) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *COINUT) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(c.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (c *COINUT) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := c.GetInstrumentTicker(c.InstrumentMap[p.Pair().String()])
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
return ticker.Price{}, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
@@ -100,30 +81,45 @@ func (c *COINUT) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error)
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.High = tick.HighestBuy
|
||||
tickerPrice.Low = tick.LowestSell
|
||||
ticker.ProcessTicker(c.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(c.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(c.Name, p, assetType)
|
||||
|
||||
}
|
||||
|
||||
func (c *COINUT) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(c.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (c *COINUT) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(c.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return c.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (c *COINUT) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(c.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return c.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (c *COINUT) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := c.GetInstrumentOrderbook(c.InstrumentMap[p.Pair().String()], 200)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Buy {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: orderbookNew.Buy[x].Quantity, Price: orderbookNew.Buy[x].Price})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Buy[x].Quantity, Price: orderbookNew.Buy[x].Price})
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Sell {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: orderbookNew.Sell[x].Quantity, Price: orderbookNew.Sell[x].Price})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Sell[x].Quantity, Price: orderbookNew.Sell[x].Price})
|
||||
}
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(c.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
|
||||
orderbook.ProcessOrderbook(c.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(c.Name, p, assetType)
|
||||
}
|
||||
|
||||
@@ -7,29 +7,36 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/nonce"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
WarningBase64DecryptSecretKeyFailed = "WARNING -- Exchange %s unable to base64 decode secret key.. Disabling Authenticated API support."
|
||||
ErrExchangeNotFound = "Exchange not found in dataset."
|
||||
warningBase64DecryptSecretKeyFailed = "WARNING -- Exchange %s unable to base64 decode secret key.. Disabling Authenticated API support."
|
||||
|
||||
// WarningAuthenticatedRequestWithoutCredentialsSet error message for authenticated request without credentails set
|
||||
WarningAuthenticatedRequestWithoutCredentialsSet = "WARNING -- Exchange %s authenticated HTTP request called but not supported due to unset/default API keys."
|
||||
// ErrExchangeNotFound is a constant for an error message
|
||||
ErrExchangeNotFound = "Exchange not found in dataset."
|
||||
)
|
||||
|
||||
//ExchangeAccountInfo : Generic type to hold each exchange's holdings in all enabled currencies
|
||||
type ExchangeAccountInfo struct {
|
||||
// AccountInfo is a Generic type to hold each exchange's holdings in
|
||||
// all enabled currencies
|
||||
type AccountInfo struct {
|
||||
ExchangeName string
|
||||
Currencies []ExchangeAccountCurrencyInfo
|
||||
Currencies []AccountCurrencyInfo
|
||||
}
|
||||
|
||||
//ExchangeAccountCurrencyInfo : Sub type to store currency name and value
|
||||
type ExchangeAccountCurrencyInfo struct {
|
||||
// AccountCurrencyInfo is a sub type to store currency name and value
|
||||
type AccountCurrencyInfo struct {
|
||||
CurrencyName string
|
||||
TotalValue float64
|
||||
Hold float64
|
||||
}
|
||||
|
||||
type ExchangeBase struct {
|
||||
// Base stores the individual exchange information
|
||||
type Base struct {
|
||||
Name string
|
||||
Enabled bool
|
||||
Verbose bool
|
||||
@@ -37,42 +44,279 @@ type ExchangeBase struct {
|
||||
RESTPollingDelay time.Duration
|
||||
AuthenticatedAPISupport bool
|
||||
APISecret, APIKey, ClientID string
|
||||
Nonce nonce.Nonce
|
||||
TakerFee, MakerFee, Fee float64
|
||||
BaseCurrencies []string
|
||||
AvailablePairs []string
|
||||
EnabledPairs []string
|
||||
AssetTypes []string
|
||||
WebsocketURL string
|
||||
APIUrl string
|
||||
RequestCurrencyPairFormat config.CurrencyPairFormatConfig
|
||||
ConfigCurrencyPairFormat config.CurrencyPairFormatConfig
|
||||
}
|
||||
|
||||
//IBotExchange : Enforces standard functions for all exchanges supported in gocryptotrader
|
||||
// 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 pair.CurrencyPair) (ticker.TickerPrice, error)
|
||||
GetOrderbookEx(currency pair.CurrencyPair) (orderbook.OrderbookBase, error)
|
||||
GetEnabledCurrencies() []string
|
||||
GetExchangeAccountInfo() (ExchangeAccountInfo, error)
|
||||
GetTickerPrice(currency pair.CurrencyPair, assetType string) (ticker.Price, error)
|
||||
UpdateTicker(currency pair.CurrencyPair, assetType string) (ticker.Price, error)
|
||||
GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error)
|
||||
UpdateOrderbook(currency pair.CurrencyPair, assetType string) (orderbook.Base, error)
|
||||
GetEnabledCurrencies() []pair.CurrencyPair
|
||||
GetExchangeAccountInfo() (AccountInfo, error)
|
||||
GetAuthenticatedAPISupport() bool
|
||||
}
|
||||
|
||||
func (e *ExchangeBase) GetName() string {
|
||||
// SetAssetTypes checks the exchange asset types (whether it supports SPOT,
|
||||
// Binary or Futures) and sets it to a default setting if it doesn't exist
|
||||
func (e *Base) SetAssetTypes() error {
|
||||
cfg := config.GetConfig()
|
||||
exch, err := cfg.GetExchangeConfig(e.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
update := false
|
||||
if exch.AssetTypes == "" {
|
||||
exch.AssetTypes = common.JoinStrings(e.AssetTypes, ",")
|
||||
update = true
|
||||
} else {
|
||||
e.AssetTypes = common.SplitStrings(exch.AssetTypes, ",")
|
||||
}
|
||||
|
||||
if update {
|
||||
return cfg.UpdateExchangeConfig(exch)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExchangeAssetTypes returns the asset types the exchange supports (SPOT,
|
||||
// binary, futures)
|
||||
func GetExchangeAssetTypes(exchName string) ([]string, error) {
|
||||
cfg := config.GetConfig()
|
||||
exch, err := cfg.GetExchangeConfig(exchName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return common.SplitStrings(exch.AssetTypes, ","), nil
|
||||
}
|
||||
|
||||
// SetCurrencyPairFormat checks the exchange request and config currency pair
|
||||
// formats and sets it to a default setting if it doesn't exist
|
||||
func (e *Base) SetCurrencyPairFormat() error {
|
||||
cfg := config.GetConfig()
|
||||
exch, err := cfg.GetExchangeConfig(e.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
update := false
|
||||
if exch.RequestCurrencyPairFormat == nil {
|
||||
exch.RequestCurrencyPairFormat = &config.CurrencyPairFormatConfig{
|
||||
Delimiter: e.RequestCurrencyPairFormat.Delimiter,
|
||||
Uppercase: e.RequestCurrencyPairFormat.Uppercase,
|
||||
Separator: e.RequestCurrencyPairFormat.Separator,
|
||||
Index: e.RequestCurrencyPairFormat.Index,
|
||||
}
|
||||
update = true
|
||||
} else {
|
||||
e.RequestCurrencyPairFormat = *exch.RequestCurrencyPairFormat
|
||||
}
|
||||
|
||||
if exch.ConfigCurrencyPairFormat == nil {
|
||||
exch.ConfigCurrencyPairFormat = &config.CurrencyPairFormatConfig{
|
||||
Delimiter: e.ConfigCurrencyPairFormat.Delimiter,
|
||||
Uppercase: e.ConfigCurrencyPairFormat.Uppercase,
|
||||
Separator: e.ConfigCurrencyPairFormat.Separator,
|
||||
Index: e.ConfigCurrencyPairFormat.Index,
|
||||
}
|
||||
update = true
|
||||
} else {
|
||||
e.ConfigCurrencyPairFormat = *exch.ConfigCurrencyPairFormat
|
||||
}
|
||||
|
||||
if update {
|
||||
return cfg.UpdateExchangeConfig(exch)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAuthenticatedAPISupport returns whether the exchange supports
|
||||
// authenticated API requests
|
||||
func (e *Base) GetAuthenticatedAPISupport() bool {
|
||||
return e.AuthenticatedAPISupport
|
||||
}
|
||||
|
||||
// GetName is a method that returns the name of the exchange base
|
||||
func (e *Base) GetName() string {
|
||||
return e.Name
|
||||
}
|
||||
func (e *ExchangeBase) GetEnabledCurrencies() []string {
|
||||
return e.EnabledPairs
|
||||
|
||||
// GetEnabledCurrencies is a method that returns the enabled currency pairs of
|
||||
// the exchange base
|
||||
func (e *Base) GetEnabledCurrencies() []pair.CurrencyPair {
|
||||
var pairs []pair.CurrencyPair
|
||||
for x := range e.EnabledPairs {
|
||||
var currencyPair pair.CurrencyPair
|
||||
if e.RequestCurrencyPairFormat.Delimiter != "" {
|
||||
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
||||
if e.ConfigCurrencyPairFormat.Delimiter == e.RequestCurrencyPairFormat.Delimiter {
|
||||
currencyPair = pair.NewCurrencyPairDelimiter(e.EnabledPairs[x],
|
||||
e.RequestCurrencyPairFormat.Delimiter)
|
||||
} else {
|
||||
currencyPair = pair.NewCurrencyPairDelimiter(e.EnabledPairs[x],
|
||||
e.ConfigCurrencyPairFormat.Delimiter)
|
||||
currencyPair.Delimiter = "-"
|
||||
}
|
||||
} else {
|
||||
if e.ConfigCurrencyPairFormat.Index != "" {
|
||||
currencyPair = pair.NewCurrencyPairFromIndex(e.EnabledPairs[x],
|
||||
e.ConfigCurrencyPairFormat.Index)
|
||||
} else {
|
||||
currencyPair = pair.NewCurrencyPair(e.EnabledPairs[x][0:3],
|
||||
e.EnabledPairs[x][3:])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
||||
currencyPair = pair.NewCurrencyPairDelimiter(e.EnabledPairs[x],
|
||||
e.ConfigCurrencyPairFormat.Delimiter)
|
||||
} else {
|
||||
if e.ConfigCurrencyPairFormat.Index != "" {
|
||||
currencyPair = pair.NewCurrencyPairFromIndex(e.EnabledPairs[x],
|
||||
e.ConfigCurrencyPairFormat.Index)
|
||||
} else {
|
||||
currencyPair = pair.NewCurrencyPair(e.EnabledPairs[x][0:3],
|
||||
e.EnabledPairs[x][3:])
|
||||
}
|
||||
}
|
||||
}
|
||||
pairs = append(pairs, currencyPair)
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
func (e *ExchangeBase) SetEnabled(enabled bool) {
|
||||
|
||||
// GetAvailableCurrencies is a method that returns the available currency pairs
|
||||
// of the exchange base
|
||||
func (e *Base) GetAvailableCurrencies() []pair.CurrencyPair {
|
||||
var pairs []pair.CurrencyPair
|
||||
for x := range e.AvailablePairs {
|
||||
var currencyPair pair.CurrencyPair
|
||||
if e.RequestCurrencyPairFormat.Delimiter != "" {
|
||||
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
||||
if e.ConfigCurrencyPairFormat.Delimiter == e.RequestCurrencyPairFormat.Delimiter {
|
||||
currencyPair = pair.NewCurrencyPairDelimiter(e.AvailablePairs[x],
|
||||
e.RequestCurrencyPairFormat.Delimiter)
|
||||
} else {
|
||||
currencyPair = pair.NewCurrencyPairDelimiter(e.AvailablePairs[x],
|
||||
e.ConfigCurrencyPairFormat.Delimiter)
|
||||
currencyPair.Delimiter = "-"
|
||||
}
|
||||
} else {
|
||||
if e.ConfigCurrencyPairFormat.Index != "" {
|
||||
currencyPair = pair.NewCurrencyPairFromIndex(e.AvailablePairs[x],
|
||||
e.ConfigCurrencyPairFormat.Index)
|
||||
} else {
|
||||
currencyPair = pair.NewCurrencyPair(e.AvailablePairs[x][0:3],
|
||||
e.AvailablePairs[x][3:])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
||||
currencyPair = pair.NewCurrencyPairDelimiter(e.AvailablePairs[x],
|
||||
e.ConfigCurrencyPairFormat.Delimiter)
|
||||
} else {
|
||||
if e.ConfigCurrencyPairFormat.Index != "" {
|
||||
currencyPair = pair.NewCurrencyPairFromIndex(e.AvailablePairs[x],
|
||||
e.ConfigCurrencyPairFormat.Index)
|
||||
} else {
|
||||
currencyPair = pair.NewCurrencyPair(e.AvailablePairs[x][0:3],
|
||||
e.AvailablePairs[x][3:])
|
||||
}
|
||||
}
|
||||
}
|
||||
pairs = append(pairs, currencyPair)
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
// GetExchangeFormatCurrencySeperator returns whether or not a specific
|
||||
// exchange contains a separator used for API requests
|
||||
func GetExchangeFormatCurrencySeperator(exchName string) bool {
|
||||
cfg := config.GetConfig()
|
||||
exch, err := cfg.GetExchangeConfig(exchName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if exch.RequestCurrencyPairFormat.Separator != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAndFormatExchangeCurrencies returns a pair.CurrencyItem string containing
|
||||
// the exchanges formatted currency pairs
|
||||
func GetAndFormatExchangeCurrencies(exchName string, pairs []pair.CurrencyPair) (pair.CurrencyItem, error) {
|
||||
var currencyItems pair.CurrencyItem
|
||||
cfg := config.GetConfig()
|
||||
exch, err := cfg.GetExchangeConfig(exchName)
|
||||
if err != nil {
|
||||
return currencyItems, err
|
||||
}
|
||||
|
||||
for x := range pairs {
|
||||
currencyItems += FormatExchangeCurrency(exchName, pairs[x])
|
||||
if x == len(pairs)-1 {
|
||||
continue
|
||||
}
|
||||
currencyItems += pair.CurrencyItem(exch.RequestCurrencyPairFormat.Separator)
|
||||
}
|
||||
return currencyItems, nil
|
||||
}
|
||||
|
||||
// FormatExchangeCurrency is a method that formats and returns a currency pair
|
||||
// based on the user currency display preferences
|
||||
func FormatExchangeCurrency(exchName string, p pair.CurrencyPair) pair.CurrencyItem {
|
||||
cfg := config.GetConfig()
|
||||
exch, _ := cfg.GetExchangeConfig(exchName)
|
||||
|
||||
return p.Display(exch.RequestCurrencyPairFormat.Delimiter,
|
||||
exch.RequestCurrencyPairFormat.Uppercase)
|
||||
}
|
||||
|
||||
// FormatCurrency is a method that formats and returns a currency pair
|
||||
// based on the user currency display preferences
|
||||
func FormatCurrency(p pair.CurrencyPair) pair.CurrencyItem {
|
||||
cfg := config.GetConfig()
|
||||
return p.Display(cfg.CurrencyPairFormat.Delimiter,
|
||||
cfg.CurrencyPairFormat.Uppercase)
|
||||
}
|
||||
|
||||
// SetEnabled is a method that sets if the exchange is enabled
|
||||
func (e *Base) SetEnabled(enabled bool) {
|
||||
e.Enabled = enabled
|
||||
}
|
||||
|
||||
func (e *ExchangeBase) IsEnabled() bool {
|
||||
// IsEnabled is a method that returns if the current exchange is enabled
|
||||
func (e *Base) IsEnabled() bool {
|
||||
return e.Enabled
|
||||
}
|
||||
|
||||
func (e *ExchangeBase) SetAPIKeys(APIKey, APISecret, ClientID string, b64Decode bool) {
|
||||
// SetAPIKeys is a method that sets the current API keys for the exchange
|
||||
func (e *Base) SetAPIKeys(APIKey, APISecret, ClientID string, b64Decode bool) {
|
||||
if !e.AuthenticatedAPISupport {
|
||||
return
|
||||
}
|
||||
|
||||
e.APIKey = APIKey
|
||||
e.ClientID = ClientID
|
||||
|
||||
@@ -80,7 +324,7 @@ func (e *ExchangeBase) SetAPIKeys(APIKey, APISecret, ClientID string, b64Decode
|
||||
result, err := common.Base64Decode(APISecret)
|
||||
if err != nil {
|
||||
e.AuthenticatedAPISupport = false
|
||||
log.Printf(WarningBase64DecryptSecretKeyFailed, e.Name)
|
||||
log.Printf(warningBase64DecryptSecretKeyFailed, e.Name)
|
||||
}
|
||||
e.APISecret = string(result)
|
||||
} else {
|
||||
@@ -88,19 +332,50 @@ func (e *ExchangeBase) SetAPIKeys(APIKey, APISecret, ClientID string, b64Decode
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExchangeBase) UpdateAvailableCurrencies(exchangeProducts []string) error {
|
||||
// UpdateEnabledCurrencies is a method that sets new pairs to the current
|
||||
// exchange. Setting force to true upgrades the enabled currencies
|
||||
func (e *Base) UpdateEnabledCurrencies(exchangeProducts []string, force bool) error {
|
||||
exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",")
|
||||
diff := common.StringSliceDifference(e.AvailablePairs, exchangeProducts)
|
||||
if len(diff) > 0 {
|
||||
diff := common.StringSliceDifference(e.EnabledPairs, exchangeProducts)
|
||||
if force || len(diff) > 0 {
|
||||
cfg := config.GetConfig()
|
||||
exch, err := cfg.GetExchangeConfig(e.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if force {
|
||||
log.Printf("%s forced update of enabled pairs.", e.Name)
|
||||
} else {
|
||||
log.Printf("%s Updating available pairs. Difference: %s.\n", e.Name, diff)
|
||||
exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",")
|
||||
cfg.UpdateExchangeConfig(exch)
|
||||
}
|
||||
exch.EnabledPairs = common.JoinStrings(exchangeProducts, ",")
|
||||
e.EnabledPairs = exchangeProducts
|
||||
return cfg.UpdateExchangeConfig(exch)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAvailableCurrencies is a method that sets new pairs to the current
|
||||
// exchange. Setting force to true upgrades the available currencies
|
||||
func (e *Base) UpdateAvailableCurrencies(exchangeProducts []string, force bool) error {
|
||||
exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",")
|
||||
diff := common.StringSliceDifference(e.AvailablePairs, exchangeProducts)
|
||||
if force || len(diff) > 0 {
|
||||
cfg := config.GetConfig()
|
||||
exch, err := cfg.GetExchangeConfig(e.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if force {
|
||||
log.Printf("%s forced update of available pairs.", e.Name)
|
||||
} else {
|
||||
log.Printf("%s Updating available pairs. Difference: %s.\n", e.Name, diff)
|
||||
}
|
||||
exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",")
|
||||
e.AvailablePairs = exchangeProducts
|
||||
return cfg.UpdateExchangeConfig(exch)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,11 +3,159 @@ package exchange
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func TestSetAssetTypes(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetAssetTypes failed to load config file. Error: %s", err)
|
||||
}
|
||||
|
||||
b := Base{
|
||||
Name: "TESTNAME",
|
||||
}
|
||||
|
||||
err = b.SetAssetTypes()
|
||||
if err == nil {
|
||||
t.Fatal("Test failed. TestSetAssetTypes returned nil error for a non-existant exchange")
|
||||
}
|
||||
|
||||
b.Name = "ANX"
|
||||
err = b.SetAssetTypes()
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetAssetTypes. Error %s", err)
|
||||
}
|
||||
|
||||
exch, err := cfg.GetExchangeConfig(b.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetAssetTypes load config failed. Error %s", err)
|
||||
}
|
||||
|
||||
exch.AssetTypes = ""
|
||||
err = cfg.UpdateExchangeConfig(exch)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetAssetTypes update config failed. Error %s", err)
|
||||
}
|
||||
|
||||
exch, err = cfg.GetExchangeConfig(b.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetAssetTypes load config failed. Error %s", err)
|
||||
}
|
||||
|
||||
if exch.AssetTypes != "" {
|
||||
t.Fatal("Test failed. TestSetAssetTypes assetTypes != ''")
|
||||
}
|
||||
|
||||
err = b.SetAssetTypes()
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetAssetTypes. Error %s", err)
|
||||
}
|
||||
|
||||
if !common.DataContains(b.AssetTypes, ticker.Spot) {
|
||||
t.Fatal("Test failed. TestSetAssetTypes assetTypes is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExchangeAssetTypes(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config file. Error: %s", err)
|
||||
}
|
||||
|
||||
result, err := GetExchangeAssetTypes("Bitfinex")
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. Unable to obtain Bitfinex asset types")
|
||||
}
|
||||
|
||||
if !common.DataContains(result, ticker.Spot) {
|
||||
t.Fatal("Test failed. Bitfinex does not contain default asset type 'SPOT'")
|
||||
}
|
||||
|
||||
_, err = GetExchangeAssetTypes("non-existant-exchange")
|
||||
if err == nil {
|
||||
t.Fatal("Test failed. Got asset types for non-existant exchange")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetCurrencyPairFormat(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetCurrencyPairFormat failed to load config file. Error: %s", err)
|
||||
}
|
||||
|
||||
b := Base{
|
||||
Name: "TESTNAME",
|
||||
}
|
||||
|
||||
err = b.SetCurrencyPairFormat()
|
||||
if err == nil {
|
||||
t.Fatal("Test failed. TestSetCurrencyPairFormat returned nil error for a non-existant exchange")
|
||||
}
|
||||
|
||||
b.Name = "ANX"
|
||||
err = b.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetCurrencyPairFormat. Error %s", err)
|
||||
}
|
||||
|
||||
exch, err := cfg.GetExchangeConfig(b.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetCurrencyPairFormat load config failed. Error %s", err)
|
||||
}
|
||||
|
||||
exch.ConfigCurrencyPairFormat = nil
|
||||
exch.RequestCurrencyPairFormat = nil
|
||||
err = cfg.UpdateExchangeConfig(exch)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetCurrencyPairFormat update config failed. Error %s", err)
|
||||
}
|
||||
|
||||
exch, err = cfg.GetExchangeConfig(b.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetCurrencyPairFormat load config failed. Error %s", err)
|
||||
}
|
||||
|
||||
if exch.ConfigCurrencyPairFormat != nil && exch.RequestCurrencyPairFormat != nil {
|
||||
t.Fatal("Test failed. TestSetCurrencyPairFormat exch values are not nil")
|
||||
}
|
||||
|
||||
err = b.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestSetCurrencyPairFormat. Error %s", err)
|
||||
}
|
||||
|
||||
if b.ConfigCurrencyPairFormat.Delimiter != "" &&
|
||||
b.ConfigCurrencyPairFormat.Index != "BTC" &&
|
||||
b.ConfigCurrencyPairFormat.Uppercase {
|
||||
t.Fatal("Test failed. TestSetCurrencyPairFormat ConfigCurrencyPairFormat values are incorrect")
|
||||
}
|
||||
|
||||
if b.RequestCurrencyPairFormat.Delimiter != "" &&
|
||||
b.RequestCurrencyPairFormat.Index != "BTC" &&
|
||||
b.RequestCurrencyPairFormat.Uppercase {
|
||||
t.Fatal("Test failed. TestSetCurrencyPairFormat RequestCurrencyPairFormat values are incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAuthenticatedAPISupport(t *testing.T) {
|
||||
base := Base{
|
||||
AuthenticatedAPISupport: false,
|
||||
}
|
||||
|
||||
if base.GetAuthenticatedAPISupport() {
|
||||
t.Fatal("Test failed. TestGetAuthenticatedAPISupport returned true when it should of been false.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetName(t *testing.T) {
|
||||
GetName := ExchangeBase{
|
||||
GetName := Base{
|
||||
Name: "TESTNAME",
|
||||
}
|
||||
|
||||
@@ -18,20 +166,230 @@ func TestGetName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetEnabledCurrencies(t *testing.T) {
|
||||
enabledPairs := []string{"BTCUSD", "BTCAUD", "LTCUSD", "LTCAUD"}
|
||||
GetEnabledCurrencies := ExchangeBase{
|
||||
Name: "TESTNAME",
|
||||
EnabledPairs: enabledPairs,
|
||||
b := Base{
|
||||
Name: "TESTNAME",
|
||||
}
|
||||
|
||||
enCurr := GetEnabledCurrencies.GetEnabledCurrencies()
|
||||
if enCurr[0] != "BTCUSD" {
|
||||
t.Error("Test Failed - Exchange GetEnabledCurrencies() incorrect string")
|
||||
b.EnabledPairs = []string{"BTC-USD"}
|
||||
format := config.CurrencyPairFormatConfig{
|
||||
Delimiter: "-",
|
||||
Index: "",
|
||||
}
|
||||
|
||||
b.RequestCurrencyPairFormat = format
|
||||
b.ConfigCurrencyPairFormat = format
|
||||
c := b.GetEnabledCurrencies()
|
||||
if c[0].Pair().String() != "BTC-USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
format.Delimiter = "~"
|
||||
b.RequestCurrencyPairFormat = format
|
||||
c = b.GetEnabledCurrencies()
|
||||
if c[0].Pair().String() != "BTC-USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
format.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat = format
|
||||
c = b.GetEnabledCurrencies()
|
||||
if c[0].Pair().String() != "BTC-USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.EnabledPairs = []string{"BTCDOGE"}
|
||||
format.Index = "BTC"
|
||||
b.ConfigCurrencyPairFormat = format
|
||||
c = b.GetEnabledCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.EnabledPairs = []string{"BTC_USD"}
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Delimiter = "_"
|
||||
c = b.GetEnabledCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.EnabledPairs = []string{"BTCDOGE"}
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Index = "BTC"
|
||||
c = b.GetEnabledCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.EnabledPairs = []string{"BTCUSD"}
|
||||
b.ConfigCurrencyPairFormat.Index = ""
|
||||
c = b.GetEnabledCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAvailableCurrencies(t *testing.T) {
|
||||
b := Base{
|
||||
Name: "TESTNAME",
|
||||
}
|
||||
|
||||
b.AvailablePairs = []string{"BTC-USD"}
|
||||
format := config.CurrencyPairFormatConfig{
|
||||
Delimiter: "-",
|
||||
Index: "",
|
||||
}
|
||||
|
||||
b.RequestCurrencyPairFormat = format
|
||||
b.ConfigCurrencyPairFormat = format
|
||||
c := b.GetAvailableCurrencies()
|
||||
if c[0].Pair().String() != "BTC-USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
format.Delimiter = "~"
|
||||
b.RequestCurrencyPairFormat = format
|
||||
c = b.GetAvailableCurrencies()
|
||||
if c[0].Pair().String() != "BTC-USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
format.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat = format
|
||||
c = b.GetAvailableCurrencies()
|
||||
if c[0].Pair().String() != "BTC-USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.AvailablePairs = []string{"BTCDOGE"}
|
||||
format.Index = "BTC"
|
||||
b.ConfigCurrencyPairFormat = format
|
||||
c = b.GetAvailableCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.AvailablePairs = []string{"BTC_USD"}
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Delimiter = "_"
|
||||
c = b.GetAvailableCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.AvailablePairs = []string{"BTCDOGE"}
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Index = "BTC"
|
||||
c = b.GetAvailableCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
|
||||
b.AvailablePairs = []string{"BTCUSD"}
|
||||
b.ConfigCurrencyPairFormat.Index = ""
|
||||
c = b.GetAvailableCurrencies()
|
||||
if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" {
|
||||
t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExchangeFormatCurrencySeperator(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config file. Error: %s", err)
|
||||
}
|
||||
|
||||
expected := true
|
||||
actual := GetExchangeFormatCurrencySeperator("BTCE")
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed - TestGetExchangeFormatCurrencySeperator expected %v != actual %v",
|
||||
expected, actual)
|
||||
}
|
||||
|
||||
expected = false
|
||||
actual = GetExchangeFormatCurrencySeperator("LocalBitcoins")
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed - TestGetExchangeFormatCurrencySeperator expected %v != actual %v",
|
||||
expected, actual)
|
||||
}
|
||||
|
||||
expected = false
|
||||
actual = GetExchangeFormatCurrencySeperator("blah")
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed - TestGetExchangeFormatCurrencySeperator expected %v != actual %v",
|
||||
expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAndFormatExchangeCurrencies(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config file. Error: %s", err)
|
||||
}
|
||||
|
||||
var pairs []pair.CurrencyPair
|
||||
pairs = append(pairs, pair.NewCurrencyPairDelimiter("BTC_USD", "_"))
|
||||
pairs = append(pairs, pair.NewCurrencyPairDelimiter("LTC_BTC", "_"))
|
||||
|
||||
actual, err := GetAndFormatExchangeCurrencies("Liqui", pairs)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed - Exchange TestGetAndFormatExchangeCurrencies error %s", err)
|
||||
}
|
||||
expected := pair.CurrencyItem("btc_usd-ltc_btc")
|
||||
|
||||
if actual.String() != expected.String() {
|
||||
t.Errorf("Test failed - Exchange TestGetAndFormatExchangeCurrencies %s != %s",
|
||||
actual, expected)
|
||||
}
|
||||
|
||||
_, err = GetAndFormatExchangeCurrencies("non-existant", pairs)
|
||||
if err == nil {
|
||||
t.Errorf("Test failed - Exchange TestGetAndFormatExchangeCurrencies returned nil error on non-existant exchange")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatExchangeCurrency(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config file. Error: %s", err)
|
||||
}
|
||||
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
expected := "BTC-USD"
|
||||
actual := FormatExchangeCurrency("GDAX", pair)
|
||||
|
||||
if actual.String() != expected {
|
||||
t.Errorf("Test failed - Exchange TestFormatExchangeCurrency %s != %s",
|
||||
actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatCurrency(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config file. Error: %s", err)
|
||||
}
|
||||
|
||||
currency := pair.NewCurrencyPair("btc", "usd")
|
||||
expected := "BTC-USD"
|
||||
actual := FormatCurrency(currency).String()
|
||||
if actual != expected {
|
||||
t.Errorf("Test failed - Exchange TestFormatCurrency %s != %s",
|
||||
actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEnabled(t *testing.T) {
|
||||
SetEnabled := ExchangeBase{
|
||||
SetEnabled := Base{
|
||||
Name: "TESTNAME",
|
||||
Enabled: false,
|
||||
}
|
||||
@@ -43,7 +401,7 @@ func TestSetEnabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEnabled(t *testing.T) {
|
||||
IsEnabled := ExchangeBase{
|
||||
IsEnabled := Base{
|
||||
Name: "TESTNAME",
|
||||
Enabled: false,
|
||||
}
|
||||
@@ -54,33 +412,99 @@ func TestIsEnabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetAPIKeys(t *testing.T) {
|
||||
SetAPIKeys := ExchangeBase{
|
||||
Name: "TESTNAME",
|
||||
Enabled: false,
|
||||
SetAPIKeys := Base{
|
||||
Name: "TESTNAME",
|
||||
Enabled: false,
|
||||
AuthenticatedAPISupport: false,
|
||||
}
|
||||
|
||||
SetAPIKeys.SetAPIKeys("RocketMan", "Digereedoo", "007", false)
|
||||
if SetAPIKeys.APIKey != "" && SetAPIKeys.APISecret != "" && SetAPIKeys.ClientID != "" {
|
||||
t.Error("Test Failed - SetAPIKeys() set values without authenticated API support enabled")
|
||||
}
|
||||
|
||||
SetAPIKeys.AuthenticatedAPISupport = true
|
||||
SetAPIKeys.SetAPIKeys("RocketMan", "Digereedoo", "007", false)
|
||||
if SetAPIKeys.APIKey != "RocketMan" && SetAPIKeys.APISecret != "Digereedoo" && SetAPIKeys.ClientID != "007" {
|
||||
t.Error("Test Failed - Exchange SetAPIKeys() did not set correct values")
|
||||
}
|
||||
SetAPIKeys.SetAPIKeys("RocketMan", "Digereedoo", "007", true)
|
||||
}
|
||||
|
||||
func TestUpdateEnabledCurrencies(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestUpdateEnabledCurrencies failed to load config")
|
||||
}
|
||||
|
||||
UAC := Base{Name: "ANX"}
|
||||
exchangeProducts := []string{"ltc", "btc", "usd", "aud"}
|
||||
|
||||
// Test updating exchange products for an exchange which doesn't exist
|
||||
UAC.Name = "Blah"
|
||||
err = UAC.UpdateEnabledCurrencies(exchangeProducts, false)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies succeeded on an exchange which doesn't exist")
|
||||
}
|
||||
|
||||
// Test updating exchange products
|
||||
UAC.Name = "ANX"
|
||||
err = UAC.UpdateEnabledCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies error: %s", err)
|
||||
}
|
||||
|
||||
// Test updating the same new products, diff should be 0
|
||||
UAC.Name = "ANX"
|
||||
err = UAC.UpdateEnabledCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies error: %s", err)
|
||||
}
|
||||
|
||||
// Test force updating to only one product
|
||||
exchangeProducts = []string{"btc"}
|
||||
err = UAC.UpdateEnabledCurrencies(exchangeProducts, true)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Forced Exchange TestUpdateEnabledCurrencies error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateAvailableCurrencies(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig(config.CONFIG_TEST_FILE)
|
||||
err := cfg.LoadConfig(config.ConfigTestFile)
|
||||
if err != nil {
|
||||
t.Log("SOMETHING DONE HAPPENED!")
|
||||
t.Fatal("Test failed. TestUpdateAvailableCurrencies failed to load config")
|
||||
}
|
||||
|
||||
UAC := ExchangeBase{
|
||||
Name: "ANX",
|
||||
}
|
||||
UAC := Base{Name: "ANX"}
|
||||
exchangeProducts := []string{"ltc", "btc", "usd", "aud"}
|
||||
|
||||
err2 := UAC.UpdateAvailableCurrencies(exchangeProducts)
|
||||
if err2 != nil {
|
||||
t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err2)
|
||||
// Test updating exchange products for an exchange which doesn't exist
|
||||
UAC.Name = "Blah"
|
||||
err = UAC.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() succeeded on an exchange which doesn't exist")
|
||||
}
|
||||
|
||||
// Test updating exchange products
|
||||
UAC.Name = "ANX"
|
||||
err = UAC.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err)
|
||||
}
|
||||
|
||||
// Test updating the same new products, diff should be 0
|
||||
UAC.Name = "ANX"
|
||||
err = UAC.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err)
|
||||
}
|
||||
|
||||
// Test force updating to only one product
|
||||
exchangeProducts = []string{"btc"}
|
||||
err = UAC.UpdateAvailableCurrencies(exchangeProducts, true)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Forced Exchange UpdateAvailableCurrencies() error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
GDAX_API_URL = "https://api.gdax.com/"
|
||||
GDAX_API_VERISON = "0"
|
||||
GDAX_API_VERSION = "0"
|
||||
GDAX_PRODUCTS = "products"
|
||||
GDAX_ORDERBOOK = "book"
|
||||
GDAX_TICKER = "ticker"
|
||||
@@ -34,7 +35,7 @@ const (
|
||||
)
|
||||
|
||||
type GDAX struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (g *GDAX) SetDefaults() {
|
||||
@@ -46,6 +47,11 @@ func (g *GDAX) SetDefaults() {
|
||||
g.Verbose = false
|
||||
g.Websocket = false
|
||||
g.RESTPollingDelay = 10
|
||||
g.RequestCurrencyPairFormat.Delimiter = "-"
|
||||
g.RequestCurrencyPairFormat.Uppercase = true
|
||||
g.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
g.ConfigCurrencyPairFormat.Uppercase = true
|
||||
g.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (g *GDAX) Setup(exch config.ExchangeConfig) {
|
||||
@@ -61,6 +67,14 @@ func (g *GDAX) Setup(exch config.ExchangeConfig) {
|
||||
g.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
g.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
g.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := g.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = g.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +384,15 @@ func (g *GDAX) GetReportStatus(reportID string) (GDAXReportResponse, error) {
|
||||
}
|
||||
|
||||
func (g *GDAX) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
|
||||
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
if !g.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name)
|
||||
}
|
||||
|
||||
if g.Nonce.Get() == 0 {
|
||||
g.Nonce.Set(time.Now().Unix())
|
||||
} else {
|
||||
g.Nonce.Inc()
|
||||
}
|
||||
|
||||
payload := []byte("")
|
||||
|
||||
@@ -386,11 +408,11 @@ func (g *GDAX) SendAuthenticatedHTTPRequest(method, path string, params map[stri
|
||||
}
|
||||
}
|
||||
|
||||
message := timestamp + method + "/" + path + string(payload)
|
||||
hmac := common.GetHMAC(common.HASH_SHA256, []byte(message), []byte(g.APISecret))
|
||||
message := g.Nonce.String() + method + "/" + path + string(payload)
|
||||
hmac := common.GetHMAC(common.HashSHA256, []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-TIMESTAMP"] = g.Nonce.String()
|
||||
headers["CB-ACCESS-KEY"] = g.APIKey
|
||||
headers["CB-ACCESS-PASSPHRASE"] = g.ClientID
|
||||
headers["Content-Type"] = "application/json"
|
||||
@@ -398,13 +420,13 @@ func (g *GDAX) SendAuthenticatedHTTPRequest(method, path string, params map[stri
|
||||
resp, err := common.SendHTTPRequest(method, GDAX_API_URL+path, headers, bytes.NewBuffer(payload))
|
||||
|
||||
if g.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
return errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -31,13 +31,13 @@ type GDAXOrderL3 struct {
|
||||
|
||||
type GDAXOrderbookL1L2 struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
Bids []GDAXOrderL1L2 `json:"asks"`
|
||||
Bids []GDAXOrderL1L2 `json:"bids"`
|
||||
Asks []GDAXOrderL1L2 `json:"asks"`
|
||||
}
|
||||
|
||||
type GDAXOrderbookL3 struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
Bids []GDAXOrderL3 `json:"asks"`
|
||||
Bids []GDAXOrderL3 `json:"bids"`
|
||||
Asks []GDAXOrderL3 `json:"asks"`
|
||||
}
|
||||
|
||||
|
||||
@@ -2,20 +2,20 @@ package gdax
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the GDAX go routine
|
||||
func (g *GDAX) Start() {
|
||||
go g.Run()
|
||||
}
|
||||
|
||||
// Run implements the GDAX wrapper
|
||||
func (g *GDAX) Run() {
|
||||
if g.Verbose {
|
||||
log.Printf("%s Websocket: %s. (url: %s).\n", g.GetName(), common.IsEnabled(g.Websocket), GDAX_WEBSOCKET_URL)
|
||||
@@ -37,41 +37,24 @@ func (g *GDAX) Run() {
|
||||
currencies = append(currencies, x.ID[0:3]+x.ID[4:])
|
||||
}
|
||||
}
|
||||
err = g.UpdateAvailableCurrencies(currencies)
|
||||
err = g.UpdateAvailableCurrencies(currencies, false)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", g.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
for g.Enabled {
|
||||
for _, x := range g.EnabledPairs {
|
||||
currency := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
currency.Delimiter = "-"
|
||||
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.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(g.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), 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()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// GDAX exchange
|
||||
func (g *GDAX) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = g.GetName()
|
||||
accountBalance, err := g.GetAccounts()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = accountBalance[i].Currency
|
||||
exchangeCurrency.TotalValue = accountBalance[i].Available
|
||||
exchangeCurrency.Hold = accountBalance[i].Hold
|
||||
@@ -81,22 +64,18 @@ func (e *GDAX) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (g *GDAX) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(g.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (g *GDAX) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := g.GetTicker(exchange.FormatExchangeCurrency(g.Name, p).String())
|
||||
if err != nil {
|
||||
return ticker.Price{}, err
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := g.GetTicker(p.Pair().String())
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
}
|
||||
|
||||
stats, err := g.GetStats(p.Pair().String())
|
||||
stats, err := g.GetStats(exchange.FormatExchangeCurrency(g.Name, p).String())
|
||||
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
return ticker.Price{}, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
@@ -104,32 +83,46 @@ func (g *GDAX) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerPrice.Last = tick.Price
|
||||
tickerPrice.High = stats.High
|
||||
tickerPrice.Low = stats.Low
|
||||
ticker.ProcessTicker(g.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(g.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(g.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (g *GDAX) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(g.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (g *GDAX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(g.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return g.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
orderbookNew, err := g.GetOrderbook(p.Pair().String(), 2)
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (g *GDAX) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(g.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return g.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (g *GDAX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := g.GetOrderbook(exchange.FormatExchangeCurrency(g.Name, p).String(), 2)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
obNew := orderbookNew.(GDAXOrderbookL1L2)
|
||||
|
||||
for x, _ := range obNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price})
|
||||
for x := range obNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price})
|
||||
}
|
||||
|
||||
for x, _ := range obNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price})
|
||||
for x := range obNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price})
|
||||
}
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(g.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
|
||||
orderbook.ProcessOrderbook(g.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(g.Name, p, assetType)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,7 +37,7 @@ const (
|
||||
)
|
||||
|
||||
type Gemini struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (g *Gemini) SetDefaults() {
|
||||
@@ -45,6 +46,11 @@ func (g *Gemini) SetDefaults() {
|
||||
g.Verbose = false
|
||||
g.Websocket = false
|
||||
g.RESTPollingDelay = 10
|
||||
g.RequestCurrencyPairFormat.Delimiter = ""
|
||||
g.RequestCurrencyPairFormat.Uppercase = true
|
||||
g.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
g.ConfigCurrencyPairFormat.Uppercase = true
|
||||
g.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (g *Gemini) Setup(exch config.ExchangeConfig) {
|
||||
@@ -60,6 +66,14 @@ func (g *Gemini) Setup(exch config.ExchangeConfig) {
|
||||
g.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
g.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
g.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := g.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = g.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,9 +259,19 @@ func (g *Gemini) PostHeartbeat() (bool, error) {
|
||||
}
|
||||
|
||||
func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
|
||||
if !g.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name)
|
||||
}
|
||||
|
||||
if g.Nonce.Get() == 0 {
|
||||
g.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
g.Nonce.Inc()
|
||||
}
|
||||
|
||||
request := make(map[string]interface{})
|
||||
request["request"] = fmt.Sprintf("/v%s/%s", GEMINI_API_VERSION, path)
|
||||
request["nonce"] = time.Now().UnixNano()
|
||||
request["nonce"] = g.Nonce.Get()
|
||||
|
||||
if params != nil {
|
||||
for key, value := range params {
|
||||
@@ -255,18 +279,18 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
|
||||
}
|
||||
}
|
||||
|
||||
PayloadJson, err := common.JSONEncode(request)
|
||||
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)
|
||||
log.Printf("Request JSON: %s\n", PayloadJSON)
|
||||
}
|
||||
|
||||
PayloadBase64 := common.Base64Encode(PayloadJson)
|
||||
hmac := common.GetHMAC(common.HASH_SHA512_384, []byte(PayloadBase64), []byte(g.APISecret))
|
||||
PayloadBase64 := common.Base64Encode(PayloadJSON)
|
||||
hmac := common.GetHMAC(common.HashSHA512_384, []byte(PayloadBase64), []byte(g.APISecret))
|
||||
headers := make(map[string]string)
|
||||
headers["X-GEMINI-APIKEY"] = g.APIKey
|
||||
headers["X-GEMINI-PAYLOAD"] = PayloadBase64
|
||||
@@ -275,13 +299,13 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
|
||||
resp, err := common.SendHTTPRequest(method, GEMINI_API_URL+path, headers, strings.NewReader(""))
|
||||
|
||||
if g.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
return errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,19 +3,19 @@ package gemini
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Gemini go routine
|
||||
func (g *Gemini) Start() {
|
||||
go g.Run()
|
||||
}
|
||||
|
||||
// Run implements the Gemini wrapper
|
||||
func (g *Gemini) Run() {
|
||||
if g.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", g.GetName(), g.RESTPollingDelay)
|
||||
@@ -26,55 +26,35 @@ func (g *Gemini) Run() {
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available symbols.\n", g.GetName())
|
||||
} else {
|
||||
err = g.UpdateAvailableCurrencies(exchangeProducts)
|
||||
err = g.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", g.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
for g.Enabled {
|
||||
for _, x := range g.EnabledPairs {
|
||||
currency := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
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.Pair().String(), ticker.Last, ticker.Bid, ticker.Ask, ticker.Volume)
|
||||
stats.AddExchangeInfo(g.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), 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()
|
||||
// GetExchangeAccountInfo Retrieves balances for all enabled currencies for the
|
||||
// Gemini exchange
|
||||
func (g *Gemini) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = g.GetName()
|
||||
accountBalance, err := g.GetBalances()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
for i := 0; i < len(accountBalance); i++ {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
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(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(g.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (g *Gemini) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := g.GetTicker(p.Pair().String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
@@ -84,31 +64,44 @@ func (g *Gemini) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error)
|
||||
tickerPrice.Bid = tick.Bid
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Volume.USD
|
||||
ticker.ProcessTicker(g.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(g.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(g.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (g *Gemini) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(g.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (g *Gemini) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(g.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return g.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (g *Gemini) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(g.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return g.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (g *Gemini) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := g.GetOrderbook(p.Pair().String(), url.Values{})
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price})
|
||||
for x := range orderbookNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price})
|
||||
for x := range orderbookNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(g.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
orderbook.ProcessOrderbook(g.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(g.Name, p, assetType)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,7 +20,7 @@ const (
|
||||
)
|
||||
|
||||
type HUOBI struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (h *HUOBI) SetDefaults() {
|
||||
@@ -29,6 +30,11 @@ func (h *HUOBI) SetDefaults() {
|
||||
h.Verbose = false
|
||||
h.Websocket = false
|
||||
h.RESTPollingDelay = 10
|
||||
h.RequestCurrencyPairFormat.Delimiter = ""
|
||||
h.RequestCurrencyPairFormat.Uppercase = false
|
||||
h.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
h.ConfigCurrencyPairFormat.Uppercase = true
|
||||
h.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (h *HUOBI) Setup(exch config.ExchangeConfig) {
|
||||
@@ -44,6 +50,14 @@ func (h *HUOBI) Setup(exch config.ExchangeConfig) {
|
||||
h.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
h.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
h.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := h.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = h.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +67,7 @@ func (h *HUOBI) GetFee() float64 {
|
||||
|
||||
func (h *HUOBI) GetTicker(symbol string) (HuobiTicker, error) {
|
||||
resp := HuobiTickerResponse{}
|
||||
path := fmt.Sprintf("http://api.huobi.com/staticmarket/ticker_%s_json.js", symbol)
|
||||
path := fmt.Sprintf("https://api.huobi.com/staticmarket/ticker_%s_json.js", symbol)
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
@@ -63,7 +77,7 @@ func (h *HUOBI) GetTicker(symbol string) (HuobiTicker, error) {
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetOrderBook(symbol string) (HuobiOrderbook, error) {
|
||||
path := fmt.Sprintf("http://api.huobi.com/staticmarket/depth_%s_json.js", symbol)
|
||||
path := fmt.Sprintf("https://api.huobi.com/staticmarket/depth_%s_json.js", symbol)
|
||||
resp := HuobiOrderbook{}
|
||||
err := common.SendHTTPGetRequest(path, true, &resp)
|
||||
if err != nil {
|
||||
@@ -177,6 +191,10 @@ func (h *HUOBI) GetOrderIDByTradeID(coinType, orderID int) {
|
||||
}
|
||||
|
||||
func (h *HUOBI) SendAuthenticatedRequest(method string, v url.Values) error {
|
||||
if !h.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name)
|
||||
}
|
||||
|
||||
v.Set("access_key", h.APIKey)
|
||||
v.Set("created", strconv.FormatInt(time.Now().Unix(), 10))
|
||||
v.Set("method", method)
|
||||
@@ -198,7 +216,7 @@ func (h *HUOBI) SendAuthenticatedRequest(method string, v url.Values) error {
|
||||
}
|
||||
|
||||
if h.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
log.Printf("Received raw: %s\n", resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,21 +2,20 @@ package huobi
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the HUOBI go routine
|
||||
func (h *HUOBI) Start() {
|
||||
go h.Run()
|
||||
}
|
||||
|
||||
// Run implements the HUOBI wrapper
|
||||
func (h *HUOBI) Run() {
|
||||
if h.Verbose {
|
||||
log.Printf("%s Websocket: %s (url: %s).\n", h.GetName(), common.IsEnabled(h.Websocket), HUOBI_SOCKETIO_ADDRESS)
|
||||
@@ -27,35 +26,11 @@ func (h *HUOBI) Run() {
|
||||
if h.Websocket {
|
||||
go h.WebsocketClient()
|
||||
}
|
||||
|
||||
for h.Enabled {
|
||||
for _, x := range h.EnabledPairs {
|
||||
curr := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
go func() {
|
||||
ticker, err := h.GetTickerPrice(curr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
HuobiLastUSD, _ := currency.ConvertCurrency(ticker.Last, "CNY", "USD")
|
||||
HuobiHighUSD, _ := currency.ConvertCurrency(ticker.High, "CNY", "USD")
|
||||
HuobiLowUSD, _ := currency.ConvertCurrency(ticker.Low, "CNY", "USD")
|
||||
log.Printf("Huobi %s: Last %f (%f) High %f (%f) Low %f (%f) Volume %f\n", curr.Pair().String(), HuobiLastUSD, ticker.Last, HuobiHighUSD, ticker.High, HuobiLowUSD, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(h.GetName(), curr.GetFirstCurrency().String(), curr.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
stats.AddExchangeInfo(h.GetName(), curr.GetFirstCurrency().String(), "USD", HuobiLastUSD, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * h.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(h.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (h *HUOBI) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := h.GetTicker(p.GetFirstCurrency().Lower().String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
@@ -67,40 +42,54 @@ func (h *HUOBI) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error)
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(h.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(h.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(h.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (h *HUOBI) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(h.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (h *HUOBI) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(h.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return h.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (h *HUOBI) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(h.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return h.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (h *HUOBI) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := h.GetOrderBook(p.GetFirstCurrency().Lower().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(h.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
|
||||
orderbook.ProcessOrderbook(h.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(h.Name, p, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
//GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// HUOBI exchange - to-do
|
||||
func (h *HUOBI) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = h.GetName()
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package itbit
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,7 +21,7 @@ const (
|
||||
)
|
||||
|
||||
type ItBit struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (i *ItBit) SetDefaults() {
|
||||
@@ -30,6 +32,11 @@ func (i *ItBit) SetDefaults() {
|
||||
i.Verbose = false
|
||||
i.Websocket = false
|
||||
i.RESTPollingDelay = 10
|
||||
i.RequestCurrencyPairFormat.Delimiter = ""
|
||||
i.RequestCurrencyPairFormat.Uppercase = true
|
||||
i.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
i.ConfigCurrencyPairFormat.Uppercase = true
|
||||
i.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (i *ItBit) Setup(exch config.ExchangeConfig) {
|
||||
@@ -45,33 +52,40 @@ func (i *ItBit) Setup(exch config.ExchangeConfig) {
|
||||
i.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
i.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
i.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := i.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = i.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetFee(maker bool) float64 {
|
||||
if maker {
|
||||
return i.MakerFee
|
||||
} else {
|
||||
return i.TakerFee
|
||||
}
|
||||
return i.TakerFee
|
||||
}
|
||||
|
||||
func (i *ItBit) GetTicker(currency string) (ItBitTicker, error) {
|
||||
func (i *ItBit) GetTicker(currency string) (Ticker, error) {
|
||||
path := ITBIT_API_URL + "/markets/" + currency + "/ticker"
|
||||
var itbitTicker ItBitTicker
|
||||
var itbitTicker Ticker
|
||||
err := common.SendHTTPGetRequest(path, true, &itbitTicker)
|
||||
if err != nil {
|
||||
return ItBitTicker{}, err
|
||||
return Ticker{}, err
|
||||
}
|
||||
return itbitTicker, nil
|
||||
}
|
||||
|
||||
func (i *ItBit) GetOrderbook(currency string) (ItBitOrderbookResponse, error) {
|
||||
response := ItBitOrderbookResponse{}
|
||||
func (i *ItBit) GetOrderbook(currency string) (OrderbookResponse, error) {
|
||||
response := OrderbookResponse{}
|
||||
path := ITBIT_API_URL + "/markets/" + currency + "/order_book"
|
||||
err := common.SendHTTPGetRequest(path, true, &response)
|
||||
if err != nil {
|
||||
return ItBitOrderbookResponse{}, err
|
||||
return OrderbookResponse{}, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
@@ -227,14 +241,16 @@ func (i *ItBit) WalletTransfer(walletID, sourceWallet, destWallet string, amount
|
||||
}
|
||||
|
||||
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
|
||||
if !i.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, i.Name)
|
||||
}
|
||||
|
||||
if i.Nonce.Get() == 0 {
|
||||
i.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
i.Nonce.Inc()
|
||||
}
|
||||
|
||||
nonce -= 1
|
||||
request := make(map[string]interface{})
|
||||
url := ITBIT_API_URL + path
|
||||
|
||||
@@ -244,41 +260,40 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method string, path string, params
|
||||
}
|
||||
}
|
||||
|
||||
PayloadJson := []byte("")
|
||||
PayloadJSON := []byte("")
|
||||
|
||||
if params != nil {
|
||||
PayloadJson, err = common.JSONEncode(request)
|
||||
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)
|
||||
log.Printf("Request JSON: %s\n", PayloadJSON)
|
||||
}
|
||||
}
|
||||
|
||||
nonceStr := strconv.Itoa(nonce)
|
||||
message, err := common.JSONEncode([]string{method, url, string(PayloadJson), nonceStr, timestamp})
|
||||
message, err := common.JSONEncode([]string{method, url, string(PayloadJSON), i.Nonce.String(), i.Nonce.String()[0:13]})
|
||||
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))
|
||||
hash := common.GetSHA256([]byte(i.Nonce.String() + string(message)))
|
||||
hmac := common.GetHMAC(common.HashSHA512, []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["X-Auth-Timestamp"] = i.Nonce.String()[0:13]
|
||||
headers["X-Auth-Nonce"] = i.Nonce.String()
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
resp, err := common.SendHTTPRequest(method, url, headers, bytes.NewBuffer([]byte(PayloadJson)))
|
||||
resp, err := common.SendHTTPRequest(method, url, headers, bytes.NewBuffer([]byte(PayloadJSON)))
|
||||
|
||||
if i.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package itbit
|
||||
|
||||
type ItBitTicker struct {
|
||||
type Ticker struct {
|
||||
Pair string
|
||||
Bid float64 `json:",string"`
|
||||
BidAmt float64 `json:",string"`
|
||||
@@ -20,7 +20,7 @@ type ItBitTicker struct {
|
||||
ServertimeUTC string
|
||||
}
|
||||
|
||||
type ItBitOrderbookResponse struct {
|
||||
type OrderbookResponse struct {
|
||||
Bids [][]string `json:"bids"`
|
||||
Asks [][]string `json:"asks"`
|
||||
}
|
||||
|
||||
@@ -3,49 +3,31 @@ package itbit
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the ItBit go routine
|
||||
func (i *ItBit) Start() {
|
||||
go i.Run()
|
||||
}
|
||||
|
||||
// Run implements the ItBit wrapper
|
||||
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 := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
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.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(i.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * i.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ItBit) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(i.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := i.GetTicker(p.Pair().String())
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (i *ItBit) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := i.GetTicker(exchange.FormatExchangeCurrency(i.Name,
|
||||
p).String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
@@ -57,23 +39,38 @@ func (i *ItBit) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error)
|
||||
tickerPrice.High = tick.High24h
|
||||
tickerPrice.Low = tick.Low24h
|
||||
tickerPrice.Volume = tick.Volume24h
|
||||
ticker.ProcessTicker(i.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
ticker.ProcessTicker(i.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(i.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (i *ItBit) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(i.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (i *ItBit) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(i.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return i.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
orderbookNew, err := i.GetOrderbook(p.Pair().String())
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (i *ItBit) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(i.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return i.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (i *ItBit) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := i.GetOrderbook(exchange.FormatExchangeCurrency(i.Name,
|
||||
p).String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
price, err := strconv.ParseFloat(data[0], 64)
|
||||
if err != nil {
|
||||
@@ -83,10 +80,10 @@ func (i *ItBit) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, er
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: amount, Price: price})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: amount, Price: price})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
price, err := strconv.ParseFloat(data[0], 64)
|
||||
if err != nil {
|
||||
@@ -96,17 +93,17 @@ func (i *ItBit) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, er
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: amount, Price: price})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: amount, Price: price})
|
||||
}
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(i.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
|
||||
orderbook.ProcessOrderbook(i.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(i.Name, p, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
//ItBit exchange - to-do
|
||||
func (i *ItBit) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = i.GetName()
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -41,7 +42,7 @@ const (
|
||||
)
|
||||
|
||||
type Kraken struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
CryptoFee, FiatFee float64
|
||||
Ticker map[string]KrakenTicker
|
||||
}
|
||||
@@ -55,6 +56,12 @@ func (k *Kraken) SetDefaults() {
|
||||
k.Websocket = false
|
||||
k.RESTPollingDelay = 10
|
||||
k.Ticker = make(map[string]KrakenTicker)
|
||||
k.RequestCurrencyPairFormat.Delimiter = ""
|
||||
k.RequestCurrencyPairFormat.Uppercase = true
|
||||
k.RequestCurrencyPairFormat.Separator = ","
|
||||
k.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
k.ConfigCurrencyPairFormat.Uppercase = true
|
||||
k.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (k *Kraken) Setup(exch config.ExchangeConfig) {
|
||||
@@ -70,6 +77,14 @@ func (k *Kraken) Setup(exch config.ExchangeConfig) {
|
||||
k.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
k.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
k.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := k.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = k.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,20 +193,62 @@ func (k *Kraken) GetOHLC(symbol string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetDepth(symbol string) error {
|
||||
// GetDepth returns the orderbook for a particular currency
|
||||
func (k *Kraken) GetDepth(symbol string) (Orderbook, error) {
|
||||
values := url.Values{}
|
||||
values.Set("pair", symbol)
|
||||
|
||||
var result interface{}
|
||||
var ob Orderbook
|
||||
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
|
||||
return ob, err
|
||||
}
|
||||
|
||||
log.Println(result)
|
||||
return nil
|
||||
data := result.(map[string]interface{})
|
||||
orderbookData := data["result"].(map[string]interface{})
|
||||
|
||||
var bidsData []interface{}
|
||||
var asksData []interface{}
|
||||
for _, y := range orderbookData {
|
||||
yData := y.(map[string]interface{})
|
||||
bidsData = yData["bids"].([]interface{})
|
||||
asksData = yData["asks"].([]interface{})
|
||||
}
|
||||
|
||||
processOrderbook := func(data []interface{}) ([]OrderbookBase, error) {
|
||||
var result []OrderbookBase
|
||||
for x := range data {
|
||||
entry := data[x].([]interface{})
|
||||
|
||||
price, err := strconv.ParseFloat(entry[0].(string), 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
amount, err := strconv.ParseFloat(entry[1].(string), 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, OrderbookBase{Price: price, Amount: amount})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
ob.Bids, err = processOrderbook(bidsData)
|
||||
if err != nil {
|
||||
return ob, err
|
||||
}
|
||||
|
||||
ob.Asks, err = processOrderbook(asksData)
|
||||
if err != nil {
|
||||
return ob, err
|
||||
}
|
||||
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
func (k *Kraken) GetTrades(symbol string) error {
|
||||
@@ -509,8 +566,18 @@ func (k *Kraken) CancelOrder(orderID int64) {
|
||||
}
|
||||
|
||||
func (k *Kraken) SendAuthenticatedHTTPRequest(method string, values url.Values) (interface{}, error) {
|
||||
if !k.AuthenticatedAPISupport {
|
||||
return nil, fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, k.Name)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/%s/private/%s", KRAKEN_API_VERSION, method)
|
||||
values.Set("nonce", strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
if k.Nonce.Get() == 0 {
|
||||
k.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
k.Nonce.Inc()
|
||||
}
|
||||
|
||||
values.Set("nonce", k.Nonce.String())
|
||||
secret, err := common.Base64Decode(k.APISecret)
|
||||
|
||||
if err != nil {
|
||||
@@ -518,7 +585,7 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, values url.Values)
|
||||
}
|
||||
|
||||
shasum := common.GetSHA256([]byte(values.Get("nonce") + values.Encode()))
|
||||
signature := common.Base64Encode(common.GetHMAC(common.HASH_SHA512, append([]byte(path), shasum...), secret))
|
||||
signature := common.Base64Encode(common.GetHMAC(common.HashSHA512, append([]byte(path), shasum...), secret))
|
||||
|
||||
if k.Verbose {
|
||||
log.Printf("Sending POST request to %s, path: %s.", KRAKEN_API_URL, path)
|
||||
@@ -535,7 +602,7 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, values url.Values)
|
||||
}
|
||||
|
||||
if k.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
||||
@@ -31,6 +31,18 @@ type KrakenTicker struct {
|
||||
Open float64
|
||||
}
|
||||
|
||||
// OrderbookBase stores the orderbook price and amount data
|
||||
type OrderbookBase struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
// Orderbook stores the bids and asks orderbook data
|
||||
type Orderbook struct {
|
||||
Bids []OrderbookBase
|
||||
Asks []OrderbookBase
|
||||
}
|
||||
|
||||
type KrakenTickerResponse struct {
|
||||
Ask []string `json:"a"`
|
||||
Bid []string `json:"b"`
|
||||
|
||||
@@ -2,20 +2,19 @@ package kraken
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Kraken go routine
|
||||
func (k *Kraken) Start() {
|
||||
go k.Run()
|
||||
}
|
||||
|
||||
// Run implements the Kraken wrapper
|
||||
func (k *Kraken) Run() {
|
||||
if k.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", k.GetName(), k.RESTPollingDelay)
|
||||
@@ -30,50 +29,87 @@ func (k *Kraken) Run() {
|
||||
for _, v := range assetPairs {
|
||||
exchangeProducts = append(exchangeProducts, v.Altname)
|
||||
}
|
||||
err = k.UpdateAvailableCurrencies(exchangeProducts)
|
||||
err = k.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", k.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
stats.AddExchangeInfo(k.GetName(), x[0:3], x[3:], ticker.Last, ticker.Volume)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Second * k.RESTPollingDelay)
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (k *Kraken) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
pairs := k.GetEnabledCurrencies()
|
||||
pairsCollated, err := exchange.GetAndFormatExchangeCurrencies(k.Name, pairs)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
err = k.GetTicker(pairsCollated.String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
}
|
||||
|
||||
//This will return the TickerPrice struct when tickers are completed here..
|
||||
func (k *Kraken) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
var tickerPrice ticker.TickerPrice
|
||||
/*
|
||||
ticker, err := i.GetTicker(currency)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return tickerPrice
|
||||
for _, x := range pairs {
|
||||
var tp ticker.Price
|
||||
tick, ok := k.Ticker[x.Pair().String()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
tickerPrice.Ask = ticker.Ask
|
||||
tickerPrice.Bid = ticker.Bid
|
||||
*/
|
||||
return tickerPrice, nil
|
||||
|
||||
tp.Pair = x
|
||||
tp.Last = tick.Last
|
||||
tp.Ask = tick.Ask
|
||||
tp.Bid = tick.Bid
|
||||
tp.High = tick.High
|
||||
tp.Low = tick.Low
|
||||
tp.Volume = tick.Volume
|
||||
ticker.ProcessTicker(k.GetName(), x, tp, assetType)
|
||||
}
|
||||
return ticker.GetTicker(k.GetName(), p, assetType)
|
||||
}
|
||||
|
||||
func (k *Kraken) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
return orderbook.OrderbookBase{}, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (k *Kraken) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(k.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return k.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, 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()
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (k *Kraken) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(k.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return k.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (k *Kraken) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := k.GetDepth(exchange.FormatExchangeCurrency(k.GetName(), p).String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price})
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price})
|
||||
}
|
||||
|
||||
orderbook.ProcessOrderbook(k.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(k.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// Kraken exchange - to-do
|
||||
func (k *Kraken) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = k.GetName()
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -31,7 +32,7 @@ const (
|
||||
)
|
||||
|
||||
type LakeBTC struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (l *LakeBTC) SetDefaults() {
|
||||
@@ -42,6 +43,11 @@ func (l *LakeBTC) SetDefaults() {
|
||||
l.Verbose = false
|
||||
l.Websocket = false
|
||||
l.RESTPollingDelay = 10
|
||||
l.RequestCurrencyPairFormat.Delimiter = ""
|
||||
l.RequestCurrencyPairFormat.Uppercase = true
|
||||
l.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
l.ConfigCurrencyPairFormat.Uppercase = true
|
||||
l.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (l *LakeBTC) Setup(exch config.ExchangeConfig) {
|
||||
@@ -57,6 +63,14 @@ func (l *LakeBTC) Setup(exch config.ExchangeConfig) {
|
||||
l.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
l.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
l.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := l.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = l.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,9 +286,18 @@ func (l *LakeBTC) CreateWithdraw(amount float64, accountID int64) (LakeBTCWithdr
|
||||
}
|
||||
|
||||
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.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
|
||||
}
|
||||
|
||||
if l.Nonce.Get() == 0 {
|
||||
l.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
l.Nonce.Inc()
|
||||
}
|
||||
|
||||
req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s¶ms=%s", l.Nonce.String(), l.APIKey, method, params)
|
||||
hmac := common.GetHMAC(common.HashSHA1, []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)
|
||||
@@ -291,7 +314,7 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Json-Rpc-Tonce"] = nonce
|
||||
headers["Json-Rpc-Tonce"] = l.Nonce.String()
|
||||
headers["Authorization"] = "Basic " + common.Base64Encode([]byte(l.APIKey+":"+common.HexEncodeToString(hmac)))
|
||||
headers["Content-Type"] = "application/json-rpc"
|
||||
|
||||
@@ -301,7 +324,7 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
|
||||
}
|
||||
|
||||
if l.Verbose {
|
||||
log.Printf("Recieved raw: %s\n", resp)
|
||||
log.Printf("Received raw: %s\n", resp)
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
@@ -311,7 +334,7 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
|
||||
errResponse := ErrorResponse{}
|
||||
err = common.JSONDecode([]byte(resp), &errResponse)
|
||||
if err != nil {
|
||||
return errors.New("Unable to check response for error.")
|
||||
return errors.New("unable to check response for error")
|
||||
}
|
||||
|
||||
if errResponse.Error != "" {
|
||||
@@ -321,7 +344,7 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
return errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,95 +3,91 @@ package lakebtc
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the LakeBTC go routine
|
||||
func (l *LakeBTC) Start() {
|
||||
go l.Run()
|
||||
}
|
||||
|
||||
// Run implements the LakeBTC wrapper
|
||||
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 {
|
||||
currency := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
ticker, err := l.GetTickerPrice(currency)
|
||||
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)
|
||||
stats.AddExchangeInfo(l.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}
|
||||
time.Sleep(time.Second * l.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(l.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (l *LakeBTC) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tick, err := l.GetTicker()
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
return ticker.Price{}, err
|
||||
}
|
||||
|
||||
result, ok := tick[p.Pair().String()]
|
||||
if !ok {
|
||||
return ticker.TickerPrice{}, err
|
||||
for _, x := range l.GetEnabledCurrencies() {
|
||||
currency := exchange.FormatExchangeCurrency(l.Name, x).String()
|
||||
var tickerPrice ticker.Price
|
||||
tickerPrice.Pair = x
|
||||
tickerPrice.Ask = tick[currency].Ask
|
||||
tickerPrice.Bid = tick[currency].Bid
|
||||
tickerPrice.Volume = tick[currency].Volume
|
||||
tickerPrice.High = tick[currency].High
|
||||
tickerPrice.Low = tick[currency].Low
|
||||
tickerPrice.Last = tick[currency].Last
|
||||
ticker.ProcessTicker(l.GetName(), x, tickerPrice, assetType)
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = result.Ask
|
||||
tickerPrice.Bid = result.Bid
|
||||
tickerPrice.Volume = result.Volume
|
||||
tickerPrice.High = result.High
|
||||
tickerPrice.Low = result.Low
|
||||
tickerPrice.Last = result.Last
|
||||
ticker.ProcessTicker(l.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
return ticker.GetTicker(l.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(l.GetName(), p)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (l *LakeBTC) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(l.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return l.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (l *LakeBTC) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(l.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return l.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (l *LakeBTC) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := l.GetOrderBook(p.Pair().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price})
|
||||
for x := range orderbookNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price})
|
||||
for x := range orderbookNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price})
|
||||
}
|
||||
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(l.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
orderbook.ProcessOrderbook(l.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(l.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (l *LakeBTC) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// LakeBTC exchange
|
||||
func (l *LakeBTC) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = l.GetName()
|
||||
accountInfo, err := l.GetAccountInfo()
|
||||
if err != nil {
|
||||
@@ -101,7 +97,7 @@ func (l *LakeBTC) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error)
|
||||
for x, y := range accountInfo.Balance {
|
||||
for z, w := range accountInfo.Locked {
|
||||
if z == x {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||
exchangeCurrency.TotalValue, _ = strconv.ParseFloat(y, 64)
|
||||
exchangeCurrency.Hold, _ = strconv.ParseFloat(w, 64)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,7 +34,7 @@ const (
|
||||
)
|
||||
|
||||
type Liqui struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
Ticker map[string]LiquiTicker
|
||||
Info LiquiInfo
|
||||
}
|
||||
@@ -46,6 +47,12 @@ func (l *Liqui) SetDefaults() {
|
||||
l.Websocket = false
|
||||
l.RESTPollingDelay = 10
|
||||
l.Ticker = make(map[string]LiquiTicker)
|
||||
l.RequestCurrencyPairFormat.Delimiter = "_"
|
||||
l.RequestCurrencyPairFormat.Uppercase = false
|
||||
l.RequestCurrencyPairFormat.Separator = "-"
|
||||
l.ConfigCurrencyPairFormat.Delimiter = "_"
|
||||
l.ConfigCurrencyPairFormat.Uppercase = true
|
||||
l.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (l *Liqui) Setup(exch config.ExchangeConfig) {
|
||||
@@ -61,6 +68,14 @@ func (l *Liqui) Setup(exch config.ExchangeConfig) {
|
||||
l.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
l.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
l.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := l.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = l.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +91,7 @@ func (l *Liqui) GetFee(currency string) (float64, error) {
|
||||
func (l *Liqui) GetAvailablePairs(nonHidden bool) []string {
|
||||
var pairs []string
|
||||
for x, y := range l.Info.Pairs {
|
||||
if nonHidden && y.Hidden == 1 {
|
||||
if nonHidden && y.Hidden == 1 || x == "" {
|
||||
continue
|
||||
}
|
||||
pairs = append(pairs, common.StringToUpper(x))
|
||||
@@ -249,12 +264,20 @@ func (l *Liqui) WithdrawCoins(coin string, amount float64, address string) (Liqu
|
||||
}
|
||||
|
||||
func (l *Liqui) SendAuthenticatedHTTPRequest(method string, values url.Values, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
values.Set("nonce", nonce)
|
||||
if !l.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
|
||||
}
|
||||
|
||||
if l.Nonce.Get() == 0 {
|
||||
l.Nonce.Set(time.Now().Unix())
|
||||
} else {
|
||||
l.Nonce.Inc()
|
||||
}
|
||||
values.Set("nonce", l.Nonce.String())
|
||||
values.Set("method", method)
|
||||
|
||||
encoded := values.Encode()
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(encoded), []byte(l.APISecret))
|
||||
hmac := common.GetHMAC(common.HashSHA512, []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)
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
package liqui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Liqui go routine
|
||||
func (l *Liqui) Start() {
|
||||
go l.Run()
|
||||
}
|
||||
|
||||
// Run implements the Liqui wrapper
|
||||
func (l *Liqui) Run() {
|
||||
if l.Verbose {
|
||||
log.Printf("%s polling delay: %ds.\n", l.GetName(), l.RESTPollingDelay)
|
||||
@@ -29,92 +28,95 @@ func (l *Liqui) Run() {
|
||||
log.Printf("%s Unable to fetch info.\n", l.GetName())
|
||||
} else {
|
||||
exchangeProducts := l.GetAvailablePairs(true)
|
||||
err = l.UpdateAvailableCurrencies(exchangeProducts)
|
||||
err = l.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", l.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
currency := pair.NewCurrencyPairDelimiter(common.StringToUpper(x), "_")
|
||||
log.Printf("Liqui %s: Last %f High %f Low %f Volume %f\n", currency.Pair().String(), y.Last, y.High, y.Low, y.Vol_cur)
|
||||
l.Ticker[x] = y
|
||||
stats.AddExchangeInfo(l.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), y.Last, y.Vol_cur)
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Second * l.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Liqui) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, ok := l.Ticker[p.Pair().Lower().String()]
|
||||
if !ok {
|
||||
return tickerPrice, errors.New("Unable to get currency.")
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (l *Liqui) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
pairsString, err := exchange.GetAndFormatExchangeCurrencies(l.Name,
|
||||
l.GetEnabledCurrencies())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Buy
|
||||
tickerPrice.Bid = tick.Sell
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol_cur
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(l.GetName(), p, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
|
||||
result, err := l.GetTicker(pairsString.String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
for _, x := range l.GetEnabledCurrencies() {
|
||||
currency := exchange.FormatExchangeCurrency(l.Name, x).String()
|
||||
var tp ticker.Price
|
||||
tp.Pair = x
|
||||
tp.Last = result[currency].Last
|
||||
tp.Ask = result[currency].Sell
|
||||
tp.Bid = result[currency].Buy
|
||||
tp.Last = result[currency].Last
|
||||
tp.Low = result[currency].Low
|
||||
tp.Volume = result[currency].Vol_cur
|
||||
ticker.ProcessTicker(l.Name, x, tp, assetType)
|
||||
}
|
||||
|
||||
return ticker.GetTicker(l.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (l *Liqui) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(l.GetName(), p)
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (l *Liqui) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(l.Name, p, assetType)
|
||||
if err != nil {
|
||||
return l.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (l *Liqui) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(l.Name, p, assetType)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
return l.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
orderbookNew, err := l.GetDepth(p.Pair().Lower().String())
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (l *Liqui) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := l.GetDepth(exchange.FormatExchangeCurrency(l.Name, p).String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
orderBook.Pair = p
|
||||
orderbook.ProcessOrderbook(l.GetName(), p, orderBook)
|
||||
return orderBook, nil
|
||||
|
||||
orderbook.ProcessOrderbook(l.Name, p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(l.Name, p, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// Liqui exchange
|
||||
func (l *Liqui) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = l.GetName()
|
||||
accountBalance, err := l.GetAccountInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
for x, y := range accountBalance.Funds {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||
exchangeCurrency.TotalValue = y
|
||||
exchangeCurrency.Hold = 0
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,7 +29,7 @@ const (
|
||||
)
|
||||
|
||||
type LocalBitcoins struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) SetDefaults() {
|
||||
@@ -38,6 +39,11 @@ func (l *LocalBitcoins) SetDefaults() {
|
||||
l.Verbose = false
|
||||
l.Websocket = false
|
||||
l.RESTPollingDelay = 10
|
||||
l.RequestCurrencyPairFormat.Delimiter = ""
|
||||
l.RequestCurrencyPairFormat.Uppercase = true
|
||||
l.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
l.ConfigCurrencyPairFormat.Uppercase = true
|
||||
l.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) Setup(exch config.ExchangeConfig) {
|
||||
@@ -53,6 +59,14 @@ func (l *LocalBitcoins) Setup(exch config.ExchangeConfig) {
|
||||
l.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
l.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
l.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := l.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = l.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +281,16 @@ func (l *LocalBitcoins) GetWalletAddress() (string, error) {
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, values url.Values, result interface{}) (err error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
if !l.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
|
||||
}
|
||||
|
||||
if l.Nonce.Get() == 0 {
|
||||
l.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
l.Nonce.Inc()
|
||||
}
|
||||
|
||||
payload := ""
|
||||
path = "/api/" + path
|
||||
|
||||
@@ -275,24 +298,24 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, values
|
||||
payload = values.Encode()
|
||||
}
|
||||
|
||||
message := string(nonce) + l.APIKey + path + payload
|
||||
hmac := common.GetHMAC(common.HASH_SHA256, []byte(message), []byte(l.APISecret))
|
||||
message := l.Nonce.String() + l.APIKey + path + payload
|
||||
hmac := common.GetHMAC(common.HashSHA256, []byte(message), []byte(l.APISecret))
|
||||
headers := make(map[string]string)
|
||||
headers["Apiauth-Key"] = l.APIKey
|
||||
headers["Apiauth-Nonce"] = string(nonce)
|
||||
headers["Apiauth-Nonce"] = l.Nonce.String()
|
||||
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)
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
return errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,77 +2,96 @@ package localbitcoins
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the LocalBitcoins go routine
|
||||
func (l *LocalBitcoins) Start() {
|
||||
go l.Run()
|
||||
}
|
||||
|
||||
// Run implements the LocalBitcoins wrapper
|
||||
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 := pair.NewCurrencyPair("BTC", x[3:])
|
||||
ticker, err := l.GetTickerPrice(currency)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("LocalBitcoins BTC %s: Last %f Volume %f\n", currency.Pair().String(), ticker.Last, ticker.Volume)
|
||||
stats.AddExchangeInfo(l.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}
|
||||
time.Sleep(time.Second * l.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetTickerPrice(p pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(l.GetName(), p)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (l *LocalBitcoins) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := l.GetTicker()
|
||||
if err != nil {
|
||||
return ticker.TickerPrice{}, err
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
for key, value := range tick {
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Last = value.Rates.Last
|
||||
tickerPrice.Pair.SecondCurrency = pair.CurrencyItem(key)
|
||||
tickerPrice.Volume = value.VolumeBTC
|
||||
ticker.ProcessTicker(l.GetName(), p, tickerPrice)
|
||||
for _, x := range l.GetEnabledCurrencies() {
|
||||
currency := x.SecondCurrency.String()
|
||||
var tp ticker.Price
|
||||
tp.Pair = x
|
||||
tp.Last = tick[currency].Rates.Last
|
||||
tp.Volume = tick[currency].VolumeBTC
|
||||
ticker.ProcessTicker(l.GetName(), x, tp, assetType)
|
||||
}
|
||||
return tickerPrice, nil
|
||||
|
||||
return ticker.GetTicker(l.GetName(), p, assetType)
|
||||
}
|
||||
|
||||
func (l *LocalBitcoins) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
return orderbook.OrderbookBase{}, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (l *LocalBitcoins) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(l.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return l.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, 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()
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (l *LocalBitcoins) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(l.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return l.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (l *LocalBitcoins) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := l.GetOrderbook(p.GetSecondCurrency().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price})
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price})
|
||||
}
|
||||
|
||||
orderbook.ProcessOrderbook(l.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(l.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// LocalBitcoins exchange
|
||||
func (l *LocalBitcoins) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = l.GetName()
|
||||
accountBalance, err := l.GetWalletBalance()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = "BTC"
|
||||
exchangeCurrency.TotalValue = accountBalance.Total.Balance
|
||||
|
||||
|
||||
49
exchanges/nonce/nonce.go
Normal file
49
exchanges/nonce/nonce.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package nonce
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Nonce struct holds the nonce value
|
||||
type Nonce struct {
|
||||
n int64
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
// Inc increments the nonce value
|
||||
func (n *Nonce) Inc() {
|
||||
n.mtx.Lock()
|
||||
n.n++
|
||||
n.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Get retrives the nonce value
|
||||
func (n *Nonce) Get() int64 {
|
||||
n.mtx.Lock()
|
||||
defer n.mtx.Unlock()
|
||||
return n.n
|
||||
}
|
||||
|
||||
// GetInc increments and returns the value of the nonce
|
||||
func (n *Nonce) GetInc() int64 {
|
||||
n.mtx.Lock()
|
||||
defer n.mtx.Unlock()
|
||||
n.n++
|
||||
return n.n
|
||||
}
|
||||
|
||||
// Set sets the nonce value
|
||||
func (n *Nonce) Set(val int64) {
|
||||
n.mtx.Lock()
|
||||
n.n = val
|
||||
n.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Returns a string version of the nonce
|
||||
func (n *Nonce) String() string {
|
||||
n.mtx.Lock()
|
||||
result := strconv.FormatInt(n.n, 10)
|
||||
n.mtx.Unlock()
|
||||
return result
|
||||
}
|
||||
75
exchanges/nonce/nonce_test.go
Normal file
75
exchanges/nonce/nonce_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package nonce
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestInc(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(1)
|
||||
nonce.Inc()
|
||||
expected := int64(2)
|
||||
result := nonce.Get()
|
||||
if result != expected {
|
||||
t.Errorf("Test failed. Expected %d got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(112321313)
|
||||
expected := int64(112321313)
|
||||
result := nonce.Get()
|
||||
if expected != result {
|
||||
t.Errorf("Test failed. Expected %d got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInc(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(1)
|
||||
expected := int64(2)
|
||||
result := nonce.GetInc()
|
||||
if expected != result {
|
||||
t.Errorf("Test failed. Expected %d got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(1)
|
||||
expected := int64(1)
|
||||
result := nonce.Get()
|
||||
if expected != result {
|
||||
t.Errorf("Test failed. Expected %d got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(12312313131)
|
||||
expected := "12312313131"
|
||||
result := nonce.String()
|
||||
if expected != result {
|
||||
t.Errorf("Test failed. Expected %s got %s", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonceConcurrency(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(12312)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
go nonce.Inc()
|
||||
}
|
||||
|
||||
// Allow sufficient time for all routines to finish
|
||||
time.Sleep(time.Second)
|
||||
|
||||
result := nonce.Get()
|
||||
expected := int64(12312 + 1000)
|
||||
if expected != result {
|
||||
t.Errorf("Test failed. Expected %d got %d", expected, result)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package okcoin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -70,13 +72,20 @@ var (
|
||||
)
|
||||
|
||||
type OKCoin struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
RESTErrors map[string]string
|
||||
WebsocketErrors map[string]string
|
||||
FuturesValues []string
|
||||
WebsocketConn *websocket.Conn
|
||||
}
|
||||
|
||||
func (o *OKCoin) setCurrencyPairFormats() {
|
||||
o.RequestCurrencyPairFormat.Delimiter = "_"
|
||||
o.RequestCurrencyPairFormat.Uppercase = false
|
||||
o.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
o.ConfigCurrencyPairFormat.Uppercase = true
|
||||
}
|
||||
|
||||
func (o *OKCoin) SetDefaults() {
|
||||
o.SetErrorDefaults()
|
||||
o.SetWebsocketErrorDefaults()
|
||||
@@ -85,16 +94,20 @@ func (o *OKCoin) SetDefaults() {
|
||||
o.Websocket = false
|
||||
o.RESTPollingDelay = 10
|
||||
o.FuturesValues = []string{"this_week", "next_week", "quarter"}
|
||||
o.AssetTypes = []string{ticker.Spot}
|
||||
|
||||
if !okcoinDefaultsSet {
|
||||
o.AssetTypes = append(o.AssetTypes, o.FuturesValues...)
|
||||
o.APIUrl = OKCOIN_API_URL
|
||||
o.Name = "OKCOIN International"
|
||||
o.WebsocketURL = OKCOIN_WEBSOCKET_URL
|
||||
okcoinDefaultsSet = true
|
||||
o.setCurrencyPairFormats()
|
||||
} else {
|
||||
o.APIUrl = OKCOIN_API_URL_CHINA
|
||||
o.Name = "OKCOIN China"
|
||||
o.WebsocketURL = OKCOIN_WEBSOCKET_URL_CHINA
|
||||
o.setCurrencyPairFormats()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +124,14 @@ func (o *OKCoin) Setup(exch config.ExchangeConfig) {
|
||||
o.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
o.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
o.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := o.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = o.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,6 +898,10 @@ func (o *OKCoin) GetFuturesUserPosition4Fix(symbol, contractType string) {
|
||||
}
|
||||
|
||||
func (o *OKCoin) SendAuthenticatedHTTPRequest(method string, v url.Values, result interface{}) (err error) {
|
||||
if !o.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, o.Name)
|
||||
}
|
||||
|
||||
v.Set("api_key", o.APIKey)
|
||||
hasher := common.GetMD5([]byte(v.Encode() + "&secret_key=" + o.APISecret))
|
||||
v.Set("sign", strings.ToUpper(common.HexEncodeToString(hasher)))
|
||||
@@ -898,13 +923,13 @@ func (o *OKCoin) SendAuthenticatedHTTPRequest(method string, v url.Values, resul
|
||||
}
|
||||
|
||||
if o.Verbose {
|
||||
log.Printf("Recieved raw: \n%s\n", resp)
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Unable to JSON Unmarshal response.")
|
||||
return errors.New("unable to JSON Unmarshal response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,21 +2,20 @@ package okcoin
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the OKCoin go routine
|
||||
func (o *OKCoin) Start() {
|
||||
go o.Run()
|
||||
}
|
||||
|
||||
// Run implements the OKCoin wrapper
|
||||
func (o *OKCoin) Run() {
|
||||
if o.Verbose {
|
||||
log.Printf("%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket), o.WebsocketURL)
|
||||
@@ -27,128 +26,113 @@ func (o *OKCoin) Run() {
|
||||
if o.Websocket {
|
||||
go o.WebsocketClient()
|
||||
}
|
||||
}
|
||||
|
||||
for o.Enabled {
|
||||
for _, x := range o.EnabledPairs {
|
||||
curr := pair.NewCurrencyPair(x[0:3], x[3:])
|
||||
curr.Delimiter = "_"
|
||||
if o.APIUrl == OKCOIN_API_URL {
|
||||
for _, y := range o.FuturesValues {
|
||||
futuresValue := y
|
||||
go func() {
|
||||
ticker, err := o.GetFuturesTicker(curr.Pair().Lower().String(), futuresValue)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("OKCoin Intl Futures %s (%s): Last %f High %f Low %f Volume %f\n", curr.Pair().String(), futuresValue, ticker.Last, ticker.High, ticker.Low, ticker.Vol)
|
||||
stats.AddExchangeInfo(o.GetName(), curr.GetFirstCurrency().String(), curr.GetSecondCurrency().String(), ticker.Last, ticker.Vol)
|
||||
}()
|
||||
}
|
||||
go func() {
|
||||
ticker, err := o.GetTickerPrice(curr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Printf("OKCoin Intl Spot %s: Last %f High %f Low %f Volume %f\n", curr.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(o.GetName(), curr.GetFirstCurrency().String(), curr.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
ticker, err := o.GetTickerPrice(curr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
tickerLastUSD, _ := currency.ConvertCurrency(ticker.Last, "CNY", "USD")
|
||||
tickerHighUSD, _ := currency.ConvertCurrency(ticker.High, "CNY", "USD")
|
||||
tickerLowUSD, _ := currency.ConvertCurrency(ticker.Low, "CNY", "USD")
|
||||
log.Printf("OKCoin China %s: Last %f (%f) High %f (%f) Low %f (%f) Volume %f\n", curr.Pair().String(), tickerLastUSD, ticker.Last, tickerHighUSD, ticker.High, tickerLowUSD, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(o.GetName(), curr.GetFirstCurrency().String(), curr.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
stats.AddExchangeInfo(o.GetName(), curr.GetFirstCurrency().String(), "USD", tickerLastUSD, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (o *OKCoin) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
currency := exchange.FormatExchangeCurrency(o.Name, p).String()
|
||||
var tickerPrice ticker.Price
|
||||
|
||||
if assetType != ticker.Spot && o.APIUrl == OKCOIN_API_URL {
|
||||
tick, err := o.GetFuturesTicker(currency, assetType)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
time.Sleep(time.Second * o.RESTPollingDelay)
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Sell
|
||||
tickerPrice.Bid = tick.Buy
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(o.GetName(), p, tickerPrice, assetType)
|
||||
} else {
|
||||
tick, err := o.GetTicker(currency)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Sell
|
||||
tickerPrice.Bid = tick.Buy
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(o.GetName(), p, tickerPrice, ticker.Spot)
|
||||
|
||||
}
|
||||
return ticker.GetTicker(o.Name, p, assetType)
|
||||
}
|
||||
|
||||
func (o *OKCoin) GetTickerPrice(currency pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
tickerNew, err := ticker.GetTicker(o.GetName(), currency)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
tick, err := o.GetTicker(currency.Pair().Lower().String())
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (o *OKCoin) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(o.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
return o.UpdateTicker(p, assetType)
|
||||
}
|
||||
tickerPrice.Pair = currency
|
||||
tickerPrice.Ask = tick.Sell
|
||||
tickerPrice.Bid = tick.Buy
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Last
|
||||
tickerPrice.Volume = tick.Vol
|
||||
tickerPrice.High = tick.High
|
||||
ticker.ProcessTicker(o.GetName(), currency, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
func (o *OKCoin) GetOrderbookEx(currency pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
ob, err := orderbook.GetOrderbook(o.GetName(), currency)
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (o *OKCoin) GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(o.GetName(), currency, assetType)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
return o.UpdateOrderbook(currency, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
orderbookNew, err := o.GetOrderBook(currency.Pair().Lower().String(), 200, false)
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (o *OKCoin) UpdateOrderbook(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := o.GetOrderBook(exchange.FormatExchangeCurrency(o.Name, currency).String(), 200, false)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
|
||||
}
|
||||
orderBook.Pair = currency
|
||||
orderbook.ProcessOrderbook(o.GetName(), currency, orderBook)
|
||||
return orderBook, nil
|
||||
|
||||
orderbook.ProcessOrderbook(o.GetName(), currency, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(o.Name, currency, assetType)
|
||||
}
|
||||
|
||||
func (e *OKCoin) GetExchangeAccountInfo() (exchange.ExchangeAccountInfo, error) {
|
||||
var response exchange.ExchangeAccountInfo
|
||||
response.ExchangeName = e.GetName()
|
||||
assets, err := e.GetUserInfo()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// OKCoin exchange
|
||||
func (o *OKCoin) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = o.GetName()
|
||||
assets, err := o.GetUserInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "BTC",
|
||||
TotalValue: assets.Info.Funds.Free.BTC,
|
||||
Hold: assets.Info.Funds.Freezed.BTC,
|
||||
})
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "LTC",
|
||||
TotalValue: assets.Info.Funds.Free.LTC,
|
||||
Hold: assets.Info.Funds.Freezed.LTC,
|
||||
})
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "USD",
|
||||
TotalValue: assets.Info.Funds.Free.USD,
|
||||
Hold: assets.Info.Funds.Freezed.USD,
|
||||
})
|
||||
|
||||
response.Currencies = append(response.Currencies, exchange.ExchangeAccountCurrencyInfo{
|
||||
response.Currencies = append(response.Currencies, exchange.AccountCurrencyInfo{
|
||||
CurrencyName: "CNY",
|
||||
TotalValue: assets.Info.Funds.Free.CNY,
|
||||
Hold: assets.Info.Funds.Freezed.CNY,
|
||||
|
||||
@@ -7,33 +7,44 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
)
|
||||
|
||||
var (
|
||||
// Const values for orderbook package
|
||||
const (
|
||||
ErrOrderbookForExchangeNotFound = "Ticker for exchange does not exist."
|
||||
ErrPrimaryCurrencyNotFound = "Error primary currency for orderbook not found."
|
||||
ErrSecondaryCurrencyNotFound = "Error secondary currency for orderbook not found."
|
||||
|
||||
Spot = "SPOT"
|
||||
)
|
||||
|
||||
// Vars for the orderbook package
|
||||
var (
|
||||
Orderbooks []Orderbook
|
||||
)
|
||||
|
||||
type OrderbookItem struct {
|
||||
// Item stores the amount and price values
|
||||
type Item struct {
|
||||
Amount float64
|
||||
Price float64
|
||||
}
|
||||
|
||||
type OrderbookBase struct {
|
||||
// Base holds the fields for the orderbook base
|
||||
type Base struct {
|
||||
Pair pair.CurrencyPair `json:"pair"`
|
||||
CurrencyPair string `json:"CurrencyPair"`
|
||||
Bids []OrderbookItem `json:"bids"`
|
||||
Asks []OrderbookItem `json:"asks"`
|
||||
Bids []Item `json:"bids"`
|
||||
Asks []Item `json:"asks"`
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
}
|
||||
|
||||
// Orderbook holds the orderbook information for a currency pair and type
|
||||
type Orderbook struct {
|
||||
Orderbook map[pair.CurrencyItem]map[pair.CurrencyItem]OrderbookBase
|
||||
Orderbook map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Base
|
||||
ExchangeName string
|
||||
}
|
||||
|
||||
func (o *OrderbookBase) CalculateTotalBids() (float64, float64) {
|
||||
// CalculateTotalBids returns the total amount of bids and the total orderbook
|
||||
// bids value
|
||||
func (o *Base) CalculateTotalBids() (float64, float64) {
|
||||
amountCollated := float64(0)
|
||||
total := float64(0)
|
||||
for _, x := range o.Bids {
|
||||
@@ -43,7 +54,9 @@ func (o *OrderbookBase) CalculateTotalBids() (float64, float64) {
|
||||
return amountCollated, total
|
||||
}
|
||||
|
||||
func (o *OrderbookBase) CalculateTotalAsks() (float64, float64) {
|
||||
// CalculateTotalAsks returns the total amount of asks and the total orderbook
|
||||
// asks value
|
||||
func (o *Base) CalculateTotalAsks() (float64, float64) {
|
||||
amountCollated := float64(0)
|
||||
total := float64(0)
|
||||
for _, x := range o.Asks {
|
||||
@@ -53,29 +66,33 @@ func (o *OrderbookBase) CalculateTotalAsks() (float64, float64) {
|
||||
return amountCollated, total
|
||||
}
|
||||
|
||||
func (o *OrderbookBase) Update(Bids, Asks []OrderbookItem) {
|
||||
// Update updates the bids and asks
|
||||
func (o *Base) Update(Bids, Asks []Item) {
|
||||
o.Bids = Bids
|
||||
o.Asks = Asks
|
||||
o.LastUpdated = time.Now()
|
||||
}
|
||||
|
||||
func GetOrderbook(exchange string, p pair.CurrencyPair) (OrderbookBase, error) {
|
||||
// GetOrderbook checks and returns the orderbook given an exchange name and
|
||||
// currency pair if it exists
|
||||
func GetOrderbook(exchange string, p pair.CurrencyPair, orderbookType string) (Base, error) {
|
||||
orderbook, err := GetOrderbookByExchange(exchange)
|
||||
if err != nil {
|
||||
return OrderbookBase{}, err
|
||||
return Base{}, err
|
||||
}
|
||||
|
||||
if !FirstCurrencyExists(exchange, p.GetFirstCurrency()) {
|
||||
return OrderbookBase{}, errors.New(ErrPrimaryCurrencyNotFound)
|
||||
return Base{}, errors.New(ErrPrimaryCurrencyNotFound)
|
||||
}
|
||||
|
||||
if !SecondCurrencyExists(exchange, p) {
|
||||
return OrderbookBase{}, errors.New(ErrSecondaryCurrencyNotFound)
|
||||
return Base{}, errors.New(ErrSecondaryCurrencyNotFound)
|
||||
}
|
||||
|
||||
return orderbook.Orderbook[p.GetFirstCurrency()][p.GetSecondCurrency()], nil
|
||||
return orderbook.Orderbook[p.GetFirstCurrency()][p.GetSecondCurrency()][orderbookType], nil
|
||||
}
|
||||
|
||||
// GetOrderbookByExchange returns an exchange orderbook
|
||||
func GetOrderbookByExchange(exchange string) (*Orderbook, error) {
|
||||
for _, y := range Orderbooks {
|
||||
if y.ExchangeName == exchange {
|
||||
@@ -85,6 +102,8 @@ func GetOrderbookByExchange(exchange string) (*Orderbook, error) {
|
||||
return nil, errors.New(ErrOrderbookForExchangeNotFound)
|
||||
}
|
||||
|
||||
// FirstCurrencyExists checks to see if the first currency of the orderbook map
|
||||
// exists
|
||||
func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool {
|
||||
for _, y := range Orderbooks {
|
||||
if y.ExchangeName == exchange {
|
||||
@@ -96,6 +115,8 @@ func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SecondCurrencyExists checks to see if the second currency of the orderbook
|
||||
// map exists
|
||||
func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool {
|
||||
for _, y := range Orderbooks {
|
||||
if y.ExchangeName == exchange {
|
||||
@@ -109,39 +130,51 @@ func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateNewOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew OrderbookBase) Orderbook {
|
||||
// CreateNewOrderbook creates a new orderbook
|
||||
func CreateNewOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Base, orderbookType string) Orderbook {
|
||||
orderbook := Orderbook{}
|
||||
orderbook.ExchangeName = exchangeName
|
||||
orderbook.Orderbook = make(map[pair.CurrencyItem]map[pair.CurrencyItem]OrderbookBase)
|
||||
sMap := make(map[pair.CurrencyItem]OrderbookBase)
|
||||
sMap[p.GetSecondCurrency()] = orderbookNew
|
||||
orderbook.Orderbook[p.GetFirstCurrency()] = sMap
|
||||
orderbook.Orderbook = make(map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Base)
|
||||
a := make(map[pair.CurrencyItem]map[string]Base)
|
||||
b := make(map[string]Base)
|
||||
b[orderbookType] = orderbookNew
|
||||
a[p.SecondCurrency] = b
|
||||
orderbook.Orderbook[p.FirstCurrency] = a
|
||||
Orderbooks = append(Orderbooks, orderbook)
|
||||
return orderbook
|
||||
}
|
||||
|
||||
func ProcessOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew OrderbookBase) {
|
||||
// ProcessOrderbook processes incoming orderbooks, creating or updating the
|
||||
// Orderbook list
|
||||
func ProcessOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Base, orderbookType string) {
|
||||
orderbookNew.CurrencyPair = p.Pair().String()
|
||||
orderbookNew.LastUpdated = time.Now()
|
||||
|
||||
if len(Orderbooks) == 0 {
|
||||
CreateNewOrderbook(exchangeName, p, orderbookNew)
|
||||
CreateNewOrderbook(exchangeName, p, orderbookNew, orderbookType)
|
||||
return
|
||||
} else {
|
||||
orderbook, err := GetOrderbookByExchange(exchangeName)
|
||||
if err != nil {
|
||||
CreateNewOrderbook(exchangeName, p, orderbookNew)
|
||||
}
|
||||
|
||||
orderbook, err := GetOrderbookByExchange(exchangeName)
|
||||
if err != nil {
|
||||
CreateNewOrderbook(exchangeName, p, orderbookNew, orderbookType)
|
||||
return
|
||||
}
|
||||
|
||||
if FirstCurrencyExists(exchangeName, p.GetFirstCurrency()) {
|
||||
if !SecondCurrencyExists(exchangeName, p) {
|
||||
a := orderbook.Orderbook[p.FirstCurrency]
|
||||
b := make(map[string]Base)
|
||||
b[orderbookType] = orderbookNew
|
||||
a[p.SecondCurrency] = b
|
||||
orderbook.Orderbook[p.FirstCurrency] = a
|
||||
return
|
||||
}
|
||||
|
||||
if FirstCurrencyExists(exchangeName, p.GetFirstCurrency()) {
|
||||
if !SecondCurrencyExists(exchangeName, p) {
|
||||
second := orderbook.Orderbook[p.GetFirstCurrency()]
|
||||
second[p.GetSecondCurrency()] = orderbookNew
|
||||
orderbook.Orderbook[p.GetFirstCurrency()] = second
|
||||
return
|
||||
}
|
||||
}
|
||||
sMap := make(map[pair.CurrencyItem]OrderbookBase)
|
||||
sMap[p.GetSecondCurrency()] = orderbookNew
|
||||
orderbook.Orderbook[p.GetFirstCurrency()] = sMap
|
||||
}
|
||||
|
||||
a := make(map[pair.CurrencyItem]map[string]Base)
|
||||
b := make(map[string]Base)
|
||||
b[orderbookType] = orderbookNew
|
||||
a[p.SecondCurrency] = b
|
||||
orderbook.Orderbook[p.FirstCurrency] = a
|
||||
}
|
||||
|
||||
266
exchanges/orderbook/orderbook_test.go
Normal file
266
exchanges/orderbook/orderbook_test.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package orderbook
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
)
|
||||
|
||||
func TestCalculateTotalBids(t *testing.T) {
|
||||
t.Parallel()
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Bids: []Item{Item{Price: 100, Amount: 10}},
|
||||
LastUpdated: time.Now(),
|
||||
}
|
||||
|
||||
a, b := base.CalculateTotalBids()
|
||||
if a != 10 && b != 1000 {
|
||||
t.Fatal("Test failed. TestCalculateTotalBids expected a = 10 and b = 1000")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateTotaAsks(t *testing.T) {
|
||||
t.Parallel()
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
LastUpdated: time.Now(),
|
||||
}
|
||||
|
||||
a, b := base.CalculateTotalAsks()
|
||||
if a != 10 && b != 1000 {
|
||||
t.Fatal("Test failed. TestCalculateTotalAsks expected a = 10 and b = 1000")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
timeNow := time.Now()
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
Bids: []Item{Item{Price: 200, Amount: 10}},
|
||||
LastUpdated: timeNow,
|
||||
}
|
||||
|
||||
asks := []Item{Item{Price: 200, Amount: 101}}
|
||||
bids := []Item{Item{Price: 201, Amount: 100}}
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
base.Update(bids, asks)
|
||||
|
||||
if !base.LastUpdated.After(timeNow) {
|
||||
t.Fatal("test failed. TestUpdate expected LastUpdated to be greater then original time")
|
||||
}
|
||||
|
||||
a, b := base.CalculateTotalAsks()
|
||||
if a != 100 && b != 20200 {
|
||||
t.Fatal("Test failed. TestUpdate expected a = 100 and b = 20100")
|
||||
}
|
||||
|
||||
a, b = base.CalculateTotalBids()
|
||||
if a != 100 && b != 20100 {
|
||||
t.Fatal("Test failed. TestUpdate expected a = 100 and b = 20100")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
Bids: []Item{Item{Price: 200, Amount: 10}},
|
||||
}
|
||||
|
||||
CreateNewOrderbook("Exchange", currency, base, Spot)
|
||||
|
||||
result, err := GetOrderbook("Exchange", currency, Spot)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestGetOrderbook failed to get orderbook. Error %s",
|
||||
err)
|
||||
}
|
||||
|
||||
if result.Pair.Pair() != currency.Pair() {
|
||||
t.Fatal("Test failed. TestGetOrderbook failed. Mismatched pairs")
|
||||
}
|
||||
|
||||
_, err = GetOrderbook("nonexistant", currency, Spot)
|
||||
if err == nil {
|
||||
t.Fatal("Test failed. TestGetOrderbook retrieved non-existant orderbook")
|
||||
}
|
||||
|
||||
currency.FirstCurrency = "blah"
|
||||
_, err = GetOrderbook("Exchange", currency, Spot)
|
||||
if err == nil {
|
||||
t.Fatal("Test failed. TestGetOrderbook retrieved non-existant orderbook using invalid first currency")
|
||||
}
|
||||
|
||||
newCurrency := pair.NewCurrencyPair("BTC", "AUD")
|
||||
_, err = GetOrderbook("Exchange", newCurrency, Spot)
|
||||
if err == nil {
|
||||
t.Fatal("Test failed. TestGetOrderbook retrieved non-existant orderbook using invalid second currency")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbookByExchange(t *testing.T) {
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
Bids: []Item{Item{Price: 200, Amount: 10}},
|
||||
}
|
||||
|
||||
CreateNewOrderbook("Exchange", currency, base, Spot)
|
||||
|
||||
_, err := GetOrderbookByExchange("Exchange")
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed. TestGetOrderbookByExchange failed to get orderbook. Error %s",
|
||||
err)
|
||||
}
|
||||
|
||||
_, err = GetOrderbookByExchange("nonexistant")
|
||||
if err == nil {
|
||||
t.Fatal("Test failed. TestGetOrderbookByExchange retrieved non-existant orderbook")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFirstCurrencyExists(t *testing.T) {
|
||||
currency := pair.NewCurrencyPair("BTC", "AUD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
Bids: []Item{Item{Price: 200, Amount: 10}},
|
||||
}
|
||||
|
||||
CreateNewOrderbook("Exchange", currency, base, Spot)
|
||||
|
||||
if !FirstCurrencyExists("Exchange", currency.FirstCurrency) {
|
||||
t.Fatal("Test failed. TestFirstCurrencyExists expected first currency doesn't exist")
|
||||
}
|
||||
|
||||
var item pair.CurrencyItem = "blah"
|
||||
if FirstCurrencyExists("Exchange", item) {
|
||||
t.Fatal("Test failed. TestFirstCurrencyExists unexpected first currency exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecondCurrencyExists(t *testing.T) {
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
Bids: []Item{Item{Price: 200, Amount: 10}},
|
||||
}
|
||||
|
||||
CreateNewOrderbook("Exchange", currency, base, Spot)
|
||||
|
||||
if !SecondCurrencyExists("Exchange", currency) {
|
||||
t.Fatal("Test failed. TestSecondCurrencyExists expected first currency doesn't exist")
|
||||
}
|
||||
|
||||
currency.SecondCurrency = "blah"
|
||||
if SecondCurrencyExists("Exchange", currency) {
|
||||
t.Fatal("Test failed. TestSecondCurrencyExists unexpected first currency exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateNewOrderbook(t *testing.T) {
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
Bids: []Item{Item{Price: 200, Amount: 10}},
|
||||
}
|
||||
|
||||
CreateNewOrderbook("Exchange", currency, base, Spot)
|
||||
|
||||
result, err := GetOrderbook("Exchange", currency, Spot)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestCreateNewOrderbook failed to create new orderbook")
|
||||
}
|
||||
|
||||
if result.Pair.Pair() != currency.Pair() {
|
||||
t.Fatal("Test failed. TestCreateNewOrderbook result pair is incorrect")
|
||||
}
|
||||
|
||||
a, b := result.CalculateTotalAsks()
|
||||
if a != 10 && b != 1000 {
|
||||
t.Fatal("Test failed. TestCreateNewOrderbook CalculateTotalAsks value is incorrect")
|
||||
}
|
||||
|
||||
a, b = result.CalculateTotalBids()
|
||||
if a != 10 && b != 2000 {
|
||||
t.Fatal("Test failed. TestCreateNewOrderbook CalculateTotalBids value is incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessOrderbook(t *testing.T) {
|
||||
Orderbooks = []Orderbook{}
|
||||
currency := pair.NewCurrencyPair("BTC", "USD")
|
||||
base := Base{
|
||||
Pair: currency,
|
||||
CurrencyPair: currency.Pair().String(),
|
||||
Asks: []Item{Item{Price: 100, Amount: 10}},
|
||||
Bids: []Item{Item{Price: 200, Amount: 10}},
|
||||
}
|
||||
|
||||
ProcessOrderbook("Exchange", currency, base, Spot)
|
||||
|
||||
result, err := GetOrderbook("Exchange", currency, Spot)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestProcessOrderbook failed to create new orderbook")
|
||||
}
|
||||
|
||||
if result.Pair.Pair() != currency.Pair() {
|
||||
t.Fatal("Test failed. TestProcessOrderbook result pair is incorrect")
|
||||
}
|
||||
|
||||
currency = pair.NewCurrencyPair("BTC", "GBP")
|
||||
base.Pair = currency
|
||||
ProcessOrderbook("Exchange", currency, base, Spot)
|
||||
|
||||
result, err = GetOrderbook("Exchange", currency, Spot)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestProcessOrderbook failed to retrieve new orderbook")
|
||||
}
|
||||
|
||||
if result.Pair.Pair() != currency.Pair() {
|
||||
t.Fatal("Test failed. TestProcessOrderbook result pair is incorrect")
|
||||
}
|
||||
|
||||
base.Asks = []Item{Item{Price: 200, Amount: 200}}
|
||||
ProcessOrderbook("Exchange", currency, base, "monthly")
|
||||
|
||||
result, err = GetOrderbook("Exchange", currency, "monthly")
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestProcessOrderbook failed to retrieve new orderbook")
|
||||
}
|
||||
|
||||
a, b := result.CalculateTotalAsks()
|
||||
if a != 200 && b != 40000 {
|
||||
t.Fatal("Test failed. TestProcessOrderbook CalculateTotalsAsks incorrect values")
|
||||
}
|
||||
|
||||
base.Bids = []Item{Item{Price: 420, Amount: 200}}
|
||||
ProcessOrderbook("Blah", currency, base, "quarterly")
|
||||
result, err = GetOrderbook("Blah", currency, "quarterly")
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestProcessOrderbook failed to create new orderbook")
|
||||
}
|
||||
|
||||
if a != 200 && b != 84000 {
|
||||
t.Fatal("Test failed. TestProcessOrderbook CalculateTotalsBids incorrect values")
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -47,7 +49,7 @@ const (
|
||||
)
|
||||
|
||||
type Poloniex struct {
|
||||
exchange.ExchangeBase
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
func (p *Poloniex) SetDefaults() {
|
||||
@@ -57,6 +59,11 @@ func (p *Poloniex) SetDefaults() {
|
||||
p.Verbose = false
|
||||
p.Websocket = false
|
||||
p.RESTPollingDelay = 10
|
||||
p.RequestCurrencyPairFormat.Delimiter = "_"
|
||||
p.RequestCurrencyPairFormat.Uppercase = true
|
||||
p.ConfigCurrencyPairFormat.Delimiter = "_"
|
||||
p.ConfigCurrencyPairFormat.Uppercase = true
|
||||
p.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
func (p *Poloniex) Setup(exch config.ExchangeConfig) {
|
||||
@@ -72,6 +79,14 @@ func (p *Poloniex) Setup(exch config.ExchangeConfig) {
|
||||
p.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
p.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
p.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := p.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = p.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,16 +137,22 @@ func (p *Poloniex) GetOrderbook(currencyPair string, depth int) (PoloniexOrderbo
|
||||
}
|
||||
|
||||
ob := PoloniexOrderbook{}
|
||||
for x, _ := range resp.Asks {
|
||||
for x := range resp.Asks {
|
||||
data := resp.Asks[x]
|
||||
price, _ := strconv.ParseFloat(data[0].(string), 64)
|
||||
price, err := strconv.ParseFloat(data[0].(string), 64)
|
||||
if err != nil {
|
||||
return ob, err
|
||||
}
|
||||
amount := data[1].(float64)
|
||||
ob.Asks = append(ob.Asks, PoloniexOrderbookItem{Price: price, Amount: amount})
|
||||
}
|
||||
|
||||
for x, _ := range resp.Bids {
|
||||
data := resp.Asks[x]
|
||||
price, _ := strconv.ParseFloat(data[0].(string), 64)
|
||||
for x := range resp.Bids {
|
||||
data := resp.Bids[x]
|
||||
price, err := strconv.ParseFloat(data[0].(string), 64)
|
||||
if err != nil {
|
||||
return ob, err
|
||||
}
|
||||
amount := data[1].(float64)
|
||||
ob.Bids = append(ob.Bids, PoloniexOrderbookItem{Price: price, Amount: amount})
|
||||
}
|
||||
@@ -745,17 +766,22 @@ func (p *Poloniex) ToggleAutoRenew(orderNumber int64) (bool, error) {
|
||||
}
|
||||
|
||||
func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, result interface{}) error {
|
||||
if !p.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, p.Name)
|
||||
}
|
||||
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)
|
||||
if p.Nonce.Get() == 0 {
|
||||
p.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
p.Nonce.Inc()
|
||||
}
|
||||
values.Set("nonce", p.Nonce.String())
|
||||
values.Set("command", endpoint)
|
||||
|
||||
hmac := common.GetHMAC(common.HASH_SHA512, []byte(values.Encode()), []byte(p.APISecret))
|
||||
hmac := common.GetHMAC(common.HashSHA512, []byte(values.Encode()), []byte(p.APISecret))
|
||||
headers["Sign"] = common.HexEncodeToString(hmac)
|
||||
|
||||
path := fmt.Sprintf("%s/%s", POLONIEX_API_URL, POLONIEX_API_TRADING_ENDPOINT)
|
||||
|
||||
@@ -2,20 +2,20 @@ package poloniex
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Poloniex go routine
|
||||
func (p *Poloniex) Start() {
|
||||
go p.Run()
|
||||
}
|
||||
|
||||
// Run implements the Poloniex wrapper
|
||||
func (p *Poloniex) Run() {
|
||||
if p.Verbose {
|
||||
log.Printf("%s Websocket: %s (url: %s).\n", p.GetName(), common.IsEnabled(p.Websocket), POLONIEX_WEBSOCKET_ADDRESS)
|
||||
@@ -26,86 +26,83 @@ func (p *Poloniex) Run() {
|
||||
if p.Websocket {
|
||||
go p.WebsocketClient()
|
||||
}
|
||||
|
||||
for p.Enabled {
|
||||
for _, x := range p.EnabledPairs {
|
||||
currency := pair.NewCurrencyPairDelimiter(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.Pair().String(), ticker.Last, ticker.High, ticker.Low, ticker.Volume)
|
||||
stats.AddExchangeInfo(p.GetName(), currency.GetFirstCurrency().String(), currency.GetSecondCurrency().String(), ticker.Last, ticker.Volume)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Second * p.RESTPollingDelay)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetTickerPrice(currencyPair pair.CurrencyPair) (ticker.TickerPrice, error) {
|
||||
currency := currencyPair.Pair().String()
|
||||
tickerNew, err := ticker.GetTicker(p.GetName(), currencyPair)
|
||||
if err == nil {
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var tickerPrice ticker.TickerPrice
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (p *Poloniex) UpdateTicker(currencyPair pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := p.GetTicker()
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = currencyPair
|
||||
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(), currencyPair, tickerPrice)
|
||||
return tickerPrice, nil
|
||||
for _, x := range p.GetEnabledCurrencies() {
|
||||
var tp ticker.Price
|
||||
curr := exchange.FormatExchangeCurrency(p.GetName(), x).String()
|
||||
tp.Pair = x
|
||||
tp.Ask = tick[curr].LowestAsk
|
||||
tp.Bid = tick[curr].HighestBid
|
||||
tp.High = tick[curr].High24Hr
|
||||
tp.Last = tick[curr].Last
|
||||
tp.Low = tick[curr].Low24Hr
|
||||
tp.Volume = tick[curr].BaseVolume
|
||||
ticker.ProcessTicker(p.GetName(), x, tp, assetType)
|
||||
}
|
||||
return ticker.GetTicker(p.Name, currencyPair, assetType)
|
||||
}
|
||||
|
||||
func (p *Poloniex) GetOrderbookEx(currencyPair pair.CurrencyPair) (orderbook.OrderbookBase, error) {
|
||||
currency := currencyPair.Pair().String()
|
||||
ob, err := orderbook.GetOrderbook(p.GetName(), currencyPair)
|
||||
if err == nil {
|
||||
return ob, nil
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (p *Poloniex) GetTickerPrice(currencyPair pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(p.GetName(), currencyPair, assetType)
|
||||
if err != nil {
|
||||
return p.UpdateTicker(currencyPair, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
var orderBook orderbook.OrderbookBase
|
||||
orderbookNew, err := p.GetOrderbook(currency, 1000)
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (p *Poloniex) GetOrderbookEx(currencyPair pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(p.GetName(), currencyPair, assetType)
|
||||
if err == nil {
|
||||
return p.UpdateOrderbook(currencyPair, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (p *Poloniex) UpdateOrderbook(currencyPair pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := p.GetOrderbook(exchange.FormatExchangeCurrency(p.GetName(), currencyPair).String(), 1000)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Bids {
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price})
|
||||
}
|
||||
|
||||
for x, _ := range orderbookNew.Asks {
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price})
|
||||
}
|
||||
orderBook.Pair = currencyPair
|
||||
orderbook.ProcessOrderbook(p.GetName(), currencyPair, orderBook)
|
||||
return orderBook, nil
|
||||
|
||||
orderbook.ProcessOrderbook(p.GetName(), currencyPair, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(p.Name, currencyPair, assetType)
|
||||
}
|
||||
|
||||
//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()
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// Poloniex exchange
|
||||
func (p *Poloniex) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = p.GetName()
|
||||
accountBalance, err := p.GetBalances()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
for x, y := range accountBalance.Currency {
|
||||
var exchangeCurrency exchange.ExchangeAccountCurrencyInfo
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = x
|
||||
exchangeCurrency.TotalValue = y
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
|
||||
@@ -3,113 +3,134 @@ package stats
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
)
|
||||
|
||||
type ExchangeInfo struct {
|
||||
Exchange string
|
||||
FirstCurrency string
|
||||
FiatCurrency string
|
||||
Price float64
|
||||
Volume float64
|
||||
// Item holds various fields for storing currency pair stats
|
||||
type Item struct {
|
||||
Exchange string
|
||||
Pair pair.CurrencyPair
|
||||
AssetType string
|
||||
Price float64
|
||||
Volume float64
|
||||
}
|
||||
|
||||
var ExchInfo []ExchangeInfo
|
||||
// Items var array
|
||||
var Items []Item
|
||||
|
||||
type ByPrice []ExchangeInfo
|
||||
// ByPrice allows sorting by price
|
||||
type ByPrice []Item
|
||||
|
||||
func (this ByPrice) Len() int {
|
||||
return len(this)
|
||||
func (b ByPrice) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (this ByPrice) Less(i, j int) bool {
|
||||
return this[i].Price < this[j].Price
|
||||
func (b ByPrice) Less(i, j int) bool {
|
||||
return b[i].Price < b[j].Price
|
||||
}
|
||||
|
||||
func (this ByPrice) Swap(i, j int) {
|
||||
this[i], this[j] = this[j], this[i]
|
||||
func (b ByPrice) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
type ByVolume []ExchangeInfo
|
||||
// ByVolume allows sorting by volume
|
||||
type ByVolume []Item
|
||||
|
||||
func (this ByVolume) Len() int {
|
||||
return len(this)
|
||||
func (b ByVolume) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (this ByVolume) Less(i, j int) bool {
|
||||
return this[i].Volume < this[j].Volume
|
||||
func (b ByVolume) Less(i, j int) bool {
|
||||
return b[i].Volume < b[j].Volume
|
||||
}
|
||||
|
||||
func (this ByVolume) Swap(i, j int) {
|
||||
this[i], this[j] = this[j], this[i]
|
||||
func (b ByVolume) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
func AddExchangeInfo(exchange, crypto, fiat string, price, volume float64) {
|
||||
if currency.BaseCurrencies == "" {
|
||||
currency.BaseCurrencies = currency.DEFAULT_CURRENCIES
|
||||
}
|
||||
|
||||
if !currency.IsFiatCurrency(fiat) {
|
||||
return
|
||||
}
|
||||
AppendExchangeInfo(exchange, crypto, fiat, price, volume)
|
||||
|
||||
}
|
||||
|
||||
func AppendExchangeInfo(exchange, crypto, fiat string, price, volume float64) {
|
||||
if ExchangeInfoAlreadyExists(exchange, crypto, fiat, price, volume) {
|
||||
// Add adds or updates the item stats
|
||||
func Add(exchange string, p pair.CurrencyPair, assetType string, price, volume float64) {
|
||||
if exchange == "" || assetType == "" || price == 0 || volume == 0 || p.FirstCurrency == "" || p.SecondCurrency == "" {
|
||||
return
|
||||
}
|
||||
|
||||
exch := ExchangeInfo{}
|
||||
exch.Exchange = exchange
|
||||
exch.FirstCurrency = crypto
|
||||
exch.FiatCurrency = fiat
|
||||
exch.Price = price
|
||||
exch.Volume = volume
|
||||
ExchInfo = append(ExchInfo, exch)
|
||||
if p.FirstCurrency == "XBT" {
|
||||
newPair := pair.NewCurrencyPair("BTC", p.SecondCurrency.String())
|
||||
Append(exchange, newPair, assetType, price, volume)
|
||||
}
|
||||
|
||||
if p.SecondCurrency == "USDT" {
|
||||
newPair := pair.NewCurrencyPair(p.FirstCurrency.String(), "USD")
|
||||
Append(exchange, newPair, assetType, price, volume)
|
||||
}
|
||||
|
||||
Append(exchange, p, assetType, price, volume)
|
||||
}
|
||||
|
||||
func ExchangeInfoAlreadyExists(exchange, crypto, fiat string, price, volume float64) bool {
|
||||
for i, _ := range ExchInfo {
|
||||
if ExchInfo[i].Exchange == exchange && ExchInfo[i].FirstCurrency == crypto && ExchInfo[i].FiatCurrency == fiat {
|
||||
ExchInfo[i].Price, ExchInfo[i].Volume = price, volume
|
||||
// Append adds or updates the item stats for a specific
|
||||
// currency pair and asset type
|
||||
func Append(exchange string, p pair.CurrencyPair, assetType string, price, volume float64) {
|
||||
if AlreadyExists(exchange, p, assetType, price, volume) {
|
||||
return
|
||||
}
|
||||
|
||||
i := Item{
|
||||
Exchange: exchange,
|
||||
Pair: p,
|
||||
AssetType: assetType,
|
||||
Price: price,
|
||||
Volume: volume,
|
||||
}
|
||||
|
||||
Items = append(Items, i)
|
||||
}
|
||||
|
||||
// AlreadyExists checks to see if item info already exists
|
||||
// for a specific currency pair and asset type
|
||||
func AlreadyExists(exchange string, p pair.CurrencyPair, assetType string, price, volume float64) bool {
|
||||
for i := range Items {
|
||||
if Items[i].Exchange == exchange && Items[i].Pair.Equal(p) && Items[i].AssetType == assetType {
|
||||
Items[i].Price, Items[i].Volume = price, volume
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func SortExchangesByVolume(crypto, fiat string, reverse bool) []ExchangeInfo {
|
||||
info := []ExchangeInfo{}
|
||||
|
||||
for _, x := range ExchInfo {
|
||||
if x.FirstCurrency == crypto && x.FiatCurrency == fiat {
|
||||
info = append(info, x)
|
||||
// SortExchangesByVolume sorts item info by volume for a specific
|
||||
// currency pair and asset type. Reverse will reverse the order from lowest to
|
||||
// highest
|
||||
func SortExchangesByVolume(p pair.CurrencyPair, assetType string, reverse bool) []Item {
|
||||
var result []Item
|
||||
for x := range Items {
|
||||
if Items[x].Pair.Equal(p) && Items[x].AssetType == assetType {
|
||||
result = append(result, Items[x])
|
||||
}
|
||||
}
|
||||
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByVolume(info)))
|
||||
sort.Sort(sort.Reverse(ByVolume(result)))
|
||||
} else {
|
||||
sort.Sort(ByVolume(info))
|
||||
sort.Sort(ByVolume(result))
|
||||
}
|
||||
return info
|
||||
return result
|
||||
}
|
||||
|
||||
func SortExchangesByPrice(crypto, fiat string, reverse bool) []ExchangeInfo {
|
||||
info := []ExchangeInfo{}
|
||||
|
||||
for _, x := range ExchInfo {
|
||||
if x.FirstCurrency == crypto && x.FiatCurrency == fiat {
|
||||
info = append(info, x)
|
||||
// SortExchangesByPrice sorts item info by volume for a specific
|
||||
// currency pair and asset type. Reverse will reverse the order from lowest to
|
||||
// highest
|
||||
func SortExchangesByPrice(p pair.CurrencyPair, assetType string, reverse bool) []Item {
|
||||
var result []Item
|
||||
for x := range Items {
|
||||
if Items[x].Pair.Equal(p) && Items[x].AssetType == assetType {
|
||||
result = append(result, Items[x])
|
||||
}
|
||||
}
|
||||
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByPrice(info)))
|
||||
sort.Sort(sort.Reverse(ByPrice(result)))
|
||||
} else {
|
||||
sort.Sort(ByPrice(info))
|
||||
sort.Sort(ByPrice(result))
|
||||
}
|
||||
return info
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -2,138 +2,180 @@ package stats
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
)
|
||||
|
||||
func TestLenByPrice(t *testing.T) {
|
||||
exchangeInfo := ExchangeInfo{
|
||||
Exchange: "ANX",
|
||||
FirstCurrency: "BTC",
|
||||
FiatCurrency: "USD",
|
||||
Price: 1200,
|
||||
Volume: 5,
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
i := Item{
|
||||
Exchange: "ANX",
|
||||
Pair: p,
|
||||
AssetType: "SPOT",
|
||||
Price: 1200,
|
||||
Volume: 5,
|
||||
}
|
||||
|
||||
ExchInfo = append(ExchInfo, exchangeInfo)
|
||||
if ByPrice.Len(ExchInfo) < 1 {
|
||||
Items = append(Items, i)
|
||||
if ByPrice.Len(Items) < 1 {
|
||||
t.Error("Test Failed - stats LenByPrice() length not correct.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLessByPrice(t *testing.T) {
|
||||
exchangeInfo := ExchangeInfo{
|
||||
Exchange: "alphapoint",
|
||||
FirstCurrency: "BTC",
|
||||
FiatCurrency: "USD",
|
||||
Price: 1200,
|
||||
Volume: 5,
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
i := Item{
|
||||
Exchange: "alphapoint",
|
||||
Pair: p,
|
||||
AssetType: "SPOT",
|
||||
Price: 1200,
|
||||
Volume: 5,
|
||||
}
|
||||
|
||||
exchangeInfo2 := ExchangeInfo{
|
||||
Exchange: "bitfinex",
|
||||
FirstCurrency: "BTC",
|
||||
FiatCurrency: "USD",
|
||||
Price: 1198,
|
||||
Volume: 20,
|
||||
i2 := Item{
|
||||
Exchange: "bitfinex",
|
||||
Pair: p,
|
||||
AssetType: "SPOT",
|
||||
Price: 1198,
|
||||
Volume: 20,
|
||||
}
|
||||
|
||||
ExchInfo = append(ExchInfo, exchangeInfo)
|
||||
ExchInfo = append(ExchInfo, exchangeInfo2)
|
||||
Items = append(Items, i)
|
||||
Items = append(Items, i2)
|
||||
|
||||
if !ByPrice.Less(ExchInfo, 2, 1) {
|
||||
if !ByPrice.Less(Items, 2, 1) {
|
||||
t.Error("Test Failed - stats LessByPrice() incorrect return.")
|
||||
}
|
||||
if ByPrice.Less(ExchInfo, 1, 2) {
|
||||
if ByPrice.Less(Items, 1, 2) {
|
||||
t.Error("Test Failed - stats LessByPrice() incorrect return.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwapByPrice(t *testing.T) {
|
||||
exchangeInfo := ExchangeInfo{
|
||||
Exchange: "bitstamp",
|
||||
FirstCurrency: "BTC",
|
||||
FiatCurrency: "USD",
|
||||
Price: 1324,
|
||||
Volume: 5,
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
i := Item{
|
||||
Exchange: "bitstamp",
|
||||
Pair: p,
|
||||
AssetType: "SPOT",
|
||||
Price: 1324,
|
||||
Volume: 5,
|
||||
}
|
||||
|
||||
exchangeInfo2 := ExchangeInfo{
|
||||
Exchange: "btcc",
|
||||
FirstCurrency: "BTC",
|
||||
FiatCurrency: "USD",
|
||||
Price: 7863,
|
||||
Volume: 20,
|
||||
i2 := Item{
|
||||
Exchange: "btcc",
|
||||
Pair: p,
|
||||
AssetType: "SPOT",
|
||||
Price: 7863,
|
||||
Volume: 20,
|
||||
}
|
||||
|
||||
ExchInfo = append(ExchInfo, exchangeInfo)
|
||||
ExchInfo = append(ExchInfo, exchangeInfo2)
|
||||
ByPrice.Swap(ExchInfo, 3, 4)
|
||||
if ExchInfo[3].Exchange != "btcc" || ExchInfo[4].Exchange != "bitstamp" {
|
||||
Items = append(Items, i)
|
||||
Items = append(Items, i2)
|
||||
ByPrice.Swap(Items, 3, 4)
|
||||
if Items[3].Exchange != "btcc" || Items[4].Exchange != "bitstamp" {
|
||||
t.Error("Test Failed - stats SwapByPrice did not swap values.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLenByVolume(t *testing.T) {
|
||||
if ByVolume.Len(ExchInfo) != 5 {
|
||||
if ByVolume.Len(Items) != 5 {
|
||||
t.Error("Test Failed - stats lenByVolume did not swap values.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLessByVolume(t *testing.T) {
|
||||
if !ByVolume.Less(ExchInfo, 1, 2) {
|
||||
if !ByVolume.Less(Items, 1, 2) {
|
||||
t.Error("Test Failed - stats LessByVolume() incorrect return.")
|
||||
}
|
||||
if ByVolume.Less(ExchInfo, 2, 1) {
|
||||
if ByVolume.Less(Items, 2, 1) {
|
||||
t.Error("Test Failed - stats LessByVolume() incorrect return.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwapByVolume(t *testing.T) {
|
||||
ByPrice.Swap(ExchInfo, 3, 4)
|
||||
ByPrice.Swap(Items, 3, 4)
|
||||
|
||||
if ExchInfo[4].Exchange != "btcc" || ExchInfo[3].Exchange != "bitstamp" {
|
||||
if Items[4].Exchange != "btcc" || Items[3].Exchange != "bitstamp" {
|
||||
t.Error("Test Failed - stats SwapByVolume did not swap values.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddExchangeInfo(t *testing.T) {
|
||||
ExchInfo = ExchInfo[:0]
|
||||
AddExchangeInfo("ANX", "BTC", "USD", 1200, 42)
|
||||
func TestAdd(t *testing.T) {
|
||||
Items = Items[:0]
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
Add("ANX", p, "SPOT", 1200, 42)
|
||||
|
||||
if len(ExchInfo) < 1 {
|
||||
t.Error("Test Failed - stats AddExchangeInfo did not add exchange info.")
|
||||
if len(Items) < 1 {
|
||||
t.Error("Test Failed - stats Add did not add exchange info.")
|
||||
}
|
||||
|
||||
Add("", p, "", 0, 0)
|
||||
|
||||
if len(Items) != 1 {
|
||||
t.Error("Test Failed - stats Add did not add exchange info.")
|
||||
}
|
||||
|
||||
p.FirstCurrency = "XBT"
|
||||
Add("ANX", p, "SPOT", 1201, 43)
|
||||
|
||||
if Items[1].Pair.Pair() != "XBTUSD" {
|
||||
t.Fatal("Test failed. stats Add did not add exchange info.")
|
||||
}
|
||||
|
||||
p = pair.NewCurrencyPair("ETH", "USDT")
|
||||
Add("ANX", p, "SPOT", 300, 1000)
|
||||
|
||||
if Items[2].Pair.Pair() != "ETHUSD" {
|
||||
t.Fatal("Test failed. stats Add did not add exchange info.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendExchangeInfo(t *testing.T) {
|
||||
AppendExchangeInfo("sillyexchange", "BTC", "USD", 1234, 45)
|
||||
if len(ExchInfo) < 2 {
|
||||
t.Error("Test Failed - stats AppendExchangeInfo did not add exchange values.")
|
||||
func TestAppend(t *testing.T) {
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
Append("sillyexchange", p, "SPOT", 1234, 45)
|
||||
if len(Items) < 2 {
|
||||
t.Error("Test Failed - stats Append did not add exchange values.")
|
||||
}
|
||||
AppendExchangeInfo("sillyexchange", "BTC", "USD", 1234, 45)
|
||||
if len(ExchInfo) == 3 {
|
||||
t.Error("Test Failed - stats AppendExchangeInfo added exchange values")
|
||||
|
||||
Append("sillyexchange", p, "SPOT", 1234, 45)
|
||||
if len(Items) == 3 {
|
||||
t.Error("Test Failed - stats Append added exchange values")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeInfoAlreadyExists(t *testing.T) {
|
||||
if !ExchangeInfoAlreadyExists("ANX", "BTC", "USD", 1200, 42) {
|
||||
t.Error("Test Failed - stats ExchangeInfoAlreadyExists exchange does not exist.")
|
||||
func TestAlreadyExists(t *testing.T) {
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
if !AlreadyExists("ANX", p, "SPOT", 1200, 42) {
|
||||
t.Error("Test Failed - stats AlreadyExists exchange does not exist.")
|
||||
}
|
||||
if ExchangeInfoAlreadyExists("bla", "dii", "USD", 1234, 123) {
|
||||
t.Error("Test Failed - stats ExchangeInfoAlreadyExists found incorrect exchange.")
|
||||
p.FirstCurrency = "dii"
|
||||
if AlreadyExists("bla", p, "SPOT", 1234, 123) {
|
||||
t.Error("Test Failed - stats AlreadyExists found incorrect exchange.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortExchangesByVolume(t *testing.T) {
|
||||
topVolume := SortExchangesByVolume("BTC", "USD", true)
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
topVolume := SortExchangesByVolume(p, "SPOT", true)
|
||||
if topVolume[0].Exchange != "sillyexchange" {
|
||||
t.Error("Test Failed - stats SortExchangesByVolume incorrectly sorted values.")
|
||||
}
|
||||
|
||||
topVolume = SortExchangesByVolume(p, "SPOT", false)
|
||||
if topVolume[0].Exchange != "ANX" {
|
||||
t.Error("Test Failed - stats SortExchangesByVolume incorrectly sorted values.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortExchangesByPrice(t *testing.T) {
|
||||
topPrice := SortExchangesByPrice("BTC", "USD", true)
|
||||
p := pair.NewCurrencyPair("BTC", "USD")
|
||||
topPrice := SortExchangesByPrice(p, "SPOT", true)
|
||||
if topPrice[0].Exchange != "sillyexchange" {
|
||||
t.Error("Test Failed - stats SortExchangesByPrice incorrectly sorted values.")
|
||||
}
|
||||
|
||||
topPrice = SortExchangesByPrice(p, "SPOT", false)
|
||||
if topPrice[0].Exchange != "ANX" {
|
||||
t.Error("Test Failed - stats SortExchangesByPrice incorrectly sorted values.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,22 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
)
|
||||
|
||||
var (
|
||||
// Const values for the ticker package
|
||||
const (
|
||||
ErrTickerForExchangeNotFound = "Ticker for exchange does not exist."
|
||||
ErrPrimaryCurrencyNotFound = "Error primary currency for ticker not found."
|
||||
ErrSecondaryCurrencyNotFound = "Error secondary currency for ticker not found."
|
||||
|
||||
Spot = "SPOT"
|
||||
)
|
||||
|
||||
// Vars for the ticker package
|
||||
var (
|
||||
Tickers []Ticker
|
||||
)
|
||||
|
||||
type TickerPrice struct {
|
||||
// Price struct stores the currency pair and pricing information
|
||||
type Price struct {
|
||||
Pair pair.CurrencyPair `json:"Pair"`
|
||||
CurrencyPair string `json:"CurrencyPair"`
|
||||
Last float64 `json:"Last"`
|
||||
@@ -28,50 +35,55 @@ type TickerPrice struct {
|
||||
PriceATH float64 `json:"PriceATH"`
|
||||
}
|
||||
|
||||
// Ticker struct holds the ticker information for a currency pair and type
|
||||
type Ticker struct {
|
||||
Price map[pair.CurrencyItem]map[pair.CurrencyItem]TickerPrice
|
||||
Price map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Price
|
||||
ExchangeName string
|
||||
}
|
||||
|
||||
func (t *Ticker) PriceToString(p pair.CurrencyPair, priceType string) string {
|
||||
// PriceToString returns the string version of a stored price field
|
||||
func (t *Ticker) PriceToString(p pair.CurrencyPair, priceType, tickerType string) string {
|
||||
priceType = common.StringToLower(priceType)
|
||||
|
||||
switch priceType {
|
||||
case "last":
|
||||
return strconv.FormatFloat(t.Price[p.GetFirstCurrency()][p.GetSecondCurrency()].Last, 'f', -1, 64)
|
||||
return strconv.FormatFloat(t.Price[p.FirstCurrency][p.SecondCurrency][tickerType].Last, 'f', -1, 64)
|
||||
case "high":
|
||||
return strconv.FormatFloat(t.Price[p.GetFirstCurrency()][p.GetSecondCurrency()].High, 'f', -1, 64)
|
||||
return strconv.FormatFloat(t.Price[p.FirstCurrency][p.SecondCurrency][tickerType].High, 'f', -1, 64)
|
||||
case "low":
|
||||
return strconv.FormatFloat(t.Price[p.GetFirstCurrency()][p.GetSecondCurrency()].Low, 'f', -1, 64)
|
||||
return strconv.FormatFloat(t.Price[p.FirstCurrency][p.SecondCurrency][tickerType].Low, 'f', -1, 64)
|
||||
case "bid":
|
||||
return strconv.FormatFloat(t.Price[p.GetFirstCurrency()][p.GetSecondCurrency()].Bid, 'f', -1, 64)
|
||||
return strconv.FormatFloat(t.Price[p.FirstCurrency][p.SecondCurrency][tickerType].Bid, 'f', -1, 64)
|
||||
case "ask":
|
||||
return strconv.FormatFloat(t.Price[p.GetFirstCurrency()][p.GetSecondCurrency()].Ask, 'f', -1, 64)
|
||||
return strconv.FormatFloat(t.Price[p.FirstCurrency][p.SecondCurrency][tickerType].Ask, 'f', -1, 64)
|
||||
case "volume":
|
||||
return strconv.FormatFloat(t.Price[p.GetFirstCurrency()][p.GetSecondCurrency()].Volume, 'f', -1, 64)
|
||||
return strconv.FormatFloat(t.Price[p.FirstCurrency][p.SecondCurrency][tickerType].Volume, 'f', -1, 64)
|
||||
case "ath":
|
||||
return strconv.FormatFloat(t.Price[p.GetFirstCurrency()][p.GetSecondCurrency()].PriceATH, 'f', -1, 64)
|
||||
return strconv.FormatFloat(t.Price[p.FirstCurrency][p.SecondCurrency][tickerType].PriceATH, 'f', -1, 64)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func GetTicker(exchange string, p pair.CurrencyPair) (TickerPrice, error) {
|
||||
// GetTicker checks and returns a requested ticker if it exists
|
||||
func GetTicker(exchange string, p pair.CurrencyPair, tickerType string) (Price, error) {
|
||||
ticker, err := GetTickerByExchange(exchange)
|
||||
if err != nil {
|
||||
return TickerPrice{}, err
|
||||
return Price{}, err
|
||||
}
|
||||
|
||||
if !FirstCurrencyExists(exchange, p.GetFirstCurrency()) {
|
||||
return TickerPrice{}, errors.New(ErrPrimaryCurrencyNotFound)
|
||||
if !FirstCurrencyExists(exchange, p.FirstCurrency) {
|
||||
return Price{}, errors.New(ErrPrimaryCurrencyNotFound)
|
||||
}
|
||||
|
||||
if !SecondCurrencyExists(exchange, p) {
|
||||
return TickerPrice{}, errors.New(ErrSecondaryCurrencyNotFound)
|
||||
return Price{}, errors.New(ErrSecondaryCurrencyNotFound)
|
||||
}
|
||||
|
||||
return ticker.Price[p.GetFirstCurrency()][p.GetSecondCurrency()], nil
|
||||
return ticker.Price[p.FirstCurrency][p.SecondCurrency][tickerType], nil
|
||||
}
|
||||
|
||||
// GetTickerByExchange returns an exchange Ticker
|
||||
func GetTickerByExchange(exchange string) (*Ticker, error) {
|
||||
for _, y := range Tickers {
|
||||
if y.ExchangeName == exchange {
|
||||
@@ -81,6 +93,8 @@ func GetTickerByExchange(exchange string) (*Ticker, error) {
|
||||
return nil, errors.New(ErrTickerForExchangeNotFound)
|
||||
}
|
||||
|
||||
// FirstCurrencyExists checks to see if the first currency of the Price map
|
||||
// exists
|
||||
func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool {
|
||||
for _, y := range Tickers {
|
||||
if y.ExchangeName == exchange {
|
||||
@@ -92,6 +106,8 @@ func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SecondCurrencyExists checks to see if the second currency of the Price map
|
||||
// exists
|
||||
func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool {
|
||||
for _, y := range Tickers {
|
||||
if y.ExchangeName == exchange {
|
||||
@@ -105,40 +121,49 @@ func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateNewTicker(exchangeName string, p pair.CurrencyPair, tickerNew TickerPrice) Ticker {
|
||||
// CreateNewTicker creates a new Ticker
|
||||
func CreateNewTicker(exchangeName string, p pair.CurrencyPair, tickerNew Price, tickerType string) Ticker {
|
||||
ticker := Ticker{}
|
||||
ticker.ExchangeName = exchangeName
|
||||
ticker.Price = make(map[pair.CurrencyItem]map[pair.CurrencyItem]TickerPrice)
|
||||
sMap := make(map[pair.CurrencyItem]TickerPrice)
|
||||
sMap[p.GetSecondCurrency()] = tickerNew
|
||||
ticker.Price[p.GetFirstCurrency()] = sMap
|
||||
ticker.Price = make(map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Price)
|
||||
a := make(map[pair.CurrencyItem]map[string]Price)
|
||||
b := make(map[string]Price)
|
||||
b[tickerType] = tickerNew
|
||||
a[p.SecondCurrency] = b
|
||||
ticker.Price[p.FirstCurrency] = a
|
||||
Tickers = append(Tickers, ticker)
|
||||
return ticker
|
||||
}
|
||||
|
||||
func ProcessTicker(exchangeName string, p pair.CurrencyPair, tickerNew TickerPrice) {
|
||||
// ProcessTicker processes incoming tickers, creating or updating the Tickers
|
||||
// list
|
||||
func ProcessTicker(exchangeName string, p pair.CurrencyPair, tickerNew Price, tickerType string) {
|
||||
tickerNew.CurrencyPair = p.Pair().String()
|
||||
if len(Tickers) == 0 {
|
||||
CreateNewTicker(exchangeName, p, tickerNew)
|
||||
//issue - not appending
|
||||
CreateNewTicker(exchangeName, p, tickerNew, tickerType)
|
||||
return
|
||||
} else {
|
||||
ticker, err := GetTickerByExchange(exchangeName)
|
||||
if err != nil {
|
||||
CreateNewTicker(exchangeName, p, tickerNew)
|
||||
}
|
||||
|
||||
ticker, err := GetTickerByExchange(exchangeName)
|
||||
if err != nil {
|
||||
CreateNewTicker(exchangeName, p, tickerNew, tickerType)
|
||||
return
|
||||
}
|
||||
|
||||
if FirstCurrencyExists(exchangeName, p.FirstCurrency) {
|
||||
if !SecondCurrencyExists(exchangeName, p) {
|
||||
a := ticker.Price[p.FirstCurrency]
|
||||
b := make(map[string]Price)
|
||||
b[tickerType] = tickerNew
|
||||
a[p.SecondCurrency] = b
|
||||
ticker.Price[p.FirstCurrency] = a
|
||||
return
|
||||
}
|
||||
|
||||
if FirstCurrencyExists(exchangeName, p.GetFirstCurrency()) {
|
||||
if !SecondCurrencyExists(exchangeName, p) {
|
||||
second := ticker.Price[p.GetFirstCurrency()]
|
||||
second[p.GetSecondCurrency()] = tickerNew
|
||||
ticker.Price[p.GetFirstCurrency()] = second
|
||||
return
|
||||
}
|
||||
}
|
||||
sMap := make(map[pair.CurrencyItem]TickerPrice)
|
||||
sMap[p.GetSecondCurrency()] = tickerNew
|
||||
ticker.Price[p.GetFirstCurrency()] = sMap
|
||||
}
|
||||
|
||||
a := make(map[pair.CurrencyItem]map[string]Price)
|
||||
b := make(map[string]Price)
|
||||
b[tickerType] = tickerNew
|
||||
a[p.SecondCurrency] = b
|
||||
ticker.Price[p.FirstCurrency] = a
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@ import (
|
||||
)
|
||||
|
||||
func TestPriceToString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newPair := pair.NewCurrencyPair("BTC", "USD")
|
||||
priceStruct := TickerPrice{
|
||||
priceStruct := Price{
|
||||
Pair: newPair,
|
||||
CurrencyPair: newPair.Pair().String(),
|
||||
Last: 1200,
|
||||
@@ -23,39 +21,37 @@ func TestPriceToString(t *testing.T) {
|
||||
PriceATH: 1337,
|
||||
}
|
||||
|
||||
newTicker := CreateNewTicker("ANX", newPair, priceStruct)
|
||||
newTicker := CreateNewTicker("ANX", newPair, priceStruct, Spot)
|
||||
|
||||
if newTicker.PriceToString(newPair, "last") != "1200" {
|
||||
if newTicker.PriceToString(newPair, "last", Spot) != "1200" {
|
||||
t.Error("Test Failed - ticker PriceToString last value is incorrect")
|
||||
}
|
||||
if newTicker.PriceToString(newPair, "high") != "1298" {
|
||||
if newTicker.PriceToString(newPair, "high", Spot) != "1298" {
|
||||
t.Error("Test Failed - ticker PriceToString high value is incorrect")
|
||||
}
|
||||
if newTicker.PriceToString(newPair, "low") != "1148" {
|
||||
if newTicker.PriceToString(newPair, "low", Spot) != "1148" {
|
||||
t.Error("Test Failed - ticker PriceToString low value is incorrect")
|
||||
}
|
||||
if newTicker.PriceToString(newPair, "bid") != "1195" {
|
||||
if newTicker.PriceToString(newPair, "bid", Spot) != "1195" {
|
||||
t.Error("Test Failed - ticker PriceToString bid value is incorrect")
|
||||
}
|
||||
if newTicker.PriceToString(newPair, "ask") != "1220" {
|
||||
if newTicker.PriceToString(newPair, "ask", Spot) != "1220" {
|
||||
t.Error("Test Failed - ticker PriceToString ask value is incorrect")
|
||||
}
|
||||
if newTicker.PriceToString(newPair, "volume") != "5" {
|
||||
if newTicker.PriceToString(newPair, "volume", Spot) != "5" {
|
||||
t.Error("Test Failed - ticker PriceToString volume value is incorrect")
|
||||
}
|
||||
if newTicker.PriceToString(newPair, "ath") != "1337" {
|
||||
if newTicker.PriceToString(newPair, "ath", Spot) != "1337" {
|
||||
t.Error("Test Failed - ticker PriceToString ath value is incorrect")
|
||||
}
|
||||
if newTicker.PriceToString(newPair, "obtuse") != "" {
|
||||
if newTicker.PriceToString(newPair, "obtuse", Spot) != "" {
|
||||
t.Error("Test Failed - ticker PriceToString obtuse value is incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newPair := pair.NewCurrencyPair("BTC", "USD")
|
||||
priceStruct := TickerPrice{
|
||||
priceStruct := Price{
|
||||
Pair: newPair,
|
||||
CurrencyPair: newPair.Pair().String(),
|
||||
Last: 1200,
|
||||
@@ -67,23 +63,47 @@ func TestGetTicker(t *testing.T) {
|
||||
PriceATH: 1337,
|
||||
}
|
||||
|
||||
bitfinexTicker := CreateNewTicker("bitfinex", newPair, priceStruct)
|
||||
Tickers = append(Tickers, bitfinexTicker)
|
||||
|
||||
tickerPrice, err := GetTicker("bitfinex", newPair)
|
||||
ProcessTicker("bitfinex", newPair, priceStruct, Spot)
|
||||
tickerPrice, err := GetTicker("bitfinex", newPair, Spot)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Ticker GetTicker init error: %s", err)
|
||||
}
|
||||
if tickerPrice.CurrencyPair != "BTCUSD" {
|
||||
t.Error("Test Failed - ticker tickerPrice.CurrencyPair value is incorrect")
|
||||
}
|
||||
|
||||
_, err = GetTicker("blah", newPair, Spot)
|
||||
if err == nil {
|
||||
t.Fatal("Test Failed. TestGetTicker returned nil error on invalid exchange")
|
||||
}
|
||||
|
||||
newPair.FirstCurrency = "ETH"
|
||||
_, err = GetTicker("bitfinex", newPair, Spot)
|
||||
if err == nil {
|
||||
t.Fatal("Test Failed. TestGetTicker returned ticker for invalid first currency")
|
||||
}
|
||||
|
||||
btcltcPair := pair.NewCurrencyPair("BTC", "LTC")
|
||||
_, err = GetTicker("bitfinex", btcltcPair, Spot)
|
||||
if err == nil {
|
||||
t.Fatal("Test Failed. TestGetTicker returned ticker for invalid second currency")
|
||||
}
|
||||
|
||||
priceStruct.PriceATH = 9001
|
||||
ProcessTicker("bitfinex", newPair, priceStruct, "futures_3m")
|
||||
tickerPrice, err = GetTicker("bitfinex", newPair, "futures_3m")
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed - Ticker GetTicker init error: %s", err)
|
||||
}
|
||||
|
||||
if tickerPrice.PriceATH != 9001 {
|
||||
t.Error("Test Failed - ticker tickerPrice.PriceATH value is incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTickerByExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newPair := pair.NewCurrencyPair("BTC", "USD")
|
||||
priceStruct := TickerPrice{
|
||||
priceStruct := Price{
|
||||
Pair: newPair,
|
||||
CurrencyPair: newPair.Pair().String(),
|
||||
Last: 1200,
|
||||
@@ -95,7 +115,7 @@ func TestGetTickerByExchange(t *testing.T) {
|
||||
PriceATH: 1337,
|
||||
}
|
||||
|
||||
anxTicker := CreateNewTicker("ANX", newPair, priceStruct)
|
||||
anxTicker := CreateNewTicker("ANX", newPair, priceStruct, Spot)
|
||||
Tickers = append(Tickers, anxTicker)
|
||||
|
||||
tickerPtr, err := GetTickerByExchange("ANX")
|
||||
@@ -108,10 +128,8 @@ func TestGetTickerByExchange(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFirstCurrencyExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newPair := pair.NewCurrencyPair("BTC", "USD")
|
||||
priceStruct := TickerPrice{
|
||||
priceStruct := Price{
|
||||
Pair: newPair,
|
||||
CurrencyPair: newPair.Pair().String(),
|
||||
Last: 1200,
|
||||
@@ -123,7 +141,7 @@ func TestFirstCurrencyExists(t *testing.T) {
|
||||
PriceATH: 1337,
|
||||
}
|
||||
|
||||
alphaTicker := CreateNewTicker("alphapoint", newPair, priceStruct)
|
||||
alphaTicker := CreateNewTicker("alphapoint", newPair, priceStruct, Spot)
|
||||
Tickers = append(Tickers, alphaTicker)
|
||||
|
||||
if !FirstCurrencyExists("alphapoint", "BTC") {
|
||||
@@ -138,7 +156,7 @@ func TestSecondCurrencyExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newPair := pair.NewCurrencyPair("BTC", "USD")
|
||||
priceStruct := TickerPrice{
|
||||
priceStruct := Price{
|
||||
Pair: newPair,
|
||||
CurrencyPair: newPair.Pair().String(),
|
||||
Last: 1200,
|
||||
@@ -150,7 +168,7 @@ func TestSecondCurrencyExists(t *testing.T) {
|
||||
PriceATH: 1337,
|
||||
}
|
||||
|
||||
bitstampTicker := CreateNewTicker("bitstamp", newPair, priceStruct)
|
||||
bitstampTicker := CreateNewTicker("bitstamp", newPair, priceStruct, "SPOT")
|
||||
Tickers = append(Tickers, bitstampTicker)
|
||||
|
||||
if !SecondCurrencyExists("bitstamp", newPair) {
|
||||
@@ -164,10 +182,8 @@ func TestSecondCurrencyExists(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNewTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newPair := pair.NewCurrencyPair("BTC", "USD")
|
||||
priceStruct := TickerPrice{
|
||||
priceStruct := Price{
|
||||
Pair: newPair,
|
||||
CurrencyPair: newPair.Pair().String(),
|
||||
Last: 1200,
|
||||
@@ -179,7 +195,7 @@ func TestCreateNewTicker(t *testing.T) {
|
||||
PriceATH: 1337,
|
||||
}
|
||||
|
||||
newTicker := CreateNewTicker("ANX", newPair, priceStruct)
|
||||
newTicker := CreateNewTicker("ANX", newPair, priceStruct, Spot)
|
||||
|
||||
if reflect.ValueOf(newTicker).NumField() != 2 {
|
||||
t.Error("Test Failed - ticker CreateNewTicker struct change/or updated")
|
||||
@@ -191,40 +207,39 @@ func TestCreateNewTicker(t *testing.T) {
|
||||
t.Error("Test Failed - ticker CreateNewTicker.ExchangeName value is not ANX")
|
||||
}
|
||||
|
||||
if newTicker.Price["BTC"]["USD"].Pair.Pair().String() != "BTCUSD" {
|
||||
if newTicker.Price["BTC"]["USD"][Spot].Pair.Pair().String() != "BTCUSD" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].Pair.Pair().String() value is not expected 'BTCUSD'")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].Ask).String() != "float64" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].Ask).String() != "float64" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].Ask value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].Bid).String() != "float64" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].Bid).String() != "float64" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].Bid value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].CurrencyPair).String() != "string" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].CurrencyPair).String() != "string" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].CurrencyPair value is not a string")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].High).String() != "float64" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].High).String() != "float64" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].High value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].Last).String() != "float64" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].Last).String() != "float64" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].Last value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].Low).String() != "float64" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].Low).String() != "float64" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].Low value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].PriceATH).String() != "float64" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].PriceATH).String() != "float64" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].PriceATH value is not a float64")
|
||||
}
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"].Volume).String() != "float64" {
|
||||
if reflect.TypeOf(newTicker.Price["BTC"]["USD"][Spot].Volume).String() != "float64" {
|
||||
t.Error("Test Failed - ticker newTicker.Price[BTC][USD].Volume value is not a float64")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessTicker(t *testing.T) { //non-appending function to tickers
|
||||
t.Parallel()
|
||||
|
||||
Tickers = []Ticker{}
|
||||
newPair := pair.NewCurrencyPair("BTC", "USD")
|
||||
priceStruct := TickerPrice{
|
||||
priceStruct := Price{
|
||||
Pair: newPair,
|
||||
CurrencyPair: newPair.Pair().String(),
|
||||
Last: 1200,
|
||||
@@ -236,5 +251,28 @@ func TestProcessTicker(t *testing.T) { //non-appending function to tickers
|
||||
PriceATH: 1337,
|
||||
}
|
||||
|
||||
ProcessTicker("btcc", newPair, priceStruct)
|
||||
ProcessTicker("btcc", newPair, priceStruct, Spot)
|
||||
|
||||
result, err := GetTicker("btcc", newPair, Spot)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestProcessTicker failed to create and return a new ticker")
|
||||
}
|
||||
|
||||
if result.Pair.Pair() != newPair.Pair() {
|
||||
t.Fatal("Test failed. TestProcessTicker pair mismatch")
|
||||
}
|
||||
|
||||
secondPair := pair.NewCurrencyPair("BTC", "AUD")
|
||||
priceStruct.Pair = secondPair
|
||||
ProcessTicker("btcc", secondPair, priceStruct, Spot)
|
||||
|
||||
result, err = GetTicker("btcc", secondPair, Spot)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestProcessTicker failed to create and return a new ticker")
|
||||
}
|
||||
|
||||
result, err = GetTicker("btcc", newPair, Spot)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestProcessTicker failed to return an existing ticker")
|
||||
}
|
||||
}
|
||||
|
||||
393
exchanges/wex/wex.go
Normal file
393
exchanges/wex/wex.go
Normal file
@@ -0,0 +1,393 @@
|
||||
package wex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
wexAPIPublicURL = "https://wex.nz/api"
|
||||
wexAPIPrivateURL = "https://wex.nz/tapi"
|
||||
wexAPIPublicVersion = "3"
|
||||
wexAPIPrivateVersion = "1"
|
||||
wexInfo = "info"
|
||||
wexTicker = "ticker"
|
||||
wexDepth = "depth"
|
||||
wexTrades = "trades"
|
||||
wexAccountInfo = "getInfo"
|
||||
wexTrade = "Trade"
|
||||
wexActiveOrders = "ActiveOrders"
|
||||
wexOrderInfo = "OrderInfo"
|
||||
wexCancelOrder = "CancelOrder"
|
||||
wexTradeHistory = "TradeHistory"
|
||||
wexTransactionHistory = "TransHistory"
|
||||
wexWithdrawCoin = "WithdrawCoin"
|
||||
wexCoinDepositAddress = "CoinDepositAddress"
|
||||
wexCreateCoupon = "CreateCoupon"
|
||||
wexRedeemCoupon = "RedeemCoupon"
|
||||
)
|
||||
|
||||
// WEX is the overarching type across the wex package
|
||||
type WEX struct {
|
||||
exchange.Base
|
||||
Ticker map[string]Ticker
|
||||
}
|
||||
|
||||
// SetDefaults sets current default value for WEX
|
||||
func (w *WEX) SetDefaults() {
|
||||
w.Name = "WEX"
|
||||
w.Enabled = false
|
||||
w.Fee = 0.2
|
||||
w.Verbose = false
|
||||
w.Websocket = false
|
||||
w.RESTPollingDelay = 10
|
||||
w.Ticker = make(map[string]Ticker)
|
||||
w.RequestCurrencyPairFormat.Delimiter = "_"
|
||||
w.RequestCurrencyPairFormat.Uppercase = false
|
||||
w.RequestCurrencyPairFormat.Separator = "-"
|
||||
w.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
w.ConfigCurrencyPairFormat.Uppercase = true
|
||||
w.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
// Setup sets exchange configuration parameters for WEX
|
||||
func (w *WEX) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
w.SetEnabled(false)
|
||||
} else {
|
||||
w.Enabled = true
|
||||
w.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
w.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
w.RESTPollingDelay = exch.RESTPollingDelay
|
||||
w.Verbose = exch.Verbose
|
||||
w.Websocket = exch.Websocket
|
||||
w.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
w.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
w.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := w.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = w.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetFee returns the exchange fee
|
||||
func (w *WEX) GetFee() float64 {
|
||||
return w.Fee
|
||||
}
|
||||
|
||||
// GetInfo returns the WEX info
|
||||
func (w *WEX) GetInfo() (Info, error) {
|
||||
req := fmt.Sprintf("%s/%s/%s/", wexAPIPublicURL, wexAPIPublicVersion, wexInfo)
|
||||
resp := Info{}
|
||||
err := common.SendHTTPGetRequest(req, true, &resp)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetTicker returns a ticker for a specific currency
|
||||
func (w *WEX) GetTicker(symbol string) (map[string]Ticker, error) {
|
||||
type Response struct {
|
||||
Data map[string]Ticker
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexTicker, symbol)
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Data, nil
|
||||
}
|
||||
|
||||
// GetDepth returns the depth for a specific currency
|
||||
func (w *WEX) GetDepth(symbol string) (Orderbook, error) {
|
||||
type Response struct {
|
||||
Data map[string]Orderbook
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexDepth, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
if err != nil {
|
||||
return Orderbook{}, err
|
||||
}
|
||||
|
||||
depth := response.Data[symbol]
|
||||
return depth, nil
|
||||
}
|
||||
|
||||
// GetTrades returns the trades for a specific currency
|
||||
func (w *WEX) GetTrades(symbol string) ([]Trades, error) {
|
||||
type Response struct {
|
||||
Data map[string][]Trades
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexTrades, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, &response.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trades := response.Data[symbol]
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
// GetAccountInfo returns a users account info
|
||||
func (w *WEX) GetAccountInfo() (AccountInfo, error) {
|
||||
var result AccountInfo
|
||||
err := w.SendAuthenticatedHTTPRequest(wexAccountInfo, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetActiveOrders returns the active orders for a specific currency
|
||||
func (w *WEX) GetActiveOrders(pair string) (map[string]ActiveOrders, error) {
|
||||
req := url.Values{}
|
||||
req.Add("pair", pair)
|
||||
|
||||
var result map[string]ActiveOrders
|
||||
err := w.SendAuthenticatedHTTPRequest(wexActiveOrders, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetOrderInfo returns the order info for a specific order ID
|
||||
func (w *WEX) GetOrderInfo(OrderID int64) (map[string]OrderInfo, error) {
|
||||
req := url.Values{}
|
||||
req.Add("order_id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
var result map[string]OrderInfo
|
||||
err := w.SendAuthenticatedHTTPRequest(wexOrderInfo, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CancelOrder cancels an order for a specific order ID
|
||||
func (w *WEX) CancelOrder(OrderID int64) (bool, error) {
|
||||
req := url.Values{}
|
||||
req.Add("order_id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
var result CancelOrder
|
||||
err := w.SendAuthenticatedHTTPRequest(wexCancelOrder, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Trade places an order and returns the order ID if successful or an error
|
||||
func (w *WEX) Trade(pair, orderType string, amount, price float64) (int64, 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 Trade
|
||||
err := w.SendAuthenticatedHTTPRequest(wexTrade, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int64(result.OrderID), nil
|
||||
}
|
||||
|
||||
// GetTransactionHistory returns the transaction history
|
||||
func (w *WEX) GetTransactionHistory(TIDFrom, Count, TIDEnd int64, order, since, end string) (map[string]TransHistory, 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]TransHistory
|
||||
err := w.SendAuthenticatedHTTPRequest(wexTransactionHistory, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetTradeHistory returns the trade history
|
||||
func (w *WEX) GetTradeHistory(TIDFrom, Count, TIDEnd int64, order, since, end, pair string) (map[string]TradeHistory, 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]TradeHistory
|
||||
err := w.SendAuthenticatedHTTPRequest(wexTradeHistory, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// WithdrawCoins withdraws coins for a specific coin
|
||||
func (w *WEX) WithdrawCoins(coin string, amount float64, address string) (WithdrawCoins, error) {
|
||||
req := url.Values{}
|
||||
req.Add("coinName", coin)
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
req.Add("address", address)
|
||||
|
||||
var result WithdrawCoins
|
||||
err := w.SendAuthenticatedHTTPRequest(wexWithdrawCoin, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CoinDepositAddress returns the deposit address for a specific currency
|
||||
func (w *WEX) CoinDepositAddress(coin string) (string, error) {
|
||||
req := url.Values{}
|
||||
req.Add("coinName", coin)
|
||||
|
||||
var result CoinDepositAddress
|
||||
err := w.SendAuthenticatedHTTPRequest(wexCoinDepositAddress, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return result.Address, nil
|
||||
}
|
||||
|
||||
// CreateCoupon creates an exchange coupon for a sepcific currency
|
||||
func (w *WEX) CreateCoupon(currency string, amount float64) (CreateCoupon, error) {
|
||||
req := url.Values{}
|
||||
req.Add("currency", currency)
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
var result CreateCoupon
|
||||
err := w.SendAuthenticatedHTTPRequest(wexCreateCoupon, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// RedeemCoupon redeems an exchange coupon
|
||||
func (w *WEX) RedeemCoupon(coupon string) (RedeemCoupon, error) {
|
||||
req := url.Values{}
|
||||
req.Add("coupon", coupon)
|
||||
|
||||
var result RedeemCoupon
|
||||
err := w.SendAuthenticatedHTTPRequest(wexRedeemCoupon, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to WEX
|
||||
func (w *WEX) SendAuthenticatedHTTPRequest(method string, values url.Values, result interface{}) (err error) {
|
||||
if !w.AuthenticatedAPISupport {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, w.Name)
|
||||
}
|
||||
|
||||
if w.Nonce.Get() == 0 {
|
||||
w.Nonce.Set(time.Now().Unix())
|
||||
} else {
|
||||
w.Nonce.Inc()
|
||||
}
|
||||
values.Set("nonce", w.Nonce.String())
|
||||
values.Set("method", method)
|
||||
|
||||
encoded := values.Encode()
|
||||
hmac := common.GetHMAC(common.HashSHA512, []byte(encoded), []byte(w.APISecret))
|
||||
|
||||
if w.Verbose {
|
||||
log.Printf("Sending POST request to %s calling method %s with params %s\n", wexAPIPrivateURL, method, encoded)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Key"] = w.APIKey
|
||||
headers["Sign"] = common.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", wexAPIPrivateURL, headers, strings.NewReader(encoded))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
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
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
package btce
|
||||
package wex
|
||||
|
||||
type BTCeTicker struct {
|
||||
High float64
|
||||
Low float64
|
||||
Avg float64
|
||||
Vol float64
|
||||
Vol_cur float64
|
||||
Last float64
|
||||
Buy float64
|
||||
Sell float64
|
||||
Updated int64
|
||||
// Ticker stores the ticker information
|
||||
type Ticker struct {
|
||||
High float64
|
||||
Low float64
|
||||
Avg float64
|
||||
Vol float64
|
||||
VolumeCurrent float64 `json:"vol_cur"`
|
||||
Last float64
|
||||
Buy float64
|
||||
Sell float64
|
||||
Updated int64
|
||||
}
|
||||
|
||||
type BTCEOrderbook struct {
|
||||
// Orderbook stores the asks and bids orderbook information
|
||||
type Orderbook struct {
|
||||
Asks [][]float64 `json:"asks"`
|
||||
Bids [][]float64 `json:"bids"`
|
||||
}
|
||||
|
||||
type BTCETrades struct {
|
||||
// Trades stores trade information
|
||||
type Trades struct {
|
||||
Type string `json:"type"`
|
||||
Price float64 `json:"bid"`
|
||||
Amount float64 `json:"amount"`
|
||||
@@ -25,13 +28,15 @@ type BTCETrades struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BTCEResponse struct {
|
||||
// Response is a generic struct used for exchange API request result
|
||||
type Response struct {
|
||||
Return interface{} `json:"return"`
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type BTCEPair struct {
|
||||
// Pair holds pair information
|
||||
type Pair struct {
|
||||
DecimalPlaces int `json:"decimal_places"`
|
||||
MinPrice float64 `json:"min_price"`
|
||||
MaxPrice float64 `json:"max_price"`
|
||||
@@ -40,12 +45,14 @@ type BTCEPair struct {
|
||||
Fee float64 `json:"fee"`
|
||||
}
|
||||
|
||||
type BTCEInfo struct {
|
||||
ServerTime int64 `json:"server_time"`
|
||||
Pairs map[string]BTCEPair `json:"pairs"`
|
||||
// Info holds server time and pair information
|
||||
type Info struct {
|
||||
ServerTime int64 `json:"server_time"`
|
||||
Pairs map[string]Pair `json:"pairs"`
|
||||
}
|
||||
|
||||
type BTCEAccountInfo struct {
|
||||
// AccountInfo stores the account information for a user
|
||||
type AccountInfo struct {
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
OpenOrders int `json:"open_orders"`
|
||||
Rights struct {
|
||||
@@ -57,7 +64,8 @@ type BTCEAccountInfo struct {
|
||||
TransactionCount int `json:"transaction_count"`
|
||||
}
|
||||
|
||||
type BTCEActiveOrders struct {
|
||||
// ActiveOrders stores active order information
|
||||
type ActiveOrders struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
Amount float64 `json:"amount"`
|
||||
@@ -66,7 +74,8 @@ type BTCEActiveOrders struct {
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type BTCEOrderInfo struct {
|
||||
// OrderInfo stores order information
|
||||
type OrderInfo struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
StartAmount float64 `json:"start_amount"`
|
||||
@@ -76,19 +85,22 @@ type BTCEOrderInfo struct {
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type BTCECancelOrder struct {
|
||||
// CancelOrder is used for the CancelOrder API request response
|
||||
type CancelOrder struct {
|
||||
OrderID float64 `json:"order_id"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCETrade struct {
|
||||
// Trade stores the trade information
|
||||
type Trade struct {
|
||||
Received float64 `json:"received"`
|
||||
Remains float64 `json:"remains"`
|
||||
OrderID float64 `json:"order_id"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCETransHistory struct {
|
||||
// TransHistory stores transaction history
|
||||
type TransHistory struct {
|
||||
Type int `json:"type"`
|
||||
Amount float64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
@@ -97,7 +109,8 @@ type BTCETransHistory struct {
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BTCETradeHistory struct {
|
||||
// TradeHistory stores trade history
|
||||
type TradeHistory struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"type"`
|
||||
Amount float64 `json:"amount"`
|
||||
@@ -107,19 +120,27 @@ type BTCETradeHistory struct {
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type BTCEWithdrawCoins struct {
|
||||
// CoinDepositAddress stores a curency deposit address
|
||||
type CoinDepositAddress struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// WithdrawCoins stores information for a withdrawcoins request
|
||||
type WithdrawCoins struct {
|
||||
TID int64 `json:"tId"`
|
||||
AmountSent float64 `json:"amountSent"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCECreateCoupon struct {
|
||||
// CreateCoupon stores information coupon information
|
||||
type CreateCoupon struct {
|
||||
Coupon string `json:"coupon"`
|
||||
TransID int64 `json:"transID"`
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
}
|
||||
|
||||
type BTCERedeemCoupon struct {
|
||||
// RedeemCoupon stores redeem coupon information
|
||||
type RedeemCoupon struct {
|
||||
CouponAmount float64 `json:"couponAmount,string"`
|
||||
CouponCurrency string `json:"couponCurrency"`
|
||||
TransID int64 `json:"transID"`
|
||||
114
exchanges/wex/wex_wrapper.go
Normal file
114
exchanges/wex/wex_wrapper.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package wex
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the WEX go routine
|
||||
func (w *WEX) Start() {
|
||||
go w.Run()
|
||||
}
|
||||
|
||||
// Run implements the WEX wrapper
|
||||
func (w *WEX) Run() {
|
||||
if w.Verbose {
|
||||
log.Printf("%s Websocket: %s.", w.GetName(), common.IsEnabled(w.Websocket))
|
||||
log.Printf("%s polling delay: %ds.\n", w.GetName(), w.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", w.GetName(), len(w.EnabledPairs), w.EnabledPairs)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (w *WEX) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
pairsCollated, err := exchange.GetAndFormatExchangeCurrencies(w.Name, w.GetEnabledCurrencies())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
result, err := w.GetTicker(pairsCollated.String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
for _, x := range w.GetEnabledCurrencies() {
|
||||
currency := exchange.FormatExchangeCurrency(w.Name, x).Lower().String()
|
||||
var tp ticker.Price
|
||||
tp.Pair = x
|
||||
tp.Last = result[currency].Last
|
||||
tp.Ask = result[currency].Sell
|
||||
tp.Bid = result[currency].Buy
|
||||
tp.Last = result[currency].Last
|
||||
tp.Low = result[currency].Low
|
||||
tp.Volume = result[currency].VolumeCurrent
|
||||
ticker.ProcessTicker(w.Name, x, tp, assetType)
|
||||
}
|
||||
return ticker.GetTicker(w.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (w *WEX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tick, err := ticker.GetTicker(w.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return w.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (w *WEX) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(w.GetName(), p, assetType)
|
||||
if err == nil {
|
||||
return w.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (w *WEX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := w.GetDepth(exchange.FormatExchangeCurrency(w.Name, p).String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Bids {
|
||||
data := orderbookNew.Bids[x]
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: data[0], Amount: data[1]})
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Asks {
|
||||
data := orderbookNew.Asks[x]
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: data[0], Amount: data[1]})
|
||||
}
|
||||
|
||||
orderbook.ProcessOrderbook(w.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(w.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// WEX exchange
|
||||
func (w *WEX) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = w.GetName()
|
||||
accountBalance, err := w.GetAccountInfo()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
for x, y := range accountBalance.Funds {
|
||||
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||
exchangeCurrency.TotalValue = y
|
||||
exchangeCurrency.Hold = 0
|
||||
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
107
helpers.go
Normal file
107
helpers.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// GetSpecificOrderbook returns a specific orderbook given the currency,
|
||||
// exchangeName and assetType
|
||||
func GetSpecificOrderbook(currency, exchangeName, assetType string) (orderbook.Base, error) {
|
||||
var specificOrderbook orderbook.Base
|
||||
var err error
|
||||
for i := 0; i < len(bot.exchanges); i++ {
|
||||
if bot.exchanges[i] != nil {
|
||||
if bot.exchanges[i].IsEnabled() && bot.exchanges[i].GetName() == exchangeName {
|
||||
specificOrderbook, err = bot.exchanges[i].GetOrderbookEx(
|
||||
pair.NewCurrencyPairFromString(currency),
|
||||
assetType,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return specificOrderbook, err
|
||||
}
|
||||
|
||||
// GetSpecificTicker returns a specific ticker given the currency,
|
||||
// exchangeName and assetType
|
||||
func GetSpecificTicker(currency, exchangeName, assetType string) (ticker.Price, error) {
|
||||
var specificTicker ticker.Price
|
||||
var err error
|
||||
for i := 0; i < len(bot.exchanges); i++ {
|
||||
if bot.exchanges[i] != nil {
|
||||
if bot.exchanges[i].IsEnabled() && bot.exchanges[i].GetName() == exchangeName {
|
||||
specificTicker, err = bot.exchanges[i].GetTickerPrice(
|
||||
pair.NewCurrencyPairFromString(currency),
|
||||
assetType,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return specificTicker, err
|
||||
}
|
||||
|
||||
// GetCollatedExchangeAccountInfoByCoin collates individual exchange account
|
||||
// information and turns into into a map string of
|
||||
// exchange.AccountCurrencyInfo
|
||||
func GetCollatedExchangeAccountInfoByCoin(accounts []exchange.AccountInfo) map[string]exchange.AccountCurrencyInfo {
|
||||
result := make(map[string]exchange.AccountCurrencyInfo)
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
for j := 0; j < len(accounts[i].Currencies); j++ {
|
||||
currencyName := accounts[i].Currencies[j].CurrencyName
|
||||
avail := accounts[i].Currencies[j].TotalValue
|
||||
onHold := accounts[i].Currencies[j].Hold
|
||||
|
||||
info, ok := result[currencyName]
|
||||
if !ok {
|
||||
accountInfo := exchange.AccountCurrencyInfo{CurrencyName: currencyName, Hold: onHold, TotalValue: avail}
|
||||
result[currencyName] = accountInfo
|
||||
} else {
|
||||
info.Hold += onHold
|
||||
info.TotalValue += avail
|
||||
result[currencyName] = info
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetAccountCurrencyInfoByExchangeName returns info for an exchange
|
||||
func GetAccountCurrencyInfoByExchangeName(accounts []exchange.AccountInfo, exchangeName string) (exchange.AccountInfo, error) {
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
if accounts[i].ExchangeName == exchangeName {
|
||||
return accounts[i], nil
|
||||
}
|
||||
}
|
||||
return exchange.AccountInfo{}, errors.New(exchange.ErrExchangeNotFound)
|
||||
}
|
||||
|
||||
// GetExchangeHighestPriceByCurrencyPair returns the exchange with the highest
|
||||
// price for a given currency pair and asset type
|
||||
func GetExchangeHighestPriceByCurrencyPair(p pair.CurrencyPair, assetType string) (string, error) {
|
||||
result := stats.SortExchangesByPrice(p, assetType, true)
|
||||
if len(result) != 1 {
|
||||
return "", fmt.Errorf("no stats for supplied currency pair and asset type")
|
||||
}
|
||||
|
||||
return result[0].Exchange, nil
|
||||
}
|
||||
|
||||
// GetExchangeLowestPriceByCurrencyPair returns the exchange with the lowest
|
||||
// price for a given currency pair and asset type
|
||||
func GetExchangeLowestPriceByCurrencyPair(p pair.CurrencyPair, assetType string) (string, error) {
|
||||
result := stats.SortExchangesByPrice(p, assetType, false)
|
||||
if len(result) != 1 {
|
||||
return "", fmt.Errorf("no stats for supplied currency pair and asset type")
|
||||
}
|
||||
|
||||
return result[0].Exchange, nil
|
||||
}
|
||||
166
main.go
166
main.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -16,8 +17,8 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/anx"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bitfinex"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bitstamp"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bittrex"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/btcc"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/btce"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/btcmarkets"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/coinut"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/gdax"
|
||||
@@ -31,16 +32,19 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/okcoin"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/poloniex"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/wex"
|
||||
"github.com/thrasher-/gocryptotrader/portfolio"
|
||||
"github.com/thrasher-/gocryptotrader/smsglobal"
|
||||
)
|
||||
|
||||
// ExchangeMain contains all the necessary exchange packages
|
||||
type ExchangeMain struct {
|
||||
anx anx.ANX
|
||||
btcc btcc.BTCC
|
||||
bitstamp bitstamp.Bitstamp
|
||||
bitfinex bitfinex.Bitfinex
|
||||
btce btce.BTCE
|
||||
bittrex bittrex.Bittrex
|
||||
wex wex.WEX
|
||||
btcmarkets btcmarkets.BTCMarkets
|
||||
coinut coinut.COINUT
|
||||
gdax gdax.GDAX
|
||||
@@ -56,13 +60,17 @@ type ExchangeMain struct {
|
||||
kraken kraken.Kraken
|
||||
}
|
||||
|
||||
// Bot contains configuration, portfolio, exchange & ticker data and is the
|
||||
// overarching type across this code base.
|
||||
type Bot struct {
|
||||
config *config.Config
|
||||
portfolio *portfolio.PortfolioBase
|
||||
exchange ExchangeMain
|
||||
exchanges []exchange.IBotExchange
|
||||
tickers []ticker.Ticker
|
||||
shutdown chan bool
|
||||
config *config.Config
|
||||
smsglobal *smsglobal.Base
|
||||
portfolio *portfolio.Base
|
||||
exchange ExchangeMain
|
||||
exchanges []exchange.IBotExchange
|
||||
tickers []ticker.Ticker
|
||||
shutdown chan bool
|
||||
configFile string
|
||||
}
|
||||
|
||||
var bot Bot
|
||||
@@ -74,10 +82,18 @@ func setupBotExchanges() {
|
||||
if bot.exchanges[i].GetName() == exch.Name {
|
||||
bot.exchanges[i].Setup(exch)
|
||||
if bot.exchanges[i].IsEnabled() {
|
||||
log.Printf("%s: Exchange support: %s (Authenticated API support: %s - Verbose mode: %s).\n", exch.Name, common.IsEnabled(exch.Enabled), common.IsEnabled(exch.AuthenticatedAPISupport), common.IsEnabled(exch.Verbose))
|
||||
log.Printf(
|
||||
"%s: Exchange support: %s (Authenticated API support: %s - Verbose mode: %s).\n",
|
||||
exch.Name, common.IsEnabled(exch.Enabled),
|
||||
common.IsEnabled(exch.AuthenticatedAPISupport),
|
||||
common.IsEnabled(exch.Verbose),
|
||||
)
|
||||
bot.exchanges[i].Start()
|
||||
} else {
|
||||
log.Printf("%s: Exchange support: %s\n", exch.Name, common.IsEnabled(exch.Enabled))
|
||||
log.Printf(
|
||||
"%s: Exchange support: %s\n", exch.Name,
|
||||
common.IsEnabled(exch.Enabled),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,30 +103,38 @@ func setupBotExchanges() {
|
||||
|
||||
func main() {
|
||||
HandleInterrupt()
|
||||
bot.config = &config.Cfg
|
||||
log.Printf("Loading config file %s..\n", config.CONFIG_FILE)
|
||||
|
||||
err := bot.config.LoadConfig("")
|
||||
//Handle flags
|
||||
flag.StringVar(&bot.configFile, "config", config.GetFilePath(""), "config file to load")
|
||||
flag.Parse()
|
||||
|
||||
bot.config = &config.Cfg
|
||||
log.Printf("Loading config file %s..\n", bot.configFile)
|
||||
|
||||
err := bot.config.LoadConfig(bot.configFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("Bot '%s' started.\n", bot.config.Name)
|
||||
AdjustGoMaxProcs()
|
||||
log.Printf("Bot '%s' started.\n", bot.config.Name)
|
||||
log.Printf("Fiat display currency: %s.", bot.config.FiatDisplayCurrency)
|
||||
|
||||
if bot.config.SMS.Enabled {
|
||||
err = bot.config.CheckSMSGlobalConfigValues()
|
||||
if err != nil {
|
||||
log.Println(err) // non fatal event
|
||||
bot.config.SMS.Enabled = false
|
||||
} else {
|
||||
log.Printf("SMS support enabled. Number of SMS contacts %d.\n", smsglobal.GetEnabledSMSContacts(bot.config.SMS))
|
||||
}
|
||||
bot.smsglobal = smsglobal.New(bot.config.SMS.Username, bot.config.SMS.Password,
|
||||
bot.config.Name, bot.config.SMS.Contacts)
|
||||
log.Printf(
|
||||
"SMS support enabled. Number of SMS contacts %d.\n",
|
||||
bot.smsglobal.GetEnabledContacts(),
|
||||
)
|
||||
} else {
|
||||
log.Println("SMS support disabled.")
|
||||
}
|
||||
|
||||
log.Printf("Available Exchanges: %d. Enabled Exchanges: %d.\n", len(bot.config.Exchanges), bot.config.GetConfigEnabledExchanges())
|
||||
log.Printf(
|
||||
"Available Exchanges: %d. Enabled Exchanges: %d.\n",
|
||||
len(bot.config.Exchanges), bot.config.GetConfigEnabledExchanges(),
|
||||
)
|
||||
log.Println("Bot Exchange support:")
|
||||
|
||||
bot.exchanges = []exchange.IBotExchange{
|
||||
@@ -119,7 +143,8 @@ func main() {
|
||||
new(btcc.BTCC),
|
||||
new(bitstamp.Bitstamp),
|
||||
new(bitfinex.Bitfinex),
|
||||
new(btce.BTCE),
|
||||
new(bittrex.Bittrex),
|
||||
new(wex.WEX),
|
||||
new(btcmarkets.BTCMarkets),
|
||||
new(coinut.COINUT),
|
||||
new(gdax.GDAX),
|
||||
@@ -137,17 +162,33 @@ func main() {
|
||||
for i := 0; i < len(bot.exchanges); i++ {
|
||||
if bot.exchanges[i] != nil {
|
||||
bot.exchanges[i].SetDefaults()
|
||||
log.Printf("Exchange %s successfully set default settings.\n", bot.exchanges[i].GetName())
|
||||
log.Printf(
|
||||
"Exchange %s successfully set default settings.\n",
|
||||
bot.exchanges[i].GetName(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
setupBotExchanges()
|
||||
|
||||
bot.config.RetrieveConfigCurrencyPairs()
|
||||
if bot.config.CurrencyExchangeProvider == "yahoo" {
|
||||
currency.SetProvider(true)
|
||||
} else {
|
||||
currency.SetProvider(false)
|
||||
}
|
||||
|
||||
log.Printf("Using %s as currency exchange provider.", bot.config.CurrencyExchangeProvider)
|
||||
|
||||
bot.config.RetrieveConfigCurrencyPairs()
|
||||
err = currency.SeedCurrencyData(currency.BaseCurrencies)
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal error retrieving config currencies. Error: %s", err)
|
||||
currency.SwapProvider()
|
||||
log.Printf("'%s' currency exchange provider failed, swapping to %s and testing..",
|
||||
bot.config.CurrencyExchangeProvider, currency.GetProvider())
|
||||
err = currency.SeedCurrencyData(currency.BaseCurrencies)
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal error retrieving config currencies. Error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Successfully retrieved config currencies.")
|
||||
@@ -157,26 +198,29 @@ func main() {
|
||||
SeedExchangeAccountInfo(GetAllEnabledExchangeAccountInfo().Data)
|
||||
go portfolio.StartPortfolioWatcher()
|
||||
|
||||
log.Println("Starting websocket handler")
|
||||
go WebsocketHandler()
|
||||
|
||||
go TickerUpdaterRoutine()
|
||||
go OrderbookUpdaterRoutine()
|
||||
|
||||
if bot.config.Webserver.Enabled {
|
||||
err := bot.config.CheckWebserverConfigValues()
|
||||
if err != nil {
|
||||
log.Println(err) // non fatal event
|
||||
//bot.config.Webserver.Enabled = false
|
||||
} else {
|
||||
listenAddr := bot.config.Webserver.ListenAddress
|
||||
log.Printf("HTTP Webserver support enabled. Listen URL: http://%s:%d/\n", common.ExtractHost(listenAddr), common.ExtractPort(listenAddr))
|
||||
router := NewRouter(bot.exchanges)
|
||||
log.Fatal(http.ListenAndServe(listenAddr, router))
|
||||
}
|
||||
}
|
||||
if !bot.config.Webserver.Enabled {
|
||||
log.Println("HTTP Webserver support disabled.")
|
||||
listenAddr := bot.config.Webserver.ListenAddress
|
||||
log.Printf(
|
||||
"HTTP Webserver support enabled. Listen URL: http://%s:%d/\n",
|
||||
common.ExtractHost(listenAddr), common.ExtractPort(listenAddr),
|
||||
)
|
||||
router := NewRouter(bot.exchanges)
|
||||
log.Fatal(http.ListenAndServe(listenAddr, router))
|
||||
} else {
|
||||
log.Println("HTTP RESTful Webserver support disabled.")
|
||||
}
|
||||
|
||||
<-bot.shutdown
|
||||
Shutdown()
|
||||
}
|
||||
|
||||
// AdjustGoMaxProcs adjusts the maximum processes that the CPU can handle.
|
||||
func AdjustGoMaxProcs() {
|
||||
log.Println("Adjusting bot runtime performance..")
|
||||
maxProcsEnv := os.Getenv("GOMAXPROCS")
|
||||
@@ -186,17 +230,20 @@ func AdjustGoMaxProcs() {
|
||||
if maxProcsEnv != "" {
|
||||
log.Println("GOMAXPROCS env =", maxProcsEnv)
|
||||
env, err := strconv.Atoi(maxProcsEnv)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Unable to convert GOMAXPROCS to int, using", maxProcs)
|
||||
} else {
|
||||
maxProcs = env
|
||||
}
|
||||
}
|
||||
if i := runtime.GOMAXPROCS(maxProcs); i != maxProcs {
|
||||
log.Fatal("Go Max Procs were not set correctly.")
|
||||
}
|
||||
log.Println("Set GOMAXPROCS to:", maxProcs)
|
||||
runtime.GOMAXPROCS(maxProcs)
|
||||
}
|
||||
|
||||
// HandleInterrupt monitors and captures the SIGTERM in a new goroutine then
|
||||
// shuts down bot
|
||||
func HandleInterrupt() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
@@ -207,10 +254,11 @@ func HandleInterrupt() {
|
||||
}()
|
||||
}
|
||||
|
||||
// Shutdown correctly shuts down bot saving configuration files
|
||||
func Shutdown() {
|
||||
log.Println("Bot shutting down..")
|
||||
bot.config.Portfolio = portfolio.Portfolio
|
||||
err := bot.config.SaveConfig("")
|
||||
err := bot.config.SaveConfig(bot.configFile)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Unable to save config.")
|
||||
@@ -222,7 +270,8 @@ func Shutdown() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func SeedExchangeAccountInfo(data []exchange.ExchangeAccountInfo) {
|
||||
// SeedExchangeAccountInfo seeds account info
|
||||
func SeedExchangeAccountInfo(data []exchange.AccountInfo) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -237,14 +286,33 @@ func SeedExchangeAccountInfo(data []exchange.ExchangeAccountInfo) {
|
||||
avail := data[i].Currencies[j].TotalValue
|
||||
total := onHold + avail
|
||||
|
||||
if total <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !port.ExchangeAddressExists(exchangeName, currencyName) {
|
||||
port.Addresses = append(port.Addresses, portfolio.PortfolioAddress{Address: exchangeName, CoinType: currencyName, Balance: total, Decscription: portfolio.PORTFOLIO_ADDRESS_EXCHANGE})
|
||||
if total <= 0 {
|
||||
continue
|
||||
}
|
||||
log.Printf("Portfolio: Adding new exchange address: %s, %s, %f, %s\n",
|
||||
exchangeName, currencyName, total, portfolio.PortfolioAddressExchange)
|
||||
port.Addresses = append(
|
||||
port.Addresses,
|
||||
portfolio.Address{Address: exchangeName, CoinType: currencyName,
|
||||
Balance: total, Description: portfolio.PortfolioAddressExchange},
|
||||
)
|
||||
} else {
|
||||
port.UpdateExchangeAddressBalance(exchangeName, currencyName, total)
|
||||
if total <= 0 {
|
||||
log.Printf("Portfolio: Removing %s %s entry.\n", exchangeName,
|
||||
currencyName)
|
||||
port.RemoveExchangeAddress(exchangeName, currencyName)
|
||||
} else {
|
||||
balance, ok := port.GetAddressBalance(exchangeName, currencyName, portfolio.PortfolioAddressExchange)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if balance != total {
|
||||
log.Printf("Portfolio: Updating %s %s entry with balance %f.\n",
|
||||
exchangeName, currencyName, total)
|
||||
port.UpdateExchangeAddressBalance(exchangeName, currencyName, total)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user