Engine QA (#381)

* 1) Update Dockerfile/docker-compose.yml
2) Remove inline strings for buy/sell/test pairs
3) Remove dangerous order submission values
4) Fix consistency with audit_events (all other spec files use
CamelCase)
5) Update web websocket endpoint
6) Fix main param set (and induce dryrun mode on specific command line
params)

* Engine QA

Link up exchange syncer to cmd params, disarm market selling bombs and fix OKEX endpoints

* Fix linter issue after merge

* Engine QA changes

Template updates
Wrapper code cleanup
Disarmed order bombs
Documentation updates

* Daily engine QA

Bitstamp improvements
Spelling mistakes
Add Coinbene exchange to support list
Protect API authenticated calls for Coinbene/LBank

* Engine QA changes

Fix exchange_wrapper_coverage tool
Add SupportsAsset to exchange interface
Fix inline string usage and add BCH withdrawal support

* Engine QA

Fix Bitstamp types
Inform user of errors when parsing time accross the codebase
Change time parsing warnings to errors (as they are)
Update markdown docs [with linter fixes]

* Engine QA changes

1) Add test for dryrunParamInteraction
2) Disarm OKCoin/OKEX bombs if someone accidently sets canManipulateRealOrders to true and runs all package tests
3) Actually check exchange setup errors for BTSE and Coinbene, plus address this in the wrapper template
4) Hardcode missing/non-retrievable contributors and bump the contributors
5) Convert numbers/strings to meaningful types in Bitstamp and OKEX
6) If WS is supported for the exchange wrapper template, preset authWebsocketSupport var

* Fix the shadow people

* Link the SyncContinuously paramerino

* Also show SyncContinuously in engine.PrintSettings

* Address nitterinos and use correct filepath for logs

* Bitstamp: Extract ALL THE APM

* Fix additional nitterinos

* Fix time parsing error for Bittrex
This commit is contained in:
Adrian Gallagher
2019-11-22 16:07:30 +11:00
committed by GitHub
parent 52e2686b9e
commit 63191ce3ec
102 changed files with 3447 additions and 1714 deletions

View File

@@ -17,6 +17,7 @@ In order to maintain a consistent style across the codebase, the following codin
- In line with gofmt, for loops and if statements don't require parenthesis.
Block style example:
```go
func SendHTTPRequest(method, path string, headers map[string]string, body io.Reader) (string, error) {
result := strings.ToUpper(method)
@@ -38,51 +39,51 @@ func SendHTTPRequest(method, path string, headers map[string]string, body io.Rea
```
## Effective Go Guidelines
[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html).
### Comment First Word as Subject
Doc comments work best as complete sentences, which allow a wide variety of automated presentations.
The first sentence should be a one-sentence summary that starts with the name being declared.
### Good Package Name
It's helpful if everyone using the package can use the same name
to refer to its contents, which implies that the package name should
be good: short, concise, evocative. By convention, packages are
given lower case, single-word names; there should be no need for
underscores or mixedCaps. Err on the side of brevity, since everyone
using your package will be typing that name. And don't worry about
collisions a priori. The package name is only the default name for
imports; it need not be unique across all source code, and in the
rare case of a collision the importing package can choose a different
name to use locally. In any case, confusion is rare because the file
It's helpful if everyone using the package can use the same name
to refer to its contents, which implies that the package name should
be good: short, concise, evocative. By convention, packages are
given lower case, single-word names; there should be no need for
underscores or mixedCaps. Err on the side of brevity, since everyone
using your package will be typing that name. And don't worry about
collisions a priori. The package name is only the default name for
imports; it need not be unique across all source code, and in the
rare case of a collision the importing package can choose a different
name to use locally. In any case, confusion is rare because the file
name in the import determines just which package is being used.
### Package Comment
Every package should have a package comment, a block comment preceding the package clause.
For multi-file packages, the package comment only needs to be present in one file, and any one will do.
The package comment should introduce the package and provide information relevant to the package as a
Every package should have a package comment, a block comment preceding the package clause.
For multi-file packages, the package comment only needs to be present in one file, and any one will do.
The package comment should introduce the package and provide information relevant to the package as a
whole. It will appear first on the godoc page and should set up the detailed documentation that follows.
### Single Method Interface Name
By convention, one-method interfaces are named by the method name plus an -er suffix
By convention, one-method interfaces are named by the method name plus an -er suffix
or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
There are a number of such names and it's productive to honor them and the function names they capture.
Read, Write, Close, Flush, String and so on have canonical signatures and meanings. To avoid confusion,
don't give your method one of those names unless it has the same signature and meaning. Conversely,
if your type implements a method with the same meaning as a method on a well-known type, give it the
There are a number of such names and it's productive to honor them and the function names they capture.
Read, Write, Close, Flush, String and so on have canonical signatures and meanings. To avoid confusion,
don't give your method one of those names unless it has the same signature and meaning. Conversely,
if your type implements a method with the same meaning as a method on a well-known type, give it the
same name and signature; call your string-converter method String not ToString.
### Avoid Annotations in Comments
Comments do not need extra formatting such as banners of stars. The generated output
may not even be presented in a fixed-width font, so don't depend on spacing for alignment—godoc,
like gofmt, takes care of that. The comments are uninterpreted plain text, so HTML and other
annotations such as _this_ will reproduce verbatim and should not be used. One adjustment godoc
does do is to display indented text in a fixed-width font, suitable for program snippets.
The package comment for the fmt package uses this to good effect.
Comments do not need extra formatting such as banners of stars. The generated output
may not even be presented in a fixed-width font, so don't depend on spacing for alignment—godoc,
like gofmt, takes care of that. The comments are uninterpreted plain text, so HTML and other
annotations such as _this_ will reproduce verbatim and should not be used. One adjustment godoc
does do is to display indented text in a fixed-width font, suitable for program snippets.
The package comment for the fmt package uses this to good effect.

View File

@@ -17,6 +17,7 @@ In order to maintain a consistent style across the codebase, the following codin
- In line with gofmt, for loops and if statements don't require parenthesis.
Block style example:
```go
func SendHTTPRequest(method, path string, headers map[string]string, body io.Reader) (string, error) {
result := strings.ToUpper(method)
@@ -38,9 +39,12 @@ func SendHTTPRequest(method, path string, headers map[string]string, body io.Rea
```
## Effective Go Guidelines
[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html).
{{range .}}
### {{.title}}
{{.body}}
{{end}}
{{end}}

View File

@@ -30,4 +30,4 @@ Please provide detailed steps for reproducing the issue.
### Failure Logs
By default, GoCryptoTrader stores its `debug.log` file in `%APPDATA%\GoCryptoTrader` on Windows and `~/.gocryptotrader` on Linux/Unix/macOS. Raw text or a link to a pastebin type site is preferred.
By default and if file logging is enabled, GoCryptoTrader stores its `log.txt` file in `%APPDATA%\GoCryptoTrader\logs` on Windows and `~/.gocryptotrader/logs` on Linux/Unix/macOS. Raw text or a link to a pastebin type site is preferred.

View File

@@ -1,6 +1,6 @@
# Description
# PR Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Please include a summary of the change, feature or issue which this pull request addresses. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
@@ -13,22 +13,22 @@ Please delete options that are not relevant and add an `x` in `[]` as item is co
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
## How has this been tested
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration and
also consider improving test coverage whilst working on a certain feature or package.
## Please also consider improving test coverage whilst working on a certain package
- [ ] go test ./... -race
- [ ] golangci-lint run
- [ ] Test X
- [ ] Test A
- [ ] Test B
# Checklist:
## Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation and regenerated documentation via the documentation tool
- [ ] I have made corresponding changes to the documentation and regenerated documentation via the documentation tool
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally and on Travis with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
- [ ] Any dependent changes have been merged and published in downstream modules

View File

@@ -8,8 +8,8 @@ ermalguni | https://github.com/ermalguni
vadimzhukck | https://github.com/vadimzhukck
140am | https://github.com/140am
marcofranssen | https://github.com/marcofranssen
cranktakular | https://github.com/cranktakular
MadCozBadd | https://github.com/MadCozBadd
cranktakular | https://github.com/cranktakular
leilaes | https://github.com/leilaes
crackcomm | https://github.com/crackcomm
andreygrehov | https://github.com/andreygrehov
@@ -30,6 +30,7 @@ frankzougc | https://github.com/frankzougc
starit | https://github.com/starit
Jimexist | https://github.com/Jimexist
lookfirst | https://github.com/lookfirst
idoall | https://github.com/idoall
mattkanwisher | https://github.com/mattkanwisher
mKurrels | https://github.com/mKurrels
m1kola | https://github.com/m1kola

View File

@@ -1,14 +1,18 @@
FROM golang:1.12 as build
FROM golang:1.13 as build
WORKDIR /go/src/github.com/thrasher-corp/gocryptotrader
COPY . .
RUN GO111MODULE=on go mod vendor
RUN mv -vn config_example.json config.json \
&& GOARCH=386 GOOS=linux CGO_ENABLED=0 go build . \
&& mv gocryptotrader /go/bin/gocryptotrader
&& GOARCH=386 GOOS=linux go build . \
&& GOARCH=386 GOOS=linux go build ./cmd/gctcli \
&& mv gocryptotrader /go/bin/gocryptotrader \
&& mv gctcli /go/bin/gctcli
FROM alpine:latest
RUN apk update && apk add --no-cache ca-certificates
VOLUME /root/.gocryptotrader
RUN apk update && apk add --no-cache ca-certificates bash
COPY --from=build /go/bin/gocryptotrader /app/
COPY --from=build /go/bin/gctcli /app/
COPY --from=build /go/src/github.com/thrasher-corp/gocryptotrader/config.json /app/
EXPOSE 9050-9053
CMD ["/app/gocryptotrader"]
ENTRYPOINT [ "/app/gocryptotrader" ]

View File

@@ -138,7 +138,7 @@ Binaries will be published once the codebase reaches a stable condition.
|User|Contribution Amount|
|--|--|
| [thrasher-](https://github.com/thrasher-) | 548 |
| [thrasher-](https://github.com/thrasher-) | 551 |
| [shazbert](https://github.com/shazbert) | 176 |
| [gloriousCode](https://github.com/gloriousCode) | 155 |
| [xtda](https://github.com/xtda) | 18 |
@@ -146,8 +146,8 @@ Binaries will be published once the codebase reaches a stable condition.
| [vadimzhukck](https://github.com/vadimzhukck) | 10 |
| [140am](https://github.com/140am) | 8 |
| [marcofranssen](https://github.com/marcofranssen) | 8 |
| [MadCozBadd](https://github.com/MadCozBadd) | 6 |
| [cranktakular](https://github.com/cranktakular) | 5 |
| [MadCozBadd](https://github.com/MadCozBadd) | 3 |
| [leilaes](https://github.com/leilaes) | 3 |
| [crackcomm](https://github.com/crackcomm) | 3 |
| [andreygrehov](https://github.com/andreygrehov) | 2 |
@@ -168,8 +168,9 @@ Binaries will be published once the codebase reaches a stable condition.
| [starit](https://github.com/starit) | 1 |
| [Jimexist](https://github.com/Jimexist) | 1 |
| [lookfirst](https://github.com/lookfirst) | 1 |
| [idoall](https://github.com/idoall) | 1 |
| [mattkanwisher](https://github.com/mattkanwisher) | 1 |
| [mKurrels](https://github.com/mKurrels) | 1 |
| [m1kola](https://github.com/m1kola) | 1 |
| [cavapoo2](https://github.com/cavapoo2) | 1 |
| [zeldrinn](https://github.com/zeldrinn )| 1 |
| [zeldrinn](https://github.com/zeldrinn) | 1 |

View File

@@ -116,6 +116,44 @@ func main() {
err)
}
// idoall's contributors were forked and merged, so his contributions
// aren't automatically retrievable
contributors = append(contributors, Contributor{
Login: "idoall",
URL: "https://github.com/idoall",
Contributions: 1,
})
// Github API missing contributors
missingAPIContributors := []Contributor{
{
Login: "mattkanwisher",
URL: "https://github.com/mattkanwisher",
Contributions: 1,
},
{
Login: "mKurrels",
URL: "https://github.com/mKurrels",
Contributions: 1,
},
{
Login: "m1kola",
URL: "https://github.com/m1kola",
Contributions: 1,
},
{
Login: "cavapoo2",
URL: "https://github.com/cavapoo2",
Contributions: 1,
},
{
Login: "zeldrinn",
URL: "https://github.com/zeldrinn",
Contributions: 1,
},
}
contributors = append(contributors, missingAPIContributors...)
if *verbose {
fmt.Println("Contributor List Fetched")
for i := range contributors {

View File

@@ -30,9 +30,9 @@ main.go
```go
var c exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "Coinbene" {
c = bot.exchanges[i]
for i := range Bot.Exchanges {
if Bot.Exchanges[i].GetName() == "Coinbene" {
c = Bot.Exchanges[i]
}
}

View File

@@ -29,22 +29,22 @@ main.go
```go
var l exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "Lbank" {
l = bot.exchanges[i]
for i := range Bot.Exchanges {
if Bot.Exchanges[i].GetName() == "Lbank" {
l = Bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := l.GetTickerPrice()
tick, err := l.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := l.GetOrderbookEx()
ob, err := l.FetchOrderbook()
if err != nil {
// Handle error
}

View File

@@ -24,7 +24,6 @@ const (
packageReadme = "README.md"
exchangePackageLocation = "../../exchanges"
exchangeLocation = "../../exchange.go"
exchangeConfigPath = "../../testdata/configtest.json"
)
@@ -35,7 +34,6 @@ var (
exchangeWrapper string
exchangeMain string
exchangeReadme string
exchangeJSON string
)
type exchange struct {
@@ -119,17 +117,22 @@ func main() {
newExchConfig.Enabled = true
newExchConfig.API.Credentials.Key = "Key"
newExchConfig.API.Credentials.Secret = "Secret"
newExchConfig.CurrencyPairs = &currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
RequestFormat: &currency.PairFormat{
Uppercase: true,
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
},
}
configTestFile.Exchanges = append(configTestFile.Exchanges, newExchConfig)
// TODO sorting function so exchanges are in alphabetical order - low priority
err = configTestFile.SaveConfig(exchangeJSON, false)
err = configTestFile.SaveConfig(exchangeConfigPath, false)
if err != nil {
log.Fatal("GoCryptoTrader: Exchange templating configuration error - cannot save")
}
@@ -217,12 +220,13 @@ func main() {
}
fmt.Println("GoCryptoTrader: Exchange templating tool service complete")
fmt.Println("When wrapper is finished add exchange to exchange.go")
fmt.Println("Test exchange.go")
fmt.Println("Update the config_test.go file")
fmt.Println("Test config.go")
fmt.Println("When the exchange code implementation has been completed (REST/Websocket/wrappers and tests), please add the exchange to engine/exchange.go")
fmt.Println("Add the exchange config settings to config_example.json (it will automatically be added to testdata/configtest.json)")
fmt.Println("Increment the available exchanges counter in config/config_test.go")
fmt.Println("Add the exchange name to exchanges/support.go")
fmt.Println("Ensure go test ./... -race passes")
fmt.Println("Open a pull request")
fmt.Println("If help is needed please post a message on Slack.")
fmt.Println("If help is needed, please post a message in Slack.")
}
func newFile(path string) {

View File

@@ -2,15 +2,7 @@
package {{.Name}}
import (
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// {{.CapitalName}} is the overarching type across this package
@@ -20,91 +12,13 @@ type {{.CapitalName}} struct {
const (
{{.Name}}APIURL = ""
{{.Name}}APIVersion = ""
{{.Name}}APIVersion = ""
// Public endpoints
// Authenticated endpoints
)
// SetDefaults sets the basic defaults for {{.CapitalName}}
func ({{.Variable}} *{{.CapitalName}}) SetDefaults() {
{{.Variable}}.Name = "{{.CapitalName}}"
{{.Variable}}.Enabled = false
{{.Variable}}.Verbose = false
{{.Variable}}.RequestCurrencyPairFormat.Delimiter = ""
{{.Variable}}.RequestCurrencyPairFormat.Uppercase = true
{{.Variable}}.ConfigCurrencyPairFormat.Delimiter = ""
{{.Variable}}.ConfigCurrencyPairFormat.Uppercase = true
{{.Variable}}.AssetTypes = asset.Items{asset.Spot}
{{.Variable}}.SupportsAutoPairUpdating = false
{{.Variable}}.SupportsRESTTickerBatching = false
{{.Variable}}.Requester = request.New({{.Variable}}.Name,
request.NewRateLimit(time.Second, 0),
request.NewRateLimit(time.Second, 0),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
{{.Variable}}.API.Endpoints.URLDefault = {{.Name}}APIURL
{{.Variable}}.API.Endpoints.URL = {{.Variable}}.API.Endpoints.URLDefault
{{.Variable}}.Websocket = monitor.New()
{{.Variable}}.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
{{.Variable}}.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
}
// Start implementing public and private exchange API funcs below
// Setup takes in the supplied exchange configuration details and sets params
func ({{.Variable}} *{{.CapitalName}}) Setup(exch *config.ExchangeConfig) error {
if !exch.Enabled {
{{.Variable}}.SetEnabled(false)
} else {
{{.Variable}}.Enabled = true
{{.Variable}}.API.AuthenticatedSupport = exch.API.AuthenticatedSupport
{{.Variable}}.API.AuthenticatedWebsocketSupport = exch.API.AuthenticatedWebsocketSupport
{{.Variable}}.SetAPIKeys(exch.API.Credentials.Key, exch.API.Credentials.Secret, "", false)
{{.Variable}}.SetHTTPClientTimeout(exch.HTTPTimeout)
{{.Variable}}.SetHTTPClientUserAgent(exch.HTTPUserAgent)
{{.Variable}}.Verbose = exch.Verbose
{{.Variable}}.Websocket.SetWsStatusAndConnection(exch.Features.Enabled.Websocket)
{{.Variable}}.BaseCurrencies = strings.Split(exch.BaseCurrencies, ",")
{{.Variable}}.AvailablePairs = strings.Split(exch.AvailablePairs, ",")
{{.Variable}}.EnabledPairs = strings.Split(exch.EnabledPairs, ",")
err := {{.Variable}}.SetCurrencyPairFormat()
if err != nil {
log.Fatal(err)
}
err = {{.Variable}}.SetAssetTypes()
if err != nil {
log.Fatal(err)
}
err = {{.Variable}}.SetFeatureDefaults()
if err != nil {
log.Fatal(err)
}
err = {{.Variable}}.SetAPIURL(exch)
if err != nil {
log.Fatal(err)
}
err = {{.Variable}}.SetClientProxyAddress(exch.ProxyAddress)
if err != nil {
log.Fatal(err)
}
// If the exchange supports websocket, update the below block
// err = {{.Variable}}.Websocket.Setup({{.Variable}}.WsConnect,
// exch.Name,
// exch.Features.Enabled.Websocket,
// {{.Name}}Websocket,
// exch.Features.Enabled.WebsocketURL)
// if err != nil {
// log.Fatal(err)
// }
// {{.Variable}}.WebsocketConn = &wshandler.WebsocketConnection{
// ExchangeName: {{.Variable}}.Name,
// URL: {{.Variable}}.Websocket.GetWebsocketURL(),
// ProxyURL: {{.Variable}}.Websocket.GetProxyAddress(),
// Verbose: {{.Variable}}.Verbose,
// ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
// ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
// }
}
}
{{end}}

View File

@@ -2,35 +2,51 @@
package {{.Name}}
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
)
// Please supply your own keys here for due diligence testing
// Please supply your own keys here to do authenticated endpoint testing
const (
testAPIKey = ""
testAPISecret = ""
apiKey = ""
apiSecret = ""
canManipulateRealOrders = false
)
var {{.Variable}} {{.CapitalName}}
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
{{.Variable}}.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
{{.Name}}Config, err := cfg.GetExchangeConfig("{{.CapitalName}}")
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Error("{{.CapitalName}} Setup() init error")
log.Fatal(err)
}
{{.Name}}Config.API.AuthenticatedSupport = true
{{.Name}}Config.API.Credentials.Key = testAPIKey
{{.Name}}Config.API.Credentials.Secret = testAPISecret
exchCfg, err := cfg.GetExchangeConfig("{{.CapitalName}}")
if err != nil {
log.Fatal(err)
}
{{.Variable}}.Setup({{.Name}}Config)
exchCfg.API.AuthenticatedSupport = true
{{ if .WS }} exchCfg.API.AuthenticatedWebsocketSupport = true {{ end }}
exchCfg.API.Credentials.Key = apiKey
exchCfg.API.Credentials.Secret = apiSecret
err = {{.Variable}}.Setup(exchCfg)
if err != nil {
log.Fatal(err)
}
os.Exit(m.Run())
}
func areTestAPIKeysSet() bool {
return {{.Variable}}.ValidateAPICredentials()
}
// Implement tests for API endpoints below
{{end}}

View File

@@ -3,15 +3,150 @@ package {{.Name}}
import (
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency/pair"
"github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// GetDefaultConfig returns a default exchange config
func ({{.Variable}} *{{.CapitalName}}) GetDefaultConfig() (*config.ExchangeConfig, error) {
{{.Variable}}.SetDefaults()
exchCfg := new(config.ExchangeConfig)
exchCfg.Name = {{.Variable}}.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = {{.Variable}}.BaseCurrencies
err := {{.Variable}}.SetupDefaults(exchCfg)
if err != nil {
return nil, err
}
if {{.Variable}}.Features.Supports.RESTCapabilities.AutoPairUpdates {
err = {{.Variable}}.UpdateTradablePairs(true)
if err != nil {
return nil, err
}
}
return exchCfg, nil
}
// SetDefaults sets the basic defaults for {{.CapitalName}}
func ({{.Variable}} *{{.CapitalName}}) SetDefaults() {
{{.Variable}}.Name = "{{.CapitalName}}"
{{.Variable}}.Enabled = true
{{.Variable}}.Verbose = true
{{.Variable}}.API.CredentialsValidator.RequiresKey = true
{{.Variable}}.API.CredentialsValidator.RequiresSecret = true
{{.Variable}}.CurrencyPairs = currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
RequestFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: "-",
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: "-",
},
}
// Fill out the capabilities/features that the exchange supports
{{.Variable}}.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
{{ if .REST }} REST: true, {{ end }}
{{ if .WS }} Websocket: true, {{ end }}
RESTCapabilities: protocol.Features{
TickerFetching: true,
OrderbookFetching: true,
},
WebsocketCapabilities: protocol.Features{
TickerFetching: true,
OrderbookFetching: true,
},
WithdrawPermissions: exchange.AutoWithdrawCrypto |
exchange.AutoWithdrawFiat,
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: true,
},
}
{{.Variable}}.Requester = request.New({{.Variable}}.Name,
request.NewRateLimit(time.Second, 0),
request.NewRateLimit(time.Second, 0),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
{{.Variable}}.API.Endpoints.URLDefault = {{.Name}}APIURL
{{.Variable}}.API.Endpoints.URL = {{.Variable}}.API.Endpoints.URLDefault
{{.Variable}}.Websocket = wshandler.New()
{{.Variable}}.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
{{.Variable}}.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
{{.Variable}}.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
// Setup takes in the supplied exchange configuration details and sets params
func ({{.Variable}} *{{.CapitalName}}) Setup(exch *config.ExchangeConfig) error {
if !exch.Enabled {
{{.Variable}}.SetEnabled(false)
return nil
}
err := {{.Variable}}.SetupDefaults(exch)
if err != nil {
return err
}
// If websocket is supported, please fill out the following
/*
err = {{.Variable}}.Websocket.Setup(
&wshandler.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport,
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: {{.Name}}WSURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
Connector: {{.Variable}}.WsConnect,
Subscriber: {{.Variable}}.Subscribe,
UnSubscriber: {{.Variable}}.Unsubscribe,
Features: &{{.Variable}}.Features.Supports.WebsocketCapabilities,
})
if err != nil {
return err
}
{{.Variable}}.WebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: {{.Variable}}.Name,
URL: {{.Variable}}.Websocket.GetWebsocketURL(),
ProxyURL: {{.Variable}}.Websocket.GetProxyAddress(),
Verbose: {{.Variable}}.Verbose,
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
// NOTE: PLEASE ENSURE YOU SET THE ORDERBOOK BUFFER SETTINGS CORRECTLY
{{.Variable}}.Websocket.Orderbook.Setup(
exch.WebsocketOrderbookBufferLimit,
true,
true,
false,
false,
exch.Name)
*/
return nil
}
// Start starts the {{.CapitalName}} go routine
func ({{.Variable}} *{{.CapitalName}}) Start(wg *sync.WaitGroup) {
wg.Add(1)
@@ -24,38 +159,68 @@ func ({{.Variable}} *{{.CapitalName}}) Start(wg *sync.WaitGroup) {
// Run implements the {{.CapitalName}} wrapper
func ({{.Variable}} *{{.CapitalName}}) Run() {
if {{.Variable}}.Verbose {
{{if .WS}} log.Debugf(log.ExchangeSys, "%s Websocket: %s. (url: %s).\n", {{.Variable}}.Name, common.IsEnabled({{.Variable}}.Websocket.IsEnabled()), {{.Variable}}.Websocket.GetWebsocketURL()) {{end}}
log.Debugf(log.ExchangeSys, "%s polling delay: %ds.\n", {{.Variable}}.Name, {{.Variable}}.RESTPollingDelay)
log.Debugf(log.ExchangeSys, "%s %d currencies enabled: %s.\n", {{.Variable}}.Name, len({{.Variable}}.EnabledPairs), {{.Variable}}.EnabledPairs)
{{ if .WS }} log.Debugf(log.ExchangeSys,
"%s Websocket: %s.",
{{.Variable}}.Name,
common.IsEnabled({{.Variable}}.Websocket.IsEnabled())) {{ end }}
{{.Variable}}.PrintEnabledPairs()
}
if !{{.Variable}}.GetEnabledFeatures().AutoPairUpdates {
return
}
err := {{.Variable}}.UpdateTradablePairs(false)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
{{.Variable}}.Name,
err)
}
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func ({{.Variable}} *{{.CapitalName}}) FetchTradablePairs(asset asset.Item) ([]string, error) {
// Implement fetching the exchange available pairs if supported
return nil, nil
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func ({{.Variable}} *{{.CapitalName}}) UpdateTradablePairs(forceUpdate bool) error {
pairs, err := {{.Variable}}.FetchTradablePairs(asset.Spot)
if err != nil {
return err
}
return {{.Variable}}.UpdatePairs(currency.NewPairsFromStrings(pairs),
asset.Spot, false, forceUpdate)
}
// UpdateTicker updates and returns the ticker for a currency pair
func ({{.Variable}} *{{.CapitalName}}) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
// NOTE: EXAMPLE FOR GETTING TICKER PRICE
/*
var tickerPrice ticker.Price
// NOTE EXAMPLE FOR GETTING TICKER PRICE
//tick, err := {{.Variable}}.GetTickers()
//if err != nil {
// return tickerPrice, err
//}
//for _, x := range {{.Variable}}.GetEnabledPairs(assetType) {
//curr := exchange.FormatExchangeCurrency({{.Variable}}.Name, x)
//for y := range tick {
// if tick[y].Symbol == curr.String() {
// tickerPrice.Pair = x
// tickerPrice.Ask = tick[y].AskPrice
// tickerPrice.Bid = tick[y].BidPrice
// tickerPrice.High = tick[y].HighPrice
// tickerPrice.Last = tick[y].LastPrice
// tickerPrice.Low = tick[y].LowPrice
// tickerPrice.Volume = tick[y].Volume
// ticker.ProcessTicker({{.Variable}}.Name, x, &tickerPrice, assetType)
// }
// }
//}
//return ticker.GetTicker({{.Variable}}.Name, p, assetType)
return tickerPrice, nil // NOTE DO NOT USE AS RETURN
tick, err := {{.Variable}}.GetTicker(p.String())
if err != nil {
return tickerPrice, err
}
tickerPrice = ticker.Price{
High: tick.High,
Low: tick.Low,
Bid: tick.Bid,
Ask: tick.Ask,
Open: tick.Open,
Close: tick.Close,
Pair: p,
}
err = ticker.ProcessTicker({{.Variable}}.Name, &tickerPrice, assetType)
if err != nil {
return tickerPrice, err
}
*/
return ticker.GetTicker({{.Variable}}.Name, p, assetType)
}
// FetchTicker returns the ticker for a currency pair
@@ -79,23 +244,39 @@ func ({{.Variable}} *{{.CapitalName}}) FetchOrderbook(currency currency.Pair, as
// UpdateOrderbook updates and returns the orderbook for a currency pair
func ({{.Variable}} *{{.CapitalName}}) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
//NOTE UPDATE ORDERBOOK EXAMPLE
//orderbookNew, err := {{.Variable}}.GetOrderBook(exchange.FormatExchangeCurrency({{.Variable}}.Name, p).String(), 1000)
//if err != nil {
// return orderBook, err
//}
// NOTE: UPDATE ORDERBOOK EXAMPLE
/*
orderbookNew, err := {{.Variable}}.GetOrderBook(exchange.FormatExchangeCurrency({{.Variable}}.Name, p).String(), 1000)
if err != nil {
return orderBook, err
}
//for _, bids := range orderbookNew.Bids {
// orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: bids.Quantity, Price: bids.Price})
//}
for x := range orderbookNew.Bids {
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x].Quantity,
Price: orderbookNew.Bids[x].Price,
})
}
//for _, asks := range orderbookNew.Asks {
// orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: asks.Quantity, Price: asks.Price})
//}
for x := range orderbookNew.Asks {
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderBook.Asks[x].Quantity,
Price: orderBook.Asks[x].Price,
})
}
*/
//orderbook.ProcessOrderbook(b.Name, p, orderBook, assetType)
//return orderbook.Get({{.Variable}}.Name, p, assetType)
return orderBook, nil // NOTE DO NOT USE AS RETURN
orderBook.Pair = p
orderBook.ExchangeName = {{.Variable}}.Name
orderBook.AssetType = assetType
err := orderBook.Process()
if err != nil {
return orderBook, err
}
return orderbook.Get({{.Variable}}.Name, p, assetType)
}
// GetAccountInfo retrieves balances for all enabled currencies for the
@@ -116,8 +297,12 @@ func ({{.Variable}} *{{.CapitalName}}) GetExchangeHistory(p currency.Pair, asset
}
// SubmitOrder submits a new order
func ({{.Variable}} *{{.CapitalName}}) SubmitOrder(p currency.Pair, side order.Side, orderType order.Type, amount, price float64, clientID string) (order.SubmitResponse, error) {
return order.SubmitResponse{}, common.ErrNotYetImplemented
func ({{.Variable}} *{{.CapitalName}}) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
return submitOrderResponse, err
}
return submitOrderResponse, common.ErrNotYetImplemented
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -142,30 +327,30 @@ func ({{.Variable}} *{{.CapitalName}}) GetOrderInfo(orderID string) (order.Detai
}
// GetDepositAddress returns a deposit address for a specified currency
func ({{.Variable}} *{{.CapitalName}}) GetDepositAddress(cryptocurrency pair.CurrencyItem, accountID string) (string, error) {
func ({{.Variable}} *{{.CapitalName}}) GetDepositAddress(cryptocurrency currency.Code, accountID string) (string, error) {
return "", common.ErrNotYetImplemented
}
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func ({{.Variable}} *{{.CapitalName}}) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.WithdrawRequest) (string, error) {
func ({{.Variable}} *{{.CapitalName}}) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) {
return "", common.ErrNotYetImplemented
}
// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is
// submitted
func ({{.Variable}} *{{.CapitalName}}) WithdrawFiatFunds(withdrawRequest *exchange.WithdrawRequest) (string, error) {
func ({{.Variable}} *{{.CapitalName}}) WithdrawFiatFunds(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrNotYetImplemented
}
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a withdrawal is
// submitted
func ({{.Variable}} *{{.CapitalName}}) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.WithdrawRequest) (string, error) {
func ({{.Variable}} *{{.CapitalName}}) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrNotYetImplemented
}
// GetWebsocket returns a pointer to the exchange websocket
func ({{.Variable}} *{{.CapitalName}}) GetWebsocket() (*exchange.Websocket, error) {
func ({{.Variable}} *{{.CapitalName}}) GetWebsocket() (*wshandler.Websocket, error) {
return nil, common.ErrNotYetImplemented
}
@@ -187,20 +372,20 @@ func ({{.Variable}} *{{.CapitalName}}) GetFeeByType(feeBuilder *exchange.FeeBuil
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle subscribing
func ({{.Variable}} *{{.CapitalName}}) SubscribeToWebsocketChannels(channels []monitor.WebsocketChannelSubscription) error {
func ({{.Variable}} *{{.CapitalName}}) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
{{.Variable}}.Websocket.SubscribeToChannels(channels)
return nil
}
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle unsubscribing
func ({{.Variable}} *{{.CapitalName}}) UnsubscribeToWebsocketChannels(channels []monitor.WebsocketChannelSubscription) error {
{{.Variable}}.Websocket.UnubscribeToChannels(channels)
func ({{.Variable}} *{{.CapitalName}}) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
{{.Variable}}.Websocket.RemoveSubscribedChannels(channels)
return nil
}
// GetSubscriptions returns a copied list of subscriptions
func ({{.Variable}} *{{.CapitalName}}) GetSubscriptions() ([]monitor.WebsocketChannelSubscription, error) {
func ({{.Variable}} *{{.CapitalName}}) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
return nil, common.ErrNotYetImplemented
}

View File

@@ -2,7 +2,9 @@ package main
import (
"log"
"math/rand"
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -70,6 +72,12 @@ func main() {
func testWrappers(e exchange.IBotExchange) []string {
p := currency.NewPair(currency.BTC, currency.USD)
assetType := asset.Spot
if !e.SupportsAsset(assetType) {
assets := e.GetAssetTypes()
rand.Seed(time.Now().Unix())
assetType = assets[rand.Intn(len(assets))]
}
var funcs []string
_, err := e.FetchTicker(p, assetType)

View File

@@ -91,6 +91,11 @@
"clientID": "ClientID",
"otpSecret": "-"
},
"coinbene": {
"key": "Key",
"secret": "Secret",
"otpSecret": "-"
},
"coinut": {
"key": "Key",
"clientID": "ClientID",
@@ -136,7 +141,11 @@
"secret": "Secret",
"otpSecret": "-"
},
"lbank": {},
"lbank": {
"key": "Key",
"secret": "Secret",
"otpSecret": "-"
},
"localbitcoins": {
"key": "Key",
"secret": "Secret",

View File

@@ -15,28 +15,9 @@ const (
var s Slack
type group struct {
ID string `json:"id"`
Name string `json:"name"`
IsGroup bool `json:"is_group"`
Created int64 `json:"created"`
Creator string `json:"creator"`
IsArchived bool `json:"is_archived"`
NameNormalised string `json:"name_normalised"`
IsMPIM bool `json:"is_mpim"`
HasPins bool `json:"has_pins"`
IsOpen bool `json:"is_open"`
LastRead string `json:"last_read"`
Members []string `json:"members"`
Topic struct {
Value string `json:"value"`
Creator string `json:"creator"`
LastSet int64 `json:"last_set"`
} `json:"topic"`
Purpose struct {
Value string `json:"value"`
Creator string `json:"creator"`
LastSet int64 `json:"last_set"`
} `json:"purpose"`
ID string `json:"id"`
Name string `json:"name"`
Members []string `json:"members"`
}
func TestSetup(t *testing.T) {
@@ -76,16 +57,7 @@ func TestBuildURL(t *testing.T) {
func TestGetChannelsString(t *testing.T) {
s.Details.Channels = append(s.Details.Channels, struct {
Created int `json:"created"`
Creator string `json:"creator"`
HasPins bool `json:"has_pins"`
ID string `json:"id"`
IsArchived bool `json:"is_archived"`
IsChannel bool `json:"is_channel"`
IsGeneral bool `json:"is_general"`
IsMember bool `json:"is_member"`
IsOrgShared bool `json:"is_org_shared"`
IsShared bool `json:"is_shared"`
Name string `json:"name"`
NameNormalized string `json:"name_normalized"`
PreviousNames []string `json:"previous_names"`
@@ -112,28 +84,9 @@ func TestGetUsernameByID(t *testing.T) {
}
s.Details.Users = append(s.Details.Users, struct {
Deleted bool `json:"deleted"`
ID string `json:"id"`
IsBot bool `json:"is_bot"`
Name string `json:"name"`
Presence string `json:"presence"`
Profile struct {
AvatarHash string `json:"avatar_hash"`
Email string `json:"email"`
Fields interface{} `json:"fields"`
FirstName string `json:"first_name"`
Image192 string `json:"image_192"`
Image24 string `json:"image_24"`
Image32 string `json:"image_32"`
Image48 string `json:"image_48"`
Image512 string `json:"image_512"`
Image72 string `json:"image_72"`
LastName string `json:"last_name"`
RealName string `json:"real_name"`
RealNameNormalized string `json:"real_name_normalized"`
} `json:"profile"`
TeamID string `json:"team_id"`
Updated int `json:"updated"`
ID string `json:"id"`
Name string `json:"name"`
TeamID string `json:"team_id"`
}{
ID: "1337",
Name: "cranktakular",
@@ -186,16 +139,7 @@ func TestGetChannelIDByName(t *testing.T) {
}
s.Details.Channels = append(s.Details.Channels, struct {
Created int `json:"created"`
Creator string `json:"creator"`
HasPins bool `json:"has_pins"`
ID string `json:"id"`
IsArchived bool `json:"is_archived"`
IsChannel bool `json:"is_channel"`
IsGeneral bool `json:"is_general"`
IsMember bool `json:"is_member"`
IsOrgShared bool `json:"is_org_shared"`
IsShared bool `json:"is_shared"`
Name string `json:"name"`
NameNormalized string `json:"name_normalized"`
PreviousNames []string `json:"previous_names"`

View File

@@ -36,406 +36,32 @@ type PresenceChange struct {
// Response is a generalised response type
type Response struct {
Bots []struct {
AppID string `json:"app_id"`
Deleted bool `json:"deleted"`
Icons struct {
Image36 string `json:"image_36"`
Image48 string `json:"image_48"`
Image72 string `json:"image_72"`
} `json:"icons"`
ID string `json:"id"`
Name string `json:"name"`
Updated int `json:"updated"`
} `json:"bots"`
CacheTs int `json:"cache_ts"`
CacheTsVersion string `json:"cache_ts_version"`
CacheVersion string `json:"cache_version"`
CanManageSharedChannels bool `json:"can_manage_shared_channels"`
Channels []struct {
Created int `json:"created"`
Creator string `json:"creator"`
HasPins bool `json:"has_pins"`
Channels []struct {
ID string `json:"id"`
IsArchived bool `json:"is_archived"`
IsChannel bool `json:"is_channel"`
IsGeneral bool `json:"is_general"`
IsMember bool `json:"is_member"`
IsOrgShared bool `json:"is_org_shared"`
IsShared bool `json:"is_shared"`
Name string `json:"name"`
NameNormalized string `json:"name_normalized"`
PreviousNames []string `json:"previous_names"`
} `json:"channels"`
Dnd struct {
DndEnabled bool `json:"dnd_enabled"`
NextDndEndTs int `json:"next_dnd_end_ts"`
NextDndStartTs int `json:"next_dnd_start_ts"`
SnoozeEnabled bool `json:"snooze_enabled"`
} `json:"dnd"`
Groups []struct {
ID string `json:"id"`
Name string `json:"name"`
IsGroup bool `json:"is_group"`
Created int64 `json:"created"`
Creator string `json:"creator"`
IsArchived bool `json:"is_archived"`
NameNormalised string `json:"name_normalised"`
IsMPIM bool `json:"is_mpim"`
HasPins bool `json:"has_pins"`
IsOpen bool `json:"is_open"`
LastRead string `json:"last_read"`
Members []string `json:"members"`
Topic struct {
Value string `json:"value"`
Creator string `json:"creator"`
LastSet int64 `json:"last_set"`
} `json:"topic"`
Purpose struct {
Value string `json:"value"`
Creator string `json:"creator"`
LastSet int64 `json:"last_set"`
} `json:"purpose"`
ID string `json:"id"`
Name string `json:"name"`
Members []string `json:"members"`
} `json:"groups"`
Ims []struct {
Created int `json:"created"`
HasPins bool `json:"has_pins"`
ID string `json:"id"`
IsIm bool `json:"is_im"`
IsOpen bool `json:"is_open"`
IsOrgShared bool `json:"is_org_shared"`
LastRead string `json:"last_read"`
User string `json:"user"`
} `json:"ims"`
LatestEventTs string `json:"latest_event_ts"`
Ok bool `json:"ok"`
Error string `json:"error"`
ReadOnlyChannels []interface{} `json:"read_only_channels"`
Self struct {
Created int `json:"created"`
ID string `json:"id"`
ManualPresence string `json:"manual_presence"`
Name string `json:"name"`
Prefs struct {
A11yAnimations bool `json:"a11y_animations"`
A11yFontSize string `json:"a11y_font_size"`
AllChannelsLoud bool `json:"all_channels_loud"`
AllNotificationsPrefs string `json:"all_notifications_prefs"`
AllUnreadsSortOrder string `json:"all_unreads_sort_order"`
AllowCallsToSetCurrentStatus bool `json:"allow_calls_to_set_current_status"`
AnalyticsUpsellCoachmarkSeen bool `json:"analytics_upsell_coachmark_seen"`
ArrowHistory bool `json:"arrow_history"`
AtChannelSuppressedChannels string `json:"at_channel_suppressed_channels"`
BoxEnabled bool `json:"box_enabled"`
ChannelSort string `json:"channel_sort"`
ClientLogsPri string `json:"client_logs_pri"`
ColorNamesInList bool `json:"color_names_in_list"`
ConfirmClearAllUnreads bool `json:"confirm_clear_all_unreads"`
ConfirmShCallStart bool `json:"confirm_sh_call_start"`
ConfirmUserMarkedAway bool `json:"confirm_user_marked_away"`
ConvertEmoticons bool `json:"convert_emoticons"`
DisplayDisplayNames bool `json:"display_display_names"`
DisplayRealNamesOverride int `json:"display_real_names_override"`
DndEnabled bool `json:"dnd_enabled"`
DndEndHour string `json:"dnd_end_hour"`
DndStartHour string `json:"dnd_start_hour"`
DropboxEnabled bool `json:"dropbox_enabled"`
EmailAlerts string `json:"email_alerts"`
EmailAlertsSleepUntil int `json:"email_alerts_sleep_until"`
EmailMisc bool `json:"email_misc"`
EmailWeekly bool `json:"email_weekly"`
EmojiAutocompleteBig bool `json:"emoji_autocomplete_big"`
EmojiMode string `json:"emoji_mode"`
EmojiUse string `json:"emoji_use"`
EnableReactEmojiPicker bool `json:"enable_react_emoji_picker"`
EnableUnreadView bool `json:"enable_unread_view"`
EnhancedDebugging bool `json:"enhanced_debugging"`
EnterIsSpecialInTbt bool `json:"enter_is_special_in_tbt"`
EnterpriseMdmCustomMsg string `json:"enterprise_mdm_custom_msg"`
EnterpriseMigrationSeen bool `json:"enterprise_migration_seen"`
ExpandInlineImgs bool `json:"expand_inline_imgs"`
ExpandInternalInlineImgs bool `json:"expand_internal_inline_imgs"`
ExpandNonMediaAttachments bool `json:"expand_non_media_attachments"`
ExpandSnippets bool `json:"expand_snippets"`
FKeySearch bool `json:"f_key_search"`
FlannelServerPool string `json:"flannel_server_pool"`
FrecencyEntJumper string `json:"frecency_ent_jumper"`
FrecencyJumper string `json:"frecency_jumper"`
FullTextExtracts bool `json:"full_text_extracts"`
FullerTimestamps bool `json:"fuller_timestamps"`
GdriveAuthed bool `json:"gdrive_authed"`
GdriveEnabled bool `json:"gdrive_enabled"`
GraphicEmoticons bool `json:"graphic_emoticons"`
GrowlsEnabled bool `json:"growls_enabled"`
GrowthMsgLimitApproachingCtaCount int `json:"growth_msg_limit_approaching_cta_count"`
GrowthMsgLimitApproachingCtaTs int `json:"growth_msg_limit_approaching_cta_ts"`
GrowthMsgLimitLongReachedCtaCount int `json:"growth_msg_limit_long_reached_cta_count"`
GrowthMsgLimitLongReachedCtaLastTs int `json:"growth_msg_limit_long_reached_cta_last_ts"`
GrowthMsgLimitReachedCtaCount int `json:"growth_msg_limit_reached_cta_count"`
GrowthMsgLimitReachedCtaLastTs int `json:"growth_msg_limit_reached_cta_last_ts"`
HasCreatedChannel bool `json:"has_created_channel"`
HasInvited bool `json:"has_invited"`
HasSearched bool `json:"has_searched"`
HasUploaded bool `json:"has_uploaded"`
HideHexSwatch bool `json:"hide_hex_swatch"`
HideUserGroupInfoPane bool `json:"hide_user_group_info_pane"`
HighlightWords string `json:"highlight_words"`
IntroToAppsMessageSeen bool `json:"intro_to_apps_message_seen"`
Jumbomoji bool `json:"jumbomoji"`
KKeyOmnibox bool `json:"k_key_omnibox"`
KKeyOmniboxAutoHideCount int `json:"k_key_omnibox_auto_hide_count"`
LastSeenAtChannelWarning int `json:"last_seen_at_channel_warning"`
LastSnippetType string `json:"last_snippet_type"`
LastTosAcknowledged interface{} `json:"last_tos_acknowledged"`
LoadLato2 bool `json:"load_lato_2"`
Locale string `json:"locale"`
LoudChannels string `json:"loud_channels"`
LoudChannelsSet string `json:"loud_channels_set"`
LsDisabled bool `json:"ls_disabled"`
MacSsbBounce string `json:"mac_ssb_bounce"`
MacSsbBullet bool `json:"mac_ssb_bullet"`
MarkMsgsReadImmediately bool `json:"mark_msgs_read_immediately"`
MeasureCSSUsage bool `json:"measure_css_usage"`
MentionsExcludeAtChannels bool `json:"mentions_exclude_at_channels"`
MentionsExcludeAtUserGroups bool `json:"mentions_exclude_at_user_groups"`
MessagesTheme string `json:"messages_theme"`
MsgPreview bool `json:"msg_preview"`
MsgPreviewPersistent bool `json:"msg_preview_persistent"`
MuteSounds bool `json:"mute_sounds"`
MutedChannels string `json:"muted_channels"`
NeverChannels string `json:"never_channels"`
NewMsgSnd string `json:"new_msg_snd"`
NewxpSeenLastMessage int `json:"newxp_seen_last_message"`
NoCreatedOverlays bool `json:"no_created_overlays"`
NoInvitesWidgetInSidebar bool `json:"no_invites_widget_in_sidebar"`
NoJoinedOverlays bool `json:"no_joined_overlays"`
NoMacelectronBanner bool `json:"no_macelectron_banner"`
NoMacssb1Banner bool `json:"no_macssb1_banner"`
NoMacssb2Banner bool `json:"no_macssb2_banner"`
NoOmniboxInChannels bool `json:"no_omnibox_in_channels"`
NoTextInNotifications bool `json:"no_text_in_notifications"`
NoWinssb1Banner bool `json:"no_winssb1_banner"`
ObeyInlineImgLimit bool `json:"obey_inline_img_limit"`
OnboardingCancelled bool `json:"onboarding_cancelled"`
OnboardingSlackbotConversationStep int `json:"onboarding_slackbot_conversation_step"`
OverloadedMessageEnabled bool `json:"overloaded_message_enabled"`
PagekeysHandled bool `json:"pagekeys_handled"`
PostsFormattingGuide bool `json:"posts_formatting_guide"`
PreferredSkinTone string `json:"preferred_skin_tone"`
PrevNextBtn bool `json:"prev_next_btn"`
PrivacyPolicySeen bool `json:"privacy_policy_seen"`
PromptedForEmailDisabling bool `json:"prompted_for_email_disabling"`
PushAtChannelSuppressedChannels string `json:"push_at_channel_suppressed_channels"`
PushDmAlert bool `json:"push_dm_alert"`
PushEverything bool `json:"push_everything"`
PushIdleWait int `json:"push_idle_wait"`
PushLoudChannels string `json:"push_loud_channels"`
PushLoudChannelsSet string `json:"push_loud_channels_set"`
PushMentionAlert bool `json:"push_mention_alert"`
PushMentionChannels string `json:"push_mention_channels"`
PushShowPreview bool `json:"push_show_preview"`
PushSound string `json:"push_sound"`
QuestsEnabled bool `json:"quests_enabled"`
RequireAt bool `json:"require_at"`
SearchExcludeBots bool `json:"search_exclude_bots"`
SearchExcludeChannels string `json:"search_exclude_channels"`
SearchOnlyCurrentTeam bool `json:"search_only_current_team"`
SearchOnlyMyChannels bool `json:"search_only_my_channels"`
SearchSort string `json:"search_sort"`
SeenAppSpaceCoachmark bool `json:"seen_app_space_coachmark"`
SeenAppSpaceTutorial bool `json:"seen_app_space_tutorial"`
SeenCallsSsMainCoachmark bool `json:"seen_calls_ss_main_coachmark"`
SeenCallsSsWindowCoachmark bool `json:"seen_calls_ss_window_coachmark"`
SeenCallsVideoBetaCoachmark bool `json:"seen_calls_video_beta_coachmark"`
SeenCallsVideoGaCoachmark bool `json:"seen_calls_video_ga_coachmark"`
SeenCustomStatusBadge bool `json:"seen_custom_status_badge"`
SeenCustomStatusCallout bool `json:"seen_custom_status_callout"`
SeenDomainInviteReminder bool `json:"seen_domain_invite_reminder"`
SeenGdriveCoachmark bool `json:"seen_gdrive_coachmark"`
SeenGuestAdminSlackbotAnnouncement bool `json:"seen_guest_admin_slackbot_announcement"`
SeenHighlightsArrowsCoachmark bool `json:"seen_highlights_arrows_coachmark"`
SeenHighlightsCoachmark bool `json:"seen_highlights_coachmark"`
SeenHighlightsWarmWelcome bool `json:"seen_highlights_warm_welcome"`
SeenIntlChannelNamesCoachmark bool `json:"seen_intl_channel_names_coachmark"`
SeenMemberInviteReminder bool `json:"seen_member_invite_reminder"`
SeenOnboardingChannels bool `json:"seen_onboarding_channels"`
SeenOnboardingDirectMessages bool `json:"seen_onboarding_direct_messages"`
SeenOnboardingInvites bool `json:"seen_onboarding_invites"`
SeenOnboardingPrivateGroups bool `json:"seen_onboarding_private_groups"`
SeenOnboardingRecentMentions bool `json:"seen_onboarding_recent_mentions"`
SeenOnboardingSearch bool `json:"seen_onboarding_search"`
SeenOnboardingSlackbotConversation bool `json:"seen_onboarding_slackbot_conversation"`
SeenOnboardingStarredItems bool `json:"seen_onboarding_starred_items"`
SeenOnboardingStart bool `json:"seen_onboarding_start"`
SeenRepliesCoachmark bool `json:"seen_replies_coachmark"`
SeenSingleEmojiMsg bool `json:"seen_single_emoji_msg"`
SeenSsbPrompt bool `json:"seen_ssb_prompt"`
SeenThreadsNotificationBanner bool `json:"seen_threads_notification_banner"`
SeenUnreadViewCoachmark bool `json:"seen_unread_view_coachmark"`
SeenWelcome2 bool `json:"seen_welcome_2"`
SeparatePrivateChannels bool `json:"separate_private_channels"`
SeparateSharedChannels bool `json:"separate_shared_channels"`
ShowAllSkinTones bool `json:"show_all_skin_tones"`
ShowJumperScores bool `json:"show_jumper_scores"`
ShowMemoryInstrument bool `json:"show_memory_instrument"`
ShowTyping bool `json:"show_typing"`
SidebarBehavior string `json:"sidebar_behavior"`
SidebarTheme string `json:"sidebar_theme"`
SidebarThemeCustomValues string `json:"sidebar_theme_custom_values"`
SnippetEditorWrapLongLines bool `json:"snippet_editor_wrap_long_lines"`
SpacesNewXpBannerDismissed bool `json:"spaces_new_xp_banner_dismissed"`
SsEmojis bool `json:"ss_emojis"`
SsbSpaceWindow string `json:"ssb_space_window"`
StartScrollAtOldest bool `json:"start_scroll_at_oldest"`
TabUIReturnSelects bool `json:"tab_ui_return_selects"`
ThreadsEverything bool `json:"threads_everything"`
Time24 bool `json:"time24"`
TwoFactorAuthEnabled bool `json:"two_factor_auth_enabled"`
TwoFactorBackupType interface{} `json:"two_factor_backup_type"`
TwoFactorType interface{} `json:"two_factor_type"`
Tz interface{} `json:"tz"`
UseReactSidebar bool `json:"use_react_sidebar"`
UserColors string `json:"user_colors"`
WebappSpellcheck bool `json:"webapp_spellcheck"`
WelcomeMessageHidden bool `json:"welcome_message_hidden"`
WhatsNewRead int `json:"whats_new_read"`
WinssbRunFromTray bool `json:"winssb_run_from_tray"`
WinssbWindowFlashBehavior string `json:"winssb_window_flash_behavior"`
} `json:"prefs"`
Ok bool `json:"ok"`
Error string `json:"error"`
Self struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"self"`
Subteams struct {
All []interface{} `json:"all"`
Self []interface{} `json:"self"`
} `json:"subteams"`
Team struct {
ApproachingMsgLimit bool `json:"approaching_msg_limit"`
AvatarBaseURL string `json:"avatar_base_url"`
Domain string `json:"domain"`
EmailDomain string `json:"email_domain"`
Icon struct {
Image102 string `json:"image_102"`
Image132 string `json:"image_132"`
Image230 string `json:"image_230"`
Image34 string `json:"image_34"`
Image44 string `json:"image_44"`
Image68 string `json:"image_68"`
Image88 string `json:"image_88"`
ImageOriginal string `json:"image_original"`
} `json:"icon"`
ID string `json:"id"`
MessagesCount int `json:"messages_count"`
MsgEditWindowMins int `json:"msg_edit_window_mins"`
Name string `json:"name"`
OverIntegrationsLimit bool `json:"over_integrations_limit"`
OverStorageLimit bool `json:"over_storage_limit"`
Plan string `json:"plan"`
Prefs struct {
AllowCalls bool `json:"allow_calls"`
AllowMessageDeletion bool `json:"allow_message_deletion"`
AllowRetentionOverride bool `json:"allow_retention_override"`
AllowSharedChannelPermsOverride bool `json:"allow_shared_channel_perms_override"`
AuthMode string `json:"auth_mode"`
CallingAppName string `json:"calling_app_name"`
ChannelHandyRxns interface{} `json:"channel_handy_rxns"`
ComplianceExportStart int `json:"compliance_export_start"`
CustomStatusDefaultEmoji string `json:"custom_status_default_emoji"`
CustomStatusPresets [][]string `json:"custom_status_presets"`
DefaultChannels []string `json:"default_channels"`
DefaultRxns []string `json:"default_rxns"`
DisableFileDeleting bool `json:"disable_file_deleting"`
DisableFileEditing bool `json:"disable_file_editing"`
DisableFileUploads string `json:"disable_file_uploads"`
DisallowPublicFileUrls bool `json:"disallow_public_file_urls"`
Discoverable string `json:"discoverable"`
DisplayEmailAddresses bool `json:"display_email_addresses"`
DisplayRealNames bool `json:"display_real_names"`
DmRetentionDuration int `json:"dm_retention_duration"`
DmRetentionType int `json:"dm_retention_type"`
DndEnabled bool `json:"dnd_enabled"`
DndEndHour string `json:"dnd_end_hour"`
DndStartHour string `json:"dnd_start_hour"`
EnterpriseDefaultChannels []interface{} `json:"enterprise_default_channels"`
EnterpriseMandatoryChannels []interface{} `json:"enterprise_mandatory_channels"`
EnterpriseMdmDateEnabled int `json:"enterprise_mdm_date_enabled"`
EnterpriseMdmLevel int `json:"enterprise_mdm_level"`
EnterpriseTeamCreationRequest struct {
IsEnabled bool `json:"is_enabled"`
} `json:"enterprise_team_creation_request"`
FileRetentionDuration int `json:"file_retention_duration"`
FileRetentionType int `json:"file_retention_type"`
GdriveEnabledTeam bool `json:"gdrive_enabled_team"`
GroupRetentionDuration int `json:"group_retention_duration"`
GroupRetentionType int `json:"group_retention_type"`
HideReferers bool `json:"hide_referers"`
InvitesLimit bool `json:"invites_limit"`
InvitesOnlyAdmins bool `json:"invites_only_admins"`
LimitReachedTs int `json:"limit_reached_ts"`
Locale string `json:"locale"`
LoudChannelMentionsLimit int `json:"loud_channel_mentions_limit"`
MsgEditWindowMins int `json:"msg_edit_window_mins"`
RequireAtForMention bool `json:"require_at_for_mention"`
RetentionDuration int `json:"retention_duration"`
RetentionType int `json:"retention_type"`
ShowJoinLeave bool `json:"show_join_leave"`
TeamHandyRxns struct {
List []struct {
Name string `json:"name"`
Title string `json:"title"`
} `json:"list"`
Restrict bool `json:"restrict"`
} `json:"team_handy_rxns"`
UsesCustomizedCustomStatusPresets bool `json:"uses_customized_custom_status_presets"`
WarnBeforeAtChannel string `json:"warn_before_at_channel"`
WhoCanArchiveChannels string `json:"who_can_archive_channels"`
WhoCanAtChannel string `json:"who_can_at_channel"`
WhoCanAtEveryone string `json:"who_can_at_everyone"`
WhoCanChangeTeamProfile string `json:"who_can_change_team_profile"`
WhoCanCreateChannels string `json:"who_can_create_channels"`
WhoCanCreateDeleteUserGroups string `json:"who_can_create_delete_user_groups"`
WhoCanCreateGroups string `json:"who_can_create_groups"`
WhoCanCreateSharedChannels string `json:"who_can_create_shared_channels"`
WhoCanEditUserGroups string `json:"who_can_edit_user_groups"`
WhoCanKickChannels string `json:"who_can_kick_channels"`
WhoCanKickGroups string `json:"who_can_kick_groups"`
WhoCanManageGuests struct {
Type []string `json:"type"`
} `json:"who_can_manage_guests"`
WhoCanManageIntegrations struct {
Type []string `json:"type"`
} `json:"who_can_manage_integrations"`
WhoCanManageSharedChannels struct {
Type []string `json:"type"`
} `json:"who_can_manage_shared_channels"`
WhoCanPostGeneral string `json:"who_can_post_general"`
WhoCanPostInSharedChannels struct {
Type []string `json:"type"`
} `json:"who_can_post_in_shared_channels"`
WhoHasTeamVisibility string `json:"who_has_team_visibility"`
} `json:"prefs"`
Domain string `json:"domain"`
ID string `json:"id"`
Name string `json:"name"`
} `json:"team"`
URL string `json:"url"`
Users []struct {
Deleted bool `json:"deleted"`
ID string `json:"id"`
IsBot bool `json:"is_bot"`
Name string `json:"name"`
Presence string `json:"presence"`
Profile struct {
AvatarHash string `json:"avatar_hash"`
Email string `json:"email"`
Fields interface{} `json:"fields"`
FirstName string `json:"first_name"`
Image192 string `json:"image_192"`
Image24 string `json:"image_24"`
Image32 string `json:"image_32"`
Image48 string `json:"image_48"`
Image512 string `json:"image_512"`
Image72 string `json:"image_72"`
LastName string `json:"last_name"`
RealName string `json:"real_name"`
RealNameNormalized string `json:"real_name_normalized"`
} `json:"profile"`
TeamID string `json:"team_id"`
Updated int `json:"updated"`
ID string `json:"id"`
Name string `json:"name"`
TeamID string `json:"team_id"`
} `json:"users"`
}

View File

@@ -18,6 +18,7 @@ const (
// added or removed
defaultEnabledExchanges = 28
testFakeExchangeName = "Stampbit"
testPair = "BTC-USD"
)
func TestGetCurrencyConfig(t *testing.T) {
@@ -668,7 +669,7 @@ func TestCheckPairConfigFormats(t *testing.T) {
Index: "USD",
},
Available: currency.Pairs{
currency.NewPairDelimiter("BTC-USD", "-"),
currency.NewPairDelimiter(testPair, "-"),
},
Enabled: currency.Pairs{
currency.NewPairDelimiter("BTC~USD", "~"),
@@ -1327,7 +1328,7 @@ func TestCheckExchangeConfigValues(t *testing.T) {
setupPairs := func(emptyAssets bool) {
cfg.Exchanges[0].CurrencyPairs = nil
p := currency.Pairs{
currency.NewPairDelimiter("BTC-USD", "-"),
currency.NewPairDelimiter(testPair, "-"),
}
cfg.Exchanges[0].PairsLastUpdated = int64ptr(1234567)
@@ -1381,12 +1382,12 @@ func TestCheckExchangeConfigValues(t *testing.T) {
}
pairs := cfg.Exchanges[0].CurrencyPairs.GetPairs(asset.Spot, true)
if len(pairs) == 0 || pairs.Join() != "BTC-USD" {
if len(pairs) == 0 || pairs.Join() != testPair {
t.Error("pairs not set properly")
}
pairs = cfg.Exchanges[0].CurrencyPairs.GetPairs(asset.Spot, false)
if len(pairs) == 0 || pairs.Join() != "BTC-USD" {
if len(pairs) == 0 || pairs.Join() != testPair {
t.Error("pairs not set properly")
}

View File

@@ -7,10 +7,12 @@ services:
depends_on:
- daemon
ports:
- "9052:80"
- "9054:80"
daemon:
build: .
ports:
- "9051:9051"
- "9050:9050"
- "9051:9051"
- "9052:9052"
- "9053:9053"

View File

@@ -57,14 +57,14 @@ supplied meet the requirements to make an authenticated request.
b.API.Credentials.Secret = "your_secret"
b.API.Credentials.ClientID = "your_clientid"
order := &exchange.OrderSubmission{
o := &order.Submit{
Pair: currency.NewPair(currency.BTC, currency.USD),
OrderSide: exchange.SellOrderSide,
OrderType: exchange.LimitOrderType,
OrderSide: order.Sell,
OrderType: order.Limit,
Price: 1000000,
Amount: 0.1,
}
resp, err := b.SubmitOrder(order)
resp, err := b.SubmitOrder(o)
if err != nil {
// Handle error
}

View File

@@ -4,7 +4,9 @@ import (
"errors"
"flag"
"fmt"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
@@ -42,13 +44,10 @@ type Engine struct {
// Vars for engine
var (
Bot *Engine
)
func init() {
if Bot == nil {
return
}
}
// Stores the set flags
flagSet = make(map[string]bool)
)
// New starts a new engine
func New() (*Engine, error) {
@@ -94,6 +93,7 @@ func NewFromSettings(settings *Settings) (*Engine, error) {
b.Settings.ConfigFile = filePath
b.Settings.DataDir = settings.DataDir
b.Settings.CheckParamInteraction = settings.CheckParamInteraction
err = utils.AdjustGoMaxProcs(settings.GoMaxProcs)
if err != nil {
@@ -106,6 +106,8 @@ func NewFromSettings(settings *Settings) (*Engine, error) {
// ValidateSettings validates and sets all bot settings
func ValidateSettings(b *Engine, s *Settings) {
flag.Visit(func(f *flag.Flag) { flagSet[f.Name] = true })
b.Settings.Verbose = s.Verbose
b.Settings.EnableDryRun = s.EnableDryRun
b.Settings.EnableAllExchanges = s.EnableAllExchanges
@@ -115,26 +117,25 @@ func ValidateSettings(b *Engine, s *Settings) {
b.Settings.EnableDatabaseManager = s.EnableDatabaseManager
b.Settings.EnableDispatcher = s.EnableDispatcher
// TO-DO: FIXME
if flag.Lookup("grpc") != nil {
if flagSet["grpc"] {
b.Settings.EnableGRPC = s.EnableGRPC
} else {
b.Settings.EnableGRPC = b.Config.RemoteControl.GRPC.Enabled
}
if flag.Lookup("grpcproxy") != nil {
if flagSet["grpcproxy"] {
b.Settings.EnableGRPCProxy = s.EnableGRPCProxy
} else {
b.Settings.EnableGRPCProxy = b.Config.RemoteControl.GRPC.GRPCProxyEnabled
}
if flag.Lookup("websocketrpc") != nil {
if flagSet["websocketrpc"] {
b.Settings.EnableWebsocketRPC = s.EnableWebsocketRPC
} else {
b.Settings.EnableWebsocketRPC = b.Config.RemoteControl.WebsocketRPC.Enabled
}
if flag.Lookup("deprecatedrpc") != nil {
if flagSet["deprecatedrpc"] {
b.Settings.EnableDeprecatedRPC = s.EnableDeprecatedRPC
} else {
b.Settings.EnableDeprecatedRPC = b.Config.RemoteControl.DeprecatedRPC.Enabled
@@ -155,9 +156,13 @@ func ValidateSettings(b *Engine, s *Settings) {
b.Settings.EnableNTPClient = s.EnableNTPClient
b.Settings.EnableOrderManager = s.EnableOrderManager
b.Settings.EnableExchangeSyncManager = s.EnableExchangeSyncManager
b.Settings.EnableDepositAddressManager = s.EnableDepositAddressManager
b.Settings.EnableTickerSyncing = s.EnableTickerSyncing
b.Settings.EnableOrderbookSyncing = s.EnableOrderbookSyncing
b.Settings.EnableTradeSyncing = s.EnableTradeSyncing
b.Settings.SyncWorkers = s.SyncWorkers
b.Settings.SyncTimeout = s.SyncTimeout
b.Settings.SyncContinuously = s.SyncContinuously
b.Settings.EnableDepositAddressManager = s.EnableDepositAddressManager
b.Settings.EnableExchangeAutoPairUpdates = s.EnableExchangeAutoPairUpdates
b.Settings.EnableExchangeWebsocketSupport = s.EnableExchangeWebsocketSupport
b.Settings.EnableExchangeRESTSupport = s.EnableExchangeRESTSupport
@@ -231,14 +236,19 @@ func PrintSettings(s *Settings) {
log.Debugf(log.Global, "\t Enable order manager: %v", s.EnableOrderManager)
log.Debugf(log.Global, "\t Enable exchange sync manager: %v", s.EnableExchangeSyncManager)
log.Debugf(log.Global, "\t Enable deposit address manager: %v\n", s.EnableDepositAddressManager)
log.Debugf(log.Global, "\t Enable ticker syncing: %v", s.EnableTickerSyncing)
log.Debugf(log.Global, "\t Enable orderbook syncing: %v", s.EnableOrderbookSyncing)
log.Debugf(log.Global, "\t Enable websocket routine: %v\n", s.EnableWebsocketRoutine)
log.Debugf(log.Global, "\t Enable NTP client: %v", s.EnableNTPClient)
log.Debugf(log.Global, "\t Enable Database manager: %v", s.EnableDatabaseManager)
log.Debugf(log.Global, "\t Enable dispatcher: %v", s.EnableDispatcher)
log.Debugf(log.Global, "\t Dispatch package max worker amount: %d", s.DispatchMaxWorkerAmount)
log.Debugf(log.Global, "\t Dispatch package jobs limit: %d", s.DispatchJobsLimit)
log.Debugf(log.Global, "- EXCHANGE SYNCER SETTINGS:\n")
log.Debugf(log.Global, "\t Exchange sync continuously: %v\n", s.SyncContinuously)
log.Debugf(log.Global, "\t Exchange sync workers: %v\n", s.SyncWorkers)
log.Debugf(log.Global, "\t Enable ticker syncing: %v\n", s.EnableTickerSyncing)
log.Debugf(log.Global, "\t Enable orderbook syncing: %v\n", s.EnableOrderbookSyncing)
log.Debugf(log.Global, "\t Enable trade syncing: %v\n", s.EnableTradeSyncing)
log.Debugf(log.Global, "\t Exchange sync timeout: %v\n", s.SyncTimeout)
log.Debugf(log.Global, "- FOREX SETTINGS:")
log.Debugf(log.Global, "\t Enable currency conveter: %v", s.EnableCurrencyConverter)
log.Debugf(log.Global, "\t Enable currency layer: %v", s.EnableCurrencyLayer)
@@ -297,6 +307,10 @@ func (e *Engine) Start() error {
e.Uptime = time.Now()
log.Debugf(log.Global, "Bot '%s' started.\n", e.Config.Name)
log.Debugf(log.Global, "Using data dir: %s\n", e.Settings.DataDir)
if *e.Config.Logging.Enabled && strings.Contains(e.Config.Logging.Output, "file") {
log.Debugf(log.Global, "Using log file: %s\n",
filepath.Join(log.LogPath, e.Config.Logging.LoggerFileConfig.FileName))
}
log.Debugf(log.Global,
"Using %d out of %d logical processors for runtime performance\n",
runtime.GOMAXPROCS(-1), runtime.NumCPU())
@@ -387,8 +401,10 @@ func (e *Engine) Start() error {
exchangeSyncCfg := CurrencyPairSyncerConfig{
SyncTicker: e.Settings.EnableTickerSyncing,
SyncOrderbook: e.Settings.EnableOrderbookSyncing,
SyncContinuously: true,
NumWorkers: 15,
SyncTrades: e.Settings.EnableTradeSyncing,
SyncContinuously: e.Settings.SyncContinuously,
NumWorkers: e.Settings.SyncWorkers,
Verbose: e.Settings.Verbose,
}
e.ExchangeCurrencyPairManager, err = NewCurrencyPairSyncer(exchangeSyncCfg)

View File

@@ -4,11 +4,12 @@ import "time"
// Settings stores engine params
type Settings struct {
ConfigFile string
DataDir string
MigrationDir string
LogFile string
GoMaxProcs int
ConfigFile string
DataDir string
MigrationDir string
LogFile string
GoMaxProcs int
CheckParamInteraction bool
// Core Settings
EnableDryRun bool
@@ -23,8 +24,6 @@ type Settings struct {
EnableCommsRelayer bool
EnableExchangeSyncManager bool
EnableDepositAddressManager bool
EnableTickerSyncing bool
EnableOrderbookSyncing bool
EnableEventManager bool
EnableOrderManager bool
EnableConnectivityMonitor bool
@@ -34,6 +33,14 @@ type Settings struct {
EventManagerDelay time.Duration
Verbose bool
// Exchange syncer settings
EnableTickerSyncing bool
EnableOrderbookSyncing bool
EnableTradeSyncing bool
SyncWorkers int
SyncContinuously bool
SyncTimeout time.Duration
// Forex settings
EnableCurrencyConverter bool
EnableCurrencyLayer bool

View File

@@ -310,7 +310,6 @@ func EventManger() {
// IsValidExchange validates the exchange
func IsValidExchange(exchangeName string) bool {
exchangeName = strings.ToLower(exchangeName)
cfg := config.GetConfig()
for x := range cfg.Exchanges {
if strings.EqualFold(cfg.Exchanges[x].Name, exchangeName) && cfg.Exchanges[x].Enabled {

View File

@@ -46,6 +46,20 @@ var (
ErrExchangeFailedToLoad = errors.New("exchange failed to load")
)
func dryrunParamInteraction(param string) {
if !Bot.Settings.CheckParamInteraction {
return
}
if !Bot.Settings.EnableDryRun && !flagSet["dryrun"] {
log.Warnf(log.Global,
"Command line argument '-%s' induces dry run mode."+
" Set -dryrun=false if you wish to override this.",
param)
Bot.Settings.EnableDryRun = true
}
}
// CheckExchangeExists returns true whether or not an exchange has already
// been loaded
func CheckExchangeExists(exchName string) bool {
@@ -204,6 +218,7 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
if Bot.Settings.EnableAllPairs {
if exchCfg.CurrencyPairs != nil {
dryrunParamInteraction("enableallpairs")
assets := exchCfg.CurrencyPairs.GetAssetTypes()
for x := range assets {
pairs := exchCfg.CurrencyPairs.GetPairs(assets[x], false)
@@ -213,10 +228,12 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
}
if Bot.Settings.EnableExchangeVerbose {
dryrunParamInteraction("exchangeverbose")
exchCfg.Verbose = true
}
if Bot.Settings.EnableExchangeWebsocketSupport {
dryrunParamInteraction("exchangewebsocketsupport")
if exchCfg.Features != nil {
if exchCfg.Features.Supports.Websocket {
exchCfg.Features.Enabled.Websocket = true
@@ -225,6 +242,7 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
}
if Bot.Settings.EnableExchangeAutoPairUpdates {
dryrunParamInteraction("exchangeautopairupdates")
if exchCfg.Features != nil {
if exchCfg.Features.Supports.RESTCapabilities.AutoPairUpdates {
exchCfg.Features.Enabled.AutoPairUpdates = true
@@ -233,6 +251,7 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
}
if Bot.Settings.DisableExchangeAutoPairUpdates {
dryrunParamInteraction("exchangedisableautopairupdates")
if exchCfg.Features != nil {
if exchCfg.Features.Supports.RESTCapabilities.AutoPairUpdates {
exchCfg.Features.Enabled.AutoPairUpdates = false
@@ -241,21 +260,29 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
}
if Bot.Settings.ExchangeHTTPUserAgent != "" {
dryrunParamInteraction("exchangehttpuseragent")
exchCfg.HTTPUserAgent = Bot.Settings.ExchangeHTTPUserAgent
}
if Bot.Settings.ExchangeHTTPProxy != "" {
dryrunParamInteraction("exchangehttpproxy")
exchCfg.ProxyAddress = Bot.Settings.ExchangeHTTPProxy
}
if Bot.Settings.ExchangeHTTPTimeout != exchange.DefaultHTTPTimeout {
dryrunParamInteraction("exchangehttptimeout")
exchCfg.HTTPTimeout = Bot.Settings.ExchangeHTTPTimeout
}
if Bot.Settings.EnableExchangeHTTPDebugging {
dryrunParamInteraction("exchangehttpdebugging")
exchCfg.HTTPDebugging = Bot.Settings.EnableExchangeHTTPDebugging
}
if Bot.Settings.EnableAllExchanges {
dryrunParamInteraction("enableallexchanges")
}
exchCfg.Enabled = true
err = exch.Setup(exchCfg)
if err != nil {

View File

@@ -21,21 +21,21 @@ func SetupTest(t *testing.T) {
testSetup = true
}
if CheckExchangeExists("Bitfinex") {
if CheckExchangeExists(testExchange) {
return
}
err := LoadExchange("Bitfinex", false, nil)
err := LoadExchange(testExchange, false, nil)
if err != nil {
t.Errorf("SetupTest: Failed to load exchange: %s", err)
}
}
func CleanupTest(t *testing.T) {
if !CheckExchangeExists("Bitfinex") {
if !CheckExchangeExists(testExchange) {
return
}
err := UnloadExchange("Bitfinex")
err := UnloadExchange(testExchange)
if err != nil {
t.Fatalf("CleanupTest: Failed to unload exchange: %s",
err)
@@ -45,7 +45,7 @@ func CleanupTest(t *testing.T) {
func TestCheckExchangeExists(t *testing.T) {
SetupTest(t)
if !CheckExchangeExists("Bitfinex") {
if !CheckExchangeExists(testExchange) {
t.Errorf("TestGetExchangeExists: Unable to find exchange")
}
@@ -59,7 +59,7 @@ func TestCheckExchangeExists(t *testing.T) {
func TestGetExchangeByName(t *testing.T) {
SetupTest(t)
exch := GetExchangeByName("Bitfinex")
exch := GetExchangeByName(testExchange)
if exch == nil {
t.Errorf("TestGetExchangeByName: Failed to get exchange")
}
@@ -69,12 +69,12 @@ func TestGetExchangeByName(t *testing.T) {
}
exch.SetEnabled(false)
bfx := GetExchangeByName("Bitfinex")
bfx := GetExchangeByName(testExchange)
if bfx.IsEnabled() {
t.Errorf("TestGetExchangeByName: Unexpected result")
}
if exch.GetName() != "Bitfinex" {
if exch.GetName() != testExchange {
t.Errorf("TestGetExchangeByName: Unexpected result")
}
@@ -95,7 +95,7 @@ func TestReloadExchange(t *testing.T) {
err)
}
err = ReloadExchange("Bitfinex")
err = ReloadExchange(testExchange)
if err != nil {
t.Errorf("TestReloadExchange: Incorrect result: %s",
err)
@@ -119,7 +119,7 @@ func TestUnloadExchange(t *testing.T) {
err)
}
err = UnloadExchange("Bitfinex")
err = UnloadExchange(testExchange)
if err != nil {
t.Errorf("TestUnloadExchange: Failed to get exchange. %s",
err)
@@ -133,3 +133,66 @@ func TestUnloadExchange(t *testing.T) {
CleanupTest(t)
}
func TestDryRunParamInteraction(t *testing.T) {
SetupTest(t)
// Load bot as per normal, dry run and verbose for Bitfinex should be
// disabled
exchCfg, err := Bot.Config.GetExchangeConfig(testExchange)
if err != nil {
t.Error(err)
}
if Bot.Settings.EnableDryRun ||
exchCfg.Verbose {
t.Error("dryrun and verbose should have been disabled")
}
// Simulate overiding default settings and ensure that enabling exchange
// verbose mode will be set on Bitfinex
if err = UnloadExchange(testExchange); err != nil {
t.Error(err)
}
Bot.Settings.CheckParamInteraction = true
Bot.Settings.EnableExchangeVerbose = true
if err = LoadExchange(testExchange, false, nil); err != nil {
t.Error(err)
}
exchCfg, err = Bot.Config.GetExchangeConfig(testExchange)
if err != nil {
t.Error(err)
}
if !Bot.Settings.EnableDryRun ||
!exchCfg.Verbose {
t.Error("dryrun and verbose should have been enabled")
}
if err = UnloadExchange(testExchange); err != nil {
t.Error(err)
}
// Now set dryrun mode to false (via flagset and the previously enabled
// setting), enable exchange verbose mode and verify that verbose mode
// will be set on Bitfinex
Bot.Settings.EnableDryRun = false
Bot.Settings.CheckParamInteraction = true
Bot.Settings.EnableExchangeVerbose = true
flagSet["dryrun"] = true
if err = LoadExchange(testExchange, false, nil); err != nil {
t.Error(err)
}
exchCfg, err = Bot.Config.GetExchangeConfig(testExchange)
if err != nil {
t.Error(err)
}
if Bot.Settings.EnableDryRun ||
!exchCfg.Verbose {
t.Error("dryrun should be false and verbose should be true")
}
}

View File

@@ -335,11 +335,18 @@ func WebsocketDataHandler(ws *wshandler.Websocket) {
// TO-DO: printOrderbookSummary
//nolint:gocritic
if Bot.Settings.Verbose {
log.Infof(log.WebsocketMgr, "Websocket %s %s orderbook updated\n", ws.GetName(), result.Pair.String())
log.Infof(log.WebsocketMgr,
"Websocket %s %s orderbook updated\n",
ws.GetName(),
FormatCurrency(result.Pair),
)
}
default:
if Bot.Settings.Verbose {
log.Warnf(log.WebsocketMgr, "Websocket Unknown type: %s\n", d)
log.Warnf(log.WebsocketMgr,
"Websocket %s Unknown type: %v\n",
ws.GetName(),
d)
}
}
}

View File

@@ -17,8 +17,8 @@ const (
SyncItemOrderbook
SyncItemTrade
defaultSyncerWorkers = 30
defaultSyncerTimeout = time.Second * 15
DefaultSyncerWorkers = 15
DefaultSyncerTimeout = time.Second * 15
)
var (
@@ -33,7 +33,11 @@ func NewCurrencyPairSyncer(c CurrencyPairSyncerConfig) (*ExchangeCurrencyPairSyn
}
if c.NumWorkers <= 0 {
c.NumWorkers = defaultSyncerWorkers
c.NumWorkers = DefaultSyncerWorkers
}
if c.SyncTimeout <= time.Duration(0) {
c.SyncTimeout = DefaultSyncerTimeout
}
s := ExchangeCurrencyPairSyncer{
@@ -42,6 +46,7 @@ func NewCurrencyPairSyncer(c CurrencyPairSyncerConfig) (*ExchangeCurrencyPairSyn
SyncOrderbook: c.SyncOrderbook,
SyncTrades: c.SyncTrades,
SyncContinuously: c.SyncContinuously,
SyncTimeout: c.SyncTimeout,
NumWorkers: c.NumWorkers,
},
}
@@ -50,9 +55,9 @@ func NewCurrencyPairSyncer(c CurrencyPairSyncerConfig) (*ExchangeCurrencyPairSyn
log.Debugf(log.SyncMgr,
"Exchange currency pair syncer config: continuous: %v ticker: %v"+
" orderbook: %v trades: %v workers: %v verbose: %v\n",
" orderbook: %v trades: %v workers: %v verbose: %v timeout: %v\n",
s.Cfg.SyncContinuously, s.Cfg.SyncTicker, s.Cfg.SyncOrderbook,
s.Cfg.SyncTrades, s.Cfg.NumWorkers, s.Cfg.Verbose)
s.Cfg.SyncTrades, s.Cfg.NumWorkers, s.Cfg.Verbose, s.Cfg.SyncTimeout)
return &s, nil
}
@@ -354,9 +359,9 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
}
if e.Cfg.SyncTicker {
if !e.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemTicker) {
if c.Ticker.LastUpdated.IsZero() || time.Since(c.Ticker.LastUpdated) > defaultSyncerTimeout {
if c.Ticker.LastUpdated.IsZero() || time.Since(c.Ticker.LastUpdated) > e.Cfg.SyncTimeout {
if c.Ticker.IsUsingWebsocket {
if time.Since(c.Created) < defaultSyncerTimeout {
if time.Since(c.Created) < e.Cfg.SyncTimeout {
continue
}
@@ -385,7 +390,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
}
e.mux.Unlock()
if batchLastDone.IsZero() || time.Since(batchLastDone) > defaultSyncerTimeout {
if batchLastDone.IsZero() || time.Since(batchLastDone) > e.Cfg.SyncTimeout {
e.mux.Lock()
if e.Cfg.Verbose {
log.Debugf(log.SyncMgr, "%s Init'ing REST ticker batching\n", exchangeName)
@@ -419,9 +424,9 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
if e.Cfg.SyncOrderbook {
if !e.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemOrderbook) {
if c.Orderbook.LastUpdated.IsZero() || time.Since(c.Orderbook.LastUpdated) > defaultSyncerTimeout {
if c.Orderbook.LastUpdated.IsZero() || time.Since(c.Orderbook.LastUpdated) > e.Cfg.SyncTimeout {
if c.Orderbook.IsUsingWebsocket {
if time.Since(c.Created) < defaultSyncerTimeout {
if time.Since(c.Created) < e.Cfg.SyncTimeout {
continue
}
if supportsREST {
@@ -452,7 +457,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
}
if e.Cfg.SyncTrades {
if !e.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemTrade) {
if c.Trade.LastUpdated.IsZero() || time.Since(c.Trade.LastUpdated) > defaultSyncerTimeout {
if c.Trade.LastUpdated.IsZero() || time.Since(c.Trade.LastUpdated) > e.Cfg.SyncTimeout {
e.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, true)
e.update(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, nil)
}
@@ -570,7 +575,6 @@ func (e *ExchangeCurrencyPairSyncer) Start() {
if !e.Cfg.SyncContinuously {
log.Debugln(log.SyncMgr, "Exchange CurrencyPairSyncer stopping.")
e.Stop()
Bot.Stop()
return
}
}

View File

@@ -14,9 +14,9 @@ type CurrencyPairSyncerConfig struct {
SyncOrderbook bool
SyncTrades bool
SyncContinuously bool
NumWorkers int
Verbose bool
SyncTimeout time.Duration
NumWorkers int
Verbose bool
}
// ExchangeSyncerConfig stores the exchange syncer config

View File

@@ -153,15 +153,17 @@ func (a *Alphapoint) UpdateOrderbook(p currency.Pair, assetType asset.Item) (ord
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids,
orderbook.Item{Amount: data.Quantity, Price: data.Price})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x].Quantity,
Price: orderbookNew.Bids[x].Price,
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks,
orderbook.Item{Amount: data.Quantity, Price: data.Price})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x].Quantity,
Price: orderbookNew.Asks[x].Price,
})
}
orderBook.Pair = p
@@ -188,9 +190,8 @@ func (a *Alphapoint) FetchOrderbook(p currency.Pair, assetType asset.Item) (orde
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (a *Alphapoint) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
// https://alphapoint.github.io/slate/#generatetreasuryactivityreport
return fundHistory, common.ErrNotYetImplemented
return nil, common.ErrNotYetImplemented
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -233,7 +233,7 @@ func TestSubmitOrder(t *testing.T) {
Quote: currency.USD,
},
OrderSide: order.Buy,
OrderType: order.Market,
OrderType: order.Limit,
Price: 1,
Amount: 1,
ClientID: "meowOrder",

View File

@@ -310,8 +310,7 @@ func (a *ANX) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (a *ANX) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -87,8 +87,7 @@ func (b *Binance) GetExchangeInfo() (ExchangeInfo, error) {
// symbol: string of currency pair
// limit: returned limit amount
func (b *Binance) GetOrderBook(obd OrderBookDataRequestParams) (OrderBook, error) {
orderbook, resp := OrderBook{}, OrderBookData{}
var orderbook OrderBook
if err := b.CheckLimit(obd.Limit); err != nil {
return orderbook, err
}
@@ -97,42 +96,44 @@ func (b *Binance) GetOrderBook(obd OrderBookDataRequestParams) (OrderBook, error
params.Set("symbol", strings.ToUpper(obd.Symbol))
params.Set("limit", fmt.Sprintf("%d", obd.Limit))
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, orderBookDepth, params.Encode())
var resp OrderBookData
path := common.EncodeURLValues(b.API.Endpoints.URL+orderBookDepth, params)
if err := b.SendHTTPRequest(path, &resp); err != nil {
return orderbook, err
}
for _, asks := range resp.Asks {
var ASK struct {
Price float64
Quantity float64
for x := range resp.Bids {
price, err := strconv.ParseFloat(resp.Bids[x][0], 64)
if err != nil {
return orderbook, err
}
for i, ask := range asks.([]interface{}) {
switch i {
case 0:
ASK.Price, _ = strconv.ParseFloat(ask.(string), 64)
case 1:
ASK.Quantity, _ = strconv.ParseFloat(ask.(string), 64)
orderbook.Asks = append(orderbook.Asks, ASK)
}
amount, err := strconv.ParseFloat(resp.Bids[x][1], 64)
if err != nil {
return orderbook, err
}
orderbook.Bids = append(orderbook.Bids, OrderbookItem{
Price: price,
Quantity: amount,
})
}
for _, bids := range resp.Bids {
var BID struct {
Price float64
Quantity float64
for x := range resp.Asks {
price, err := strconv.ParseFloat(resp.Asks[x][0], 64)
if err != nil {
return orderbook, err
}
for i, bid := range bids.([]interface{}) {
switch i {
case 0:
BID.Price, _ = strconv.ParseFloat(bid.(string), 64)
case 1:
BID.Quantity, _ = strconv.ParseFloat(bid.(string), 64)
orderbook.Bids = append(orderbook.Bids, BID)
}
amount, err := strconv.ParseFloat(resp.Asks[x][1], 64)
if err != nil {
return orderbook, err
}
orderbook.Asks = append(orderbook.Asks, OrderbookItem{
Price: price,
Quantity: amount,
})
}
orderbook.LastUpdateID = resp.LastUpdateID

View File

@@ -359,7 +359,7 @@ func TestSubmitOrder(t *testing.T) {
Quote: currency.BTC,
},
OrderSide: order.Buy,
OrderType: order.Market,
OrderType: order.Limit,
Price: 1,
Amount: 1000000000,
ClientID: "meowOrder",

View File

@@ -59,13 +59,19 @@ type OrderBookDataRequestParams struct {
Limit int `json:"limit"` // Default 100; max 1000. Valid limits:[5, 10, 20, 50, 100, 500, 1000]
}
// OrderbookItem stores an individual orderbook item
type OrderbookItem struct {
Price float64
Quantity float64
}
// OrderBookData is resp data from orderbook endpoint
type OrderBookData struct {
Code int `json:"code"`
Msg string `json:"msg"`
LastUpdateID int64 `json:"lastUpdateId"`
Bids []interface{} `json:"bids"`
Asks []interface{} `json:"asks"`
Code int `json:"code"`
Msg string `json:"msg"`
LastUpdateID int64 `json:"lastUpdateId"`
Bids [][]string `json:"bids"`
Asks [][]string `json:"asks"`
}
// OrderBook actual structured data that can be used for orderbook
@@ -73,14 +79,8 @@ type OrderBook struct {
LastUpdateID int64
Code int
Msg string
Bids []struct {
Price float64
Quantity float64
}
Asks []struct {
Price float64
Quantity float64
}
Bids []OrderbookItem
Asks []OrderbookItem
}
// DepthUpdateParams is used as an embedded type for WebsocketDepthStream

View File

@@ -383,8 +383,7 @@ func (b *Binance) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (b *Binance) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -14,6 +14,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook"
@@ -433,10 +434,10 @@ func (b *Bitfinex) WsDataHandler() {
}
if len(trades) > 0 {
side := "BUY"
side := order.Buy
newAmount := trades[0].Amount
if newAmount < 0 {
side = "SELL"
side = order.Sell
newAmount *= -1
}
@@ -447,7 +448,7 @@ func (b *Bitfinex) WsDataHandler() {
Amount: newAmount,
Exchange: b.Name,
AssetType: asset.Spot,
Side: side,
Side: side.String(),
}
}
}

View File

@@ -349,8 +349,7 @@ func (b *Bitfinex) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (b *Bitfinex) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -15,6 +15,7 @@ const (
apiKey = ""
apiSecret = ""
canManipulateRealOrders = false
testCurrency = "btc"
)
var b Bithumb
@@ -54,7 +55,7 @@ func TestGetTradablePairs(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := b.GetTicker("btc")
_, err := b.GetTicker(testCurrency)
if err != nil {
t.Error("Bithumb GetTicker() error", err)
}
@@ -70,7 +71,7 @@ func TestGetAllTickers(t *testing.T) {
func TestGetOrderBook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderBook("btc")
_, err := b.GetOrderBook(testCurrency)
if err != nil {
t.Error("Bithumb GetOrderBook() error", err)
}
@@ -78,7 +79,7 @@ func TestGetOrderBook(t *testing.T) {
func TestGetTransactionHistory(t *testing.T) {
t.Parallel()
_, err := b.GetTransactionHistory("btc")
_, err := b.GetTransactionHistory(testCurrency)
if err != nil {
t.Error("Bithumb GetTransactionHistory() error", err)
}
@@ -90,7 +91,7 @@ func TestGetAccountBalance(t *testing.T) {
t.Skip()
}
_, err := b.GetAccountBalance("BTC")
_, err := b.GetAccountBalance(testCurrency)
if err == nil {
t.Error("Bithumb GetAccountBalance() Expected error")
}
@@ -118,7 +119,7 @@ func TestGetLastTransaction(t *testing.T) {
func TestGetOrders(t *testing.T) {
t.Parallel()
_, err := b.GetOrders("1337", "bid", "100", "", "BTC")
_, err := b.GetOrders("1337", order.Bid.Lower(), "100", "", testCurrency)
if err == nil {
t.Error("Bithumb GetOrders() Expected error")
}
@@ -134,7 +135,7 @@ func TestGetUserTransactions(t *testing.T) {
func TestPlaceTrade(t *testing.T) {
t.Parallel()
_, err := b.PlaceTrade("btc", "bid", 0, 0)
_, err := b.PlaceTrade(testCurrency, order.Bid.Lower(), 0, 0)
if err == nil {
t.Error("Bithumb PlaceTrade() Expected error")
}
@@ -142,7 +143,7 @@ func TestPlaceTrade(t *testing.T) {
func TestGetOrderDetails(t *testing.T) {
t.Parallel()
_, err := b.GetOrderDetails("1337", "bid", "btc")
_, err := b.GetOrderDetails("1337", order.Bid.Lower(), testCurrency)
if err == nil {
t.Error("Bithumb GetOrderDetails() Expected error")
}
@@ -185,7 +186,7 @@ func TestRequestKRWWithdraw(t *testing.T) {
func TestMarketBuyOrder(t *testing.T) {
t.Parallel()
_, err := b.MarketBuyOrder("btc", 0)
_, err := b.MarketBuyOrder(testCurrency, 0)
if err == nil {
t.Error("Bithumb MarketBuyOrder() Expected error")
}
@@ -193,7 +194,7 @@ func TestMarketBuyOrder(t *testing.T) {
func TestMarketSellOrder(t *testing.T) {
t.Parallel()
_, err := b.MarketSellOrder("btc", 0)
_, err := b.MarketSellOrder(testCurrency, 0)
if err == nil {
t.Error("Bithumb MarketSellOrder() Expected error")
}

View File

@@ -291,8 +291,7 @@ func (b *Bithumb) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (b *Bithumb) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -347,29 +347,21 @@ func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, currencyPai
switch action {
case bitmexActionInitialData:
var newOrderBook orderbook.Base
var bids, asks []orderbook.Item
for i := range data {
if strings.EqualFold(data[i].Side, order.Sell.String()) {
asks = append(asks, orderbook.Item{
newOrderBook.Asks = append(newOrderBook.Asks, orderbook.Item{
Price: data[i].Price,
Amount: float64(data[i].Size),
ID: data[i].ID,
})
continue
}
bids = append(bids, orderbook.Item{
newOrderBook.Bids = append(newOrderBook.Bids, orderbook.Item{
Price: data[i].Price,
Amount: float64(data[i].Size),
ID: data[i].ID,
})
}
if len(bids) == 0 || len(asks) == 0 {
return errors.New("bitmex_websocket.go error - snapshot not initialised correctly")
}
newOrderBook.Asks = asks
newOrderBook.Bids = bids
newOrderBook.AssetType = assetType
newOrderBook.Pair = currencyPair
newOrderBook.ExchangeName = b.Name

View File

@@ -7,9 +7,9 @@ import (
"fmt"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
@@ -40,6 +40,7 @@ const (
bitstampAPIBitcoinWithdrawal = "bitcoin_withdrawal"
bitstampAPILTCWithdrawal = "ltc_withdrawal"
bitstampAPIETHWithdrawal = "eth_withdrawal"
bitstampAPIBCHWithdrawal = "bch_withdrawal"
bitstampAPIBitcoinDeposit = "bitcoin_deposit_address"
bitstampAPILitecoinDeposit = "ltc_address"
bitstampAPIEthereumDeposit = "eth_address"
@@ -54,6 +55,7 @@ const (
bitstampAuthRate = 8000
bitstampUnauthRate = 8000
bitstampTimeLayout = "2006-1-2 15:04:05"
)
// Bitstamp is the overarching type across the bitstamp package
@@ -121,22 +123,17 @@ func getInternationalBankDepositFee(amount float64) float64 {
}
// CalculateTradingFee returns fee on a currency pair
func (b *Bitstamp) CalculateTradingFee(base, quote currency.Code, purchasePrice, amount float64, balances *Balances) float64 {
func (b *Bitstamp) CalculateTradingFee(base, quote currency.Code, purchasePrice, amount float64, balances Balances) float64 {
var fee float64
switch base.String() + quote.String() {
case currency.BTC.String() + currency.USD.String():
fee = balances.BTCUSDFee
case currency.BTC.String() + currency.EUR.String():
fee = balances.BTCEURFee
case currency.XRP.String() + currency.EUR.String():
fee = balances.XRPEURFee
case currency.XRP.String() + currency.USD.String():
fee = balances.XRPUSDFee
case currency.EUR.String() + currency.USD.String():
fee = balances.EURUSDFee
default:
fee = 0
if v, ok := balances[base.String()]; ok {
switch quote {
case currency.BTC:
fee = v.BTCFee
case currency.USD:
fee = v.USDFee
case currency.EUR:
fee = v.EURFee
}
}
return fee * purchasePrice * amount
}
@@ -259,25 +256,62 @@ func (b *Bitstamp) GetEURUSDConversionRate() (EURUSDConversionRate, error) {
}
// GetBalance returns full balance of currency held on the exchange
func (b *Bitstamp) GetBalance() (*Balances, error) {
var balance Balances
return &balance,
b.SendAuthenticatedHTTPRequest(bitstampAPIBalance, true, nil, &balance)
func (b *Bitstamp) GetBalance() (Balances, error) {
var balance map[string]string
err := b.SendAuthenticatedHTTPRequest(bitstampAPIBalance, true, nil, &balance)
if err != nil {
return nil, err
}
balances := make(map[string]Balance)
for k := range balance {
curr := k[0:3]
_, ok := balances[strings.ToUpper(curr)]
if !ok {
avail, _ := strconv.ParseFloat(balance[curr+"_available"], 64)
bal, _ := strconv.ParseFloat(balance[curr+"_balance"], 64)
reserved, _ := strconv.ParseFloat(balance[curr+"_reserved"], 64)
withdrawalFee, _ := strconv.ParseFloat(balance[curr+"_withdrawal_fee"], 64)
currBalance := Balance{
Available: avail,
Balance: bal,
Reserved: reserved,
WithdrawalFee: withdrawalFee,
}
switch strings.ToUpper(curr) {
case currency.USD.String():
eurFee, _ := strconv.ParseFloat(balance[curr+"eur_fee"], 64)
currBalance.EURFee = eurFee
case currency.EUR.String():
usdFee, _ := strconv.ParseFloat(balance[curr+"usd_fee"], 64)
currBalance.USDFee = usdFee
default:
btcFee, _ := strconv.ParseFloat(balance[curr+"btc_fee"], 64)
currBalance.BTCFee = btcFee
eurFee, _ := strconv.ParseFloat(balance[curr+"eur_fee"], 64)
currBalance.EURFee = eurFee
usdFee, _ := strconv.ParseFloat(balance[curr+"usd_fee"], 64)
currBalance.USDFee = usdFee
}
balances[strings.ToUpper(curr)] = currBalance
}
}
return balances, nil
}
// 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"`
Type int `json:"type,string"`
USD interface{} `json:"usd"`
EUR float64 `json:"eur"`
XRP float64 `json:"xrp"`
BTC interface{} `json:"btc"`
BTCUSD interface{} `json:"btc_usd"`
Fee float64 `json:"fee,string"`
OrderID int64 `json:"order_id"`
Date string `json:"datetime"`
TransactionID int64 `json:"id"`
Type int `json:"type,string"`
USD interface{} `json:"usd"`
EUR interface{} `json:"eur"`
XRP interface{} `json:"xrp"`
BTC interface{} `json:"btc"`
BTCUSD interface{} `json:"btc_usd"`
Fee float64 `json:"fee,string"`
OrderID int64 `json:"order_id"`
}
var response []Response
@@ -297,41 +331,31 @@ func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions,
}
}
processNumber := func(i interface{}) float64 {
switch t := i.(type) {
case float64:
return t
case string:
amt, _ := strconv.ParseFloat(t, 64)
return amt
default:
return 0
}
}
var transactions []UserTransactions
for _, y := range response {
for x := range response {
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 == bitstampAPIReturnType {
tx.USD, _ = strconv.ParseFloat(y.USD.(string), 64)
} else {
tx.USD = y.USD.(float64)
}
tx.EUR = y.EUR
tx.XRP = y.XRP
varType = reflect.TypeOf(y.BTC).String()
if varType == bitstampAPIReturnType {
tx.BTC, _ = strconv.ParseFloat(y.BTC.(string), 64)
} else {
tx.BTC = y.BTC.(float64)
}
varType = reflect.TypeOf(y.BTCUSD).String()
if varType == bitstampAPIReturnType {
tx.BTCUSD, _ = strconv.ParseFloat(y.BTCUSD.(string), 64)
} else {
tx.BTCUSD = y.BTCUSD.(float64)
}
tx.Fee = y.Fee
tx.OrderID = y.OrderID
tx.Date = response[x].Date
tx.TransactionID = response[x].TransactionID
tx.Type = response[x].Type
tx.EUR = processNumber(response[x].EUR)
tx.XRP = processNumber(response[x].XRP)
tx.USD = processNumber(response[x].USD)
tx.BTC = processNumber(response[x].BTC)
tx.BTCUSD = processNumber(response[x].BTCUSD)
tx.Fee = response[x].Fee
tx.OrderID = response[x].OrderID
transactions = append(transactions, tx)
}
@@ -359,13 +383,17 @@ func (b *Bitstamp) GetOrderStatus(orderID int64) (OrderStatus, error) {
}
// CancelExistingOrder cancels order by ID
func (b *Bitstamp) CancelExistingOrder(orderID int64) (bool, error) {
result := false
func (b *Bitstamp) CancelExistingOrder(orderID int64) (CancelOrder, error) {
var req = url.Values{}
req.Add("id", strconv.FormatInt(orderID, 10))
return result,
b.SendAuthenticatedHTTPRequest(bitstampAPICancelOrder, true, req, &result)
var result CancelOrder
err := b.SendAuthenticatedHTTPRequest(bitstampAPICancelOrder, true, req, &result)
if err != nil {
return result, err
}
return result, nil
}
// CancelAllExistingOrders cancels all open orders on the exchange
@@ -433,22 +461,24 @@ func (b *Bitstamp) CryptoWithdrawal(amount float64, address, symbol, destTag str
var endpoint string
switch strings.ToLower(symbol) {
case "btc":
case currency.BTC.Lower().String():
if instant {
req.Add("instant", "1")
} else {
req.Add("instant", "0")
}
endpoint = bitstampAPIBitcoinWithdrawal
case "ltc":
case currency.LTC.Lower().String():
endpoint = bitstampAPILTCWithdrawal
case "eth":
case currency.ETH.Lower().String():
endpoint = bitstampAPIETHWithdrawal
case "xrp":
case currency.XRP.Lower().String():
if destTag != "" {
req.Add("destination_tag", destTag)
}
endpoint = bitstampAPIXrpWithdrawal
case currency.BCH.Lower().String():
endpoint = bitstampAPIBCHWithdrawal
default:
return resp, errors.New("incorrect symbol")
}
@@ -669,3 +699,7 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
return common.JSONDecode(interim, result)
}
func parseTime(dateTime string) (time.Time, error) {
return time.Parse(bitstampTimeLayout, dateTime)
}

View File

@@ -143,9 +143,11 @@ func TestGetFee(t *testing.T) {
func TestCalculateTradingFee(t *testing.T) {
t.Parallel()
var newBalance = new(Balances)
newBalance.BTCUSDFee = 1
newBalance.BTCEURFee = 0
newBalance := make(Balances)
newBalance["BTC"] = Balance{
USDFee: 1,
EURFee: 0,
}
if resp := b.CalculateTradingFee(currency.BTC, currency.USD, 0, 0, newBalance); resp != 0 {
t.Error("GetFee() error")
@@ -401,15 +403,9 @@ func TestCancelExchangeOrder(t *testing.T) {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
orderCancellation := &order.Cancel{
OrderID: "1234",
}
err := b.CancelOrder(orderCancellation)
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
@@ -428,16 +424,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
}
resp, err := b.CancelAllOrders(orderCancellation)
resp, err := b.CancelAllOrders(&order.Cancel{})
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
@@ -583,3 +570,21 @@ func TestGetDepositAddress(t *testing.T) {
t.Error("GetDepositAddress error", err)
}
}
func TestParseTime(t *testing.T) {
t.Parallel()
tm, err := parseTime("2019-10-18 01:55:14")
if err != nil {
t.Error(err)
}
if tm.Year() != 2019 ||
tm.Month() != 10 ||
tm.Day() != 18 ||
tm.Hour() != 1 ||
tm.Minute() != 55 ||
tm.Second() != 14 {
t.Error("invalid time values")
}
}

View File

@@ -1,5 +1,19 @@
package bitstamp
// Transaction types
const (
Deposit = iota
Withdrawal
MarketTrade
SubAccountTransfer = 14
)
// Order side type
const (
BuyOrder = iota
SellOrder
)
// Ticker holds ticker information
type Ticker struct {
Last float64 `json:"last,string"`
@@ -52,55 +66,51 @@ type EURUSDConversionRate struct {
Sell float64 `json:"sell,string"`
}
// 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"`
// Balance stores the balance info
type Balance struct {
Available float64
Balance float64
Reserved float64
WithdrawalFee float64
BTCFee float64 // for cryptocurrency pairs
USDFee float64
EURFee float64
}
// Balances holds full balance information with the supplied APIKEYS
type Balances map[string]Balance
// UserTransactions holds user transaction information
type UserTransactions struct {
Date string `json:"datetime"`
TransID int64 `json:"id"`
Type int `json:"type,string"`
USD float64 `json:"usd"`
EUR float64 `json:"eur"`
BTC float64 `json:"btc"`
XRP float64 `json:"xrp"`
BTCUSD float64 `json:"btc_usd"`
Fee float64 `json:"fee,string"`
OrderID int64 `json:"order_id"`
Date string `json:"datetime"`
TransactionID int64 `json:"id"`
Type int `json:"type,string"`
USD float64 `json:"usd"`
EUR float64 `json:"eur"`
BTC float64 `json:"btc"`
XRP float64 `json:"xrp"`
BTCUSD float64 `json:"btc_usd"`
Fee float64 `json:"fee,string"`
OrderID int64 `json:"order_id"`
}
// Order holds current open order data
type Order struct {
ID int64 `json:"id"`
Date int64 `json:"datetime"`
Type int `json:"type"`
Price float64 `json:"price"`
Amount float64 `json:"amount"`
ID int64 `json:"id,string"`
DateTime string `json:"datetime"`
Type int `json:"type,string"`
Price float64 `json:"price,string"`
Amount float64 `json:"amount,string"`
Currency string `json:"currency_pair"`
}
// OrderStatus holds order status information
type OrderStatus struct {
Price float64 `json:"price,string"`
Amount float64 `json:"amount,string"`
Type int `json:"type"`
ID int64 `json:"id,string"`
DateTime string `json:"datetime"`
Status string
Transactions []struct {
TradeID int64 `json:"tid"`
@@ -111,6 +121,14 @@ type OrderStatus struct {
}
}
// CancelOrder holds the order cancellation info
type CancelOrder struct {
Price float64 `json:"price"`
Amount float64 `json:"amount"`
Type int `json:"type"`
ID int64 `json:"id"`
}
// WithdrawalRequests holds request information on withdrawals
type WithdrawalRequests struct {
OrderID int64 `json:"id"`

View File

@@ -225,24 +225,18 @@ func (b *Bitstamp) seedOrderBook() error {
}
var newOrderBook orderbook.Base
var asks, bids []orderbook.Item
for i := range orderbookSeed.Asks {
var item orderbook.Item
item.Amount = orderbookSeed.Asks[i].Amount
item.Price = orderbookSeed.Asks[i].Price
asks = append(asks, item)
newOrderBook.Asks = append(newOrderBook.Asks, orderbook.Item{
Price: orderbookSeed.Asks[i].Price,
Amount: orderbookSeed.Asks[i].Amount,
})
}
for i := range orderbookSeed.Bids {
var item orderbook.Item
item.Amount = orderbookSeed.Bids[i].Amount
item.Price = orderbookSeed.Bids[i].Price
bids = append(bids, item)
newOrderBook.Bids = append(newOrderBook.Bids, orderbook.Item{
Price: orderbookSeed.Bids[i].Price,
Amount: orderbookSeed.Bids[i].Amount,
})
}
newOrderBook.Asks = asks
newOrderBook.Bids = bids
newOrderBook.Pair = p[x]
newOrderBook.AssetType = asset.Spot
newOrderBook.ExchangeName = b.Name

View File

@@ -289,13 +289,17 @@ func (b *Bitstamp) UpdateOrderbook(p currency.Pair, assetType asset.Item) (order
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x].Amount,
Price: orderbookNew.Bids[x].Price,
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x].Amount,
Price: orderbookNew.Asks[x].Price,
})
}
orderBook.Pair = p
@@ -320,27 +324,13 @@ func (b *Bitstamp) GetAccountInfo() (exchange.AccountInfo, error) {
return response, err
}
var currencies = []exchange.AccountCurrencyInfo{
{
CurrencyName: currency.BTC,
TotalValue: accountBalance.BTCAvailable,
Hold: accountBalance.BTCReserved,
},
{
CurrencyName: currency.XRP,
TotalValue: accountBalance.XRPAvailable,
Hold: accountBalance.XRPReserved,
},
{
CurrencyName: currency.USD,
TotalValue: accountBalance.USDAvailable,
Hold: accountBalance.USDReserved,
},
{
CurrencyName: currency.EUR,
TotalValue: accountBalance.EURAvailable,
Hold: accountBalance.EURReserved,
},
var currencies []exchange.AccountCurrencyInfo
for k, v := range accountBalance {
currencies = append(currencies, exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(k),
TotalValue: v.Available,
Hold: v.Reserved,
})
}
response.Accounts = append(response.Accounts, exchange.Account{
Currencies: currencies,
@@ -352,8 +342,7 @@ func (b *Bitstamp) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (b *Bitstamp) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
@@ -518,7 +507,6 @@ func (b *Bitstamp) GetWebsocket() (*wshandler.Websocket, error) {
// GetActiveOrders retrieves any orders that are active/open
func (b *Bitstamp) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) {
var orders []order.Detail
var currPair string
if len(req.Currencies) != 1 {
currPair = "all"
@@ -531,16 +519,28 @@ func (b *Bitstamp) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail,
return nil, err
}
var orders []order.Detail
for i := range resp {
orderDate := time.Unix(resp[i].Date, 0)
orderSide := order.Buy
if resp[i].Type == SellOrder {
orderSide = order.Sell
}
tm, err := parseTime(resp[i].DateTime)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s GetActiveOrders unable to parse time: %s\n", b.Name, err)
}
orders = append(orders, order.Detail{
Amount: resp[i].Amount,
ID: strconv.FormatInt(resp[i].ID, 10),
Price: resp[i].Price,
OrderDate: orderDate,
CurrencyPair: currency.NewPairFromStrings(resp[i].Currency[0:3],
resp[i].Currency[len(resp[i].Currency)-3:]),
Exchange: b.Name,
Amount: resp[i].Amount,
ID: strconv.FormatInt(resp[i].ID, 10),
Price: resp[i].Price,
OrderType: order.Limit,
OrderSide: orderSide,
OrderDate: tm,
CurrencyPair: currency.NewPairFromString(resp[i].Currency),
Exchange: b.Name,
})
}
@@ -563,7 +563,7 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
var orders []order.Detail
for i := range resp {
if resp[i].Type != 2 {
if resp[i].Type != MarketTrade {
continue
}
var quoteCurrency, baseCurrency currency.Code
@@ -575,7 +575,8 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
baseCurrency = currency.XRP
default:
log.Warnf(log.ExchangeSys,
"no base currency found for OrderID '%d'",
"%s No base currency found for OrderID '%d'\n",
b.Name,
resp[i].OrderID)
}
@@ -586,7 +587,8 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
quoteCurrency = currency.EUR
default:
log.Warnf(log.ExchangeSys,
"no quote currency found for orderID '%d'",
"%s No quote currency found for orderID '%d'\n",
b.Name,
resp[i].OrderID)
}
@@ -597,14 +599,15 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
b.GetPairFormat(asset.Spot, false).Delimiter)
}
orderDate, err := time.Parse("2006-01-02 15:04:05", resp[i].Date)
tm, err := parseTime(resp[i].Date)
if err != nil {
return nil, err
log.Errorf(log.ExchangeSys,
"%s GetOrderHistory unable to parse time: %s\n", b.Name, err)
}
orders = append(orders, order.Detail{
ID: strconv.FormatInt(resp[i].OrderID, 10),
OrderDate: orderDate,
OrderDate: tm,
Exchange: b.Name,
CurrencyPair: currPair,
})

View File

@@ -7,6 +7,7 @@ import (
"net/url"
"strconv"
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -53,6 +54,7 @@ const (
bittrexAuthRate = 0
bittrexUnauthRate = 0
bittrexTimeLayout = "2006-01-02T15:04:05"
)
// Bittrex is the overaching type across the bittrex methods
@@ -508,3 +510,7 @@ func (b *Bittrex) GetWithdrawalFee(c currency.Code) (float64, error) {
func calculateTradingFee(price, amount float64) float64 {
return 0.0025 * price * amount
}
func parseTime(t string) (time.Time, error) {
return time.Parse(bittrexTimeLayout, t)
}

View File

@@ -534,3 +534,21 @@ func TestGetDepositAddress(t *testing.T) {
}
}
}
func TestParseTime(t *testing.T) {
t.Parallel()
tm, err := parseTime("2019-11-21T02:08:34.87")
if err != nil {
t.Fatal(err)
}
if tm.Year() != 2019 ||
tm.Month() != 11 ||
tm.Day() != 21 ||
tm.Hour() != 2 ||
tm.Minute() != 8 ||
tm.Second() != 34 {
t.Error("invalid time values")
}
}

View File

@@ -227,7 +227,11 @@ func (b *Bittrex) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pr
if !strings.EqualFold(ticks.Result[j].MarketName, pairs[i].String()) {
continue
}
tickerTime, _ := time.Parse(time.RFC3339, ticks.Result[j].TimeStamp)
tickerTime, err := parseTime(ticks.Result[j].TimeStamp)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s UpdateTicker unable to parse time: %s\n", b.Name, err)
}
tickerPrice = ticker.Price{
Last: ticks.Result[j].Last,
High: ticks.Result[j].High,
@@ -309,8 +313,7 @@ func (b *Bittrex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderb
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (b *Bittrex) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
@@ -451,9 +454,9 @@ func (b *Bittrex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail,
var orders []order.Detail
for i := range resp.Result {
orderDate, err := time.Parse(time.RFC3339, resp.Result[i].Opened)
orderDate, err := parseTime(resp.Result[i].Opened)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
b.Name,
"GetActiveOrders",
@@ -498,9 +501,9 @@ func (b *Bittrex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
var orders []order.Detail
for i := range resp.Result {
orderDate, err := time.Parse(time.RFC3339, resp.Result[i].TimeStamp)
orderDate, err := parseTime(resp.Result[i].TimeStamp)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
b.Name,
"GetActiveOrders",

View File

@@ -193,6 +193,7 @@ type WsOrderbook struct {
MessageType string `json:"messageType"`
}
// WsError message received for orderbook errors
type WsError struct {
MessageType string `json:"messageType"`
Code int64 `json:"code"`

View File

@@ -279,13 +279,17 @@ func (b *BTCMarkets) UpdateOrderbook(p currency.Pair, assetType asset.Item) (ord
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x][1],
Price: orderbookNew.Bids[x][0],
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x][1],
Price: orderbookNew.Asks[x][0],
})
}
orderBook.Pair = p
@@ -331,8 +335,7 @@ func (b *BTCMarkets) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (b *BTCMarkets) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -42,6 +42,7 @@ const (
btsePendingOrders = "pending"
btseDeleteOrder = "deleteOrder"
btseFills = "fills"
btseTimeLayout = "2006-01-02 15:04:04"
)
// GetMarketsSummary stores market summary data
@@ -238,7 +239,7 @@ func (b *BTSE) SendAuthenticatedHTTPRequest(method, endpoint string, req map[str
b.Name, method, path, string(payload))
}
return b.SendPayload(method,
btseAPIURL+path,
b.API.Endpoints.URL+path,
headers,
body,
&result,
@@ -320,7 +321,6 @@ func calculateTradingFee(isMaker bool) float64 {
return fee
}
func parseOrderTime(timeStr string) time.Time {
t, _ := time.Parse("2006-01-02 15:04:04", timeStr)
return t
func parseOrderTime(timeStr string) (time.Time, error) {
return time.Parse(btseTimeLayout, timeStr)
}

View File

@@ -17,6 +17,7 @@ const (
apiKey = ""
apiSecret = ""
canManipulateRealOrders = false
testPair = "BTC-USD"
)
var b BTSE
@@ -37,7 +38,11 @@ func TestMain(m *testing.M) {
btseConfig.API.Credentials.Key = apiKey
btseConfig.API.Credentials.Secret = apiSecret
b.Setup(btseConfig)
err = b.Setup(btseConfig)
if err != nil {
log.Fatal(err)
}
os.Exit(m.Run())
}
@@ -63,7 +68,7 @@ func TestGetMarkets(t *testing.T) {
func TestFetchOrderBook(t *testing.T) {
t.Parallel()
_, err := b.FetchOrderBook("BTC-USD")
_, err := b.FetchOrderBook(testPair)
if err != nil {
t.Error(err)
}
@@ -71,7 +76,7 @@ func TestFetchOrderBook(t *testing.T) {
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := b.GetTrades("BTC-USD")
_, err := b.GetTrades(testPair)
if err != nil {
t.Error(err)
}
@@ -79,7 +84,7 @@ func TestGetTrades(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := b.GetTicker("BTC-USD")
_, err := b.GetTicker(testPair)
if err != nil {
t.Error(err)
}
@@ -87,7 +92,7 @@ func TestGetTicker(t *testing.T) {
func TestGetMarketStatistics(t *testing.T) {
t.Parallel()
_, err := b.GetMarketStatistics("BTC-USD")
_, err := b.GetMarketStatistics(testPair)
if err != nil {
t.Error(err)
}
@@ -117,7 +122,7 @@ func TestGetFills(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping test")
}
_, err := b.GetFills("", "BTC-USD", "", "", "", "")
_, err := b.GetFills("", testPair, "", "", "", "")
if err != nil {
t.Error(err)
}
@@ -128,7 +133,13 @@ func TestCreateOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
}
_, err := b.CreateOrder(0.1, 10000, "sell", "limit", "BTC-USD", "", "")
_, err := b.CreateOrder(0.1,
10000,
order.Sell.String(),
order.Limit.String(),
testPair,
"",
"")
if err != nil {
t.Error(err)
}
@@ -266,9 +277,12 @@ func TestGetFee(t *testing.T) {
func TestParseOrderTime(t *testing.T) {
expected := int64(1534794360)
actual := parseOrderTime("2018-08-20 19:20:46").Unix()
if expected != actual {
t.Errorf("TestParseOrderTime expected: %d, got %d", expected, actual)
actual, err := parseOrderTime("2018-08-20 19:20:46")
if err != nil {
t.Fatal(err)
}
if expected != actual.Unix() {
t.Errorf("TestParseOrderTime expected: %d, got %d", expected, actual.Unix())
}
}

View File

@@ -103,8 +103,8 @@ func (b *BTSE) WsHandleData() {
b.Websocket.DataHandler <- err
continue
}
var newOB orderbook.Base
var price, amount float64
var asks, bids []orderbook.Item
for i := range t.Data.SellQuote {
p := strings.Replace(t.Data.SellQuote[i].Price, ",", "", -1)
price, err = strconv.ParseFloat(p, 64)
@@ -118,7 +118,10 @@ func (b *BTSE) WsHandleData() {
b.Websocket.DataHandler <- err
continue
}
asks = append(asks, orderbook.Item{Price: price, Amount: amount})
newOB.Asks = append(newOB.Asks, orderbook.Item{
Price: price,
Amount: amount,
})
}
for j := range t.Data.BuyQuote {
p := strings.Replace(t.Data.BuyQuote[j].Price, ",", "", -1)
@@ -133,11 +136,11 @@ func (b *BTSE) WsHandleData() {
b.Websocket.DataHandler <- err
continue
}
bids = append(bids, orderbook.Item{Price: price, Amount: amount})
newOB.Bids = append(newOB.Bids, orderbook.Item{
Price: price,
Amount: amount,
})
}
var newOB orderbook.Base
newOB.Asks = asks
newOB.Bids = bids
newOB.AssetType = asset.Spot
newOB.Pair = currency.NewPairFromString(t.Topic[strings.Index(t.Topic, ":")+1 : strings.Index(t.Topic, "_")])
newOB.ExchangeName = b.Name

View File

@@ -450,7 +450,11 @@ func (b *BTSE) GetOrderInfo(orderID string) (order.Detail, error) {
od.Exchange = b.Name
od.Amount = o[i].Amount
od.ID = o[i].ID
od.OrderDate = parseOrderTime(o[i].CreatedAt)
od.OrderDate, err = parseOrderTime(o[i].CreatedAt)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s GetOrderInfo unable to parse time: %s\n", b.Name, err)
}
od.OrderSide = side
od.OrderType = order.Type(strings.ToUpper(o[i].Type))
od.Price = o[i].Price
@@ -464,7 +468,11 @@ func (b *BTSE) GetOrderInfo(orderID string) (order.Detail, error) {
}
for i := range fills {
createdAt, _ := time.Parse(time.RFC3339, fills[i].CreatedAt)
createdAt, err := parseOrderTime(fills[i].CreatedAt)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s GetOrderInfo unable to parse time: %s\n", b.Name, err)
}
od.Trades = append(od.Trades, order.TradeHistory{
Timestamp: createdAt,
TID: fills[i].ID,
@@ -521,13 +529,21 @@ func (b *BTSE) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, err
side = order.Sell
}
tm, err := parseOrderTime(resp[i].CreatedAt)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s GetActiveOrders unable to parse time: %s\n",
b.Name,
err)
}
openOrder := order.Detail{
CurrencyPair: currency.NewPairDelimiter(resp[i].Symbol,
b.GetPairFormat(asset.Spot, false).Delimiter),
Exchange: b.Name,
Amount: resp[i].Amount,
ID: resp[i].ID,
OrderDate: parseOrderTime(resp[i].CreatedAt),
OrderDate: tm,
OrderSide: side,
OrderType: order.Type(strings.ToUpper(resp[i].Type)),
Price: resp[i].Price,
@@ -544,7 +560,13 @@ func (b *BTSE) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, err
}
for i := range fills {
createdAt, _ := time.Parse(time.RFC3339, fills[i].CreatedAt)
createdAt, err := parseOrderTime(fills[i].CreatedAt)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s GetActiveOrders unable to parse time: %s\n",
b.Name,
err)
}
openOrder.Trades = append(openOrder.Trades, order.TradeHistory{
Timestamp: createdAt,
TID: fills[i].ID,

View File

@@ -22,6 +22,7 @@ const (
apiSecret = ""
clientID = "" // passphrase you made at API CREATION
canManipulateRealOrders = false
testPair = "BTC-USD"
)
func TestSetDefaults(t *testing.T) {
@@ -58,28 +59,28 @@ func TestGetProducts(t *testing.T) {
}
func TestGetTicker(t *testing.T) {
_, err := c.GetTicker("BTC-USD")
_, err := c.GetTicker(testPair)
if err != nil {
t.Error("GetTicker() error", err)
}
}
func TestGetTrades(t *testing.T) {
_, err := c.GetTrades("BTC-USD")
_, err := c.GetTrades(testPair)
if err != nil {
t.Error("GetTrades() error", err)
}
}
func TestGetHistoricRates(t *testing.T) {
_, err := c.GetHistoricRates("BTC-USD", 0, 0, 0)
_, err := c.GetHistoricRates(testPair, 0, 0, 0)
if err != nil {
t.Error("GetHistoricRates() error", err)
}
}
func TestGetStats(t *testing.T) {
_, err := c.GetStats("BTC-USD")
_, err := c.GetStats(testPair)
if err != nil {
t.Error("GetStats() error", err)
}
@@ -128,21 +129,23 @@ func TestAuthRequests(t *testing.T) {
if err == nil {
t.Error("Expecting error")
}
orderResponse, err := c.PlaceLimitOrder("", 0.001, 0.001, "buy", "", "", "BTC-USD", "", false)
orderResponse, err := c.PlaceLimitOrder("", 0.001, 0.001,
order.Buy.Lower(), "", "", testPair, "", false)
if orderResponse != "" {
t.Error("Expecting no data returned")
}
if err == nil {
t.Error("Expecting error")
}
marketOrderResponse, err := c.PlaceMarketOrder("", 1, 0, "buy", "BTC-USD", "")
marketOrderResponse, err := c.PlaceMarketOrder("", 1, 0,
order.Buy.Lower(), testPair, "")
if marketOrderResponse != "" {
t.Error("Expecting no data returned")
}
if err == nil {
t.Error("Expecting error")
}
fillsResponse, err := c.GetFills("1337", "BTC-USD")
fillsResponse, err := c.GetFills("1337", testPair)
if len(fillsResponse) > 0 {
t.Error("Expecting no data returned")
}
@@ -616,7 +619,7 @@ func TestWsAuth(t *testing.T) {
go c.WsHandleData()
err = c.Subscribe(wshandler.WebsocketChannelSubscription{
Channel: "user",
Currency: currency.NewPairFromString("BTC-USD"),
Currency: currency.NewPairFromString(testPair),
})
if err != nil {
t.Error(err)

View File

@@ -515,7 +515,7 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta
orderType := order.Type(strings.ToUpper(respOrders[i].Type))
orderDate, err := time.Parse(time.RFC3339, respOrders[i].CreatedAt)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
c.Name,
"GetActiveOrders",
@@ -562,7 +562,7 @@ func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Deta
orderType := order.Type(strings.ToUpper(respOrders[i].Type))
orderDate, err := time.Parse(time.RFC3339, respOrders[i].CreatedAt)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
c.Name,
"GetActiveOrders",

View File

@@ -48,22 +48,22 @@ main.go
```go
var c exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "Coinbene" {
c = bot.exchanges[i]
for i := range Bot.Exchanges {
if Bot.Exchanges[i].GetName() == "Coinbene" {
c = Bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := c.GetTickerPrice()
tick, err := c.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := c.GetOrderbookEx()
ob, err := c.FetchOrderbook()
if err != nil {
// Handle error
}

View File

@@ -247,9 +247,14 @@ func (c *Coinbene) SendHTTPRequest(path string, result interface{}) error {
// SendAuthHTTPRequest sends an authenticated HTTP request
func (c *Coinbene) SendAuthHTTPRequest(method, path, epPath string, params url.Values, result interface{}) error {
if !c.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, c.Name)
}
if params == nil {
params = url.Values{}
}
timestamp := time.Now().UTC().Format("2006-01-02T15:04:05.999Z")
var finalBody io.Reader
var preSign string

View File

@@ -34,7 +34,11 @@ func TestMain(m *testing.M) {
coinbeneConfig.API.AuthenticatedSupport = true
coinbeneConfig.API.Credentials.Secret = testAPISecret
coinbeneConfig.API.Credentials.Key = testAPIKey
c.Setup(coinbeneConfig)
err = c.Setup(coinbeneConfig)
if err != nil {
log.Fatal(err)
}
os.Exit(m.Run())
}

View File

@@ -421,9 +421,7 @@ func (c *COINUT) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbo
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (c *COINUT) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -755,10 +755,10 @@ func (e *Base) FormatWithdrawPermissions() string {
return NoAPIWithdrawalMethodsText
}
// IsAssetTypeSupported whether or not the supplied asset is supported
// SupportsAsset whether or not the supplied asset is supported
// by the exchange
func (e *Base) IsAssetTypeSupported(asset asset.Item) bool {
return e.CurrencyPairs.AssetTypes.Contains(asset)
func (e *Base) SupportsAsset(a asset.Item) bool {
return e.CurrencyPairs.AssetTypes.Contains(a)
}
// PrintEnabledPairs prints the exchanges enabled asset pairs

View File

@@ -550,7 +550,7 @@ func TestGetEnabledPairs(t *testing.T) {
}
b.CurrencyPairs.StorePairs(asset.Spot,
currency.NewPairsFromStrings([]string{"BTC-USD"}), true)
currency.NewPairsFromStrings([]string{defaultTestCurrencyPair}), true)
format := currency.PairFormat{
Delimiter: "-",
Index: "",
@@ -1321,18 +1321,16 @@ func TestFormatWithdrawPermissions(t *testing.T) {
}
}
func TestIsAssetTypeSupported(t *testing.T) {
func TestSupportsAsset(t *testing.T) {
t.Parallel()
var b Base
b.CurrencyPairs.AssetTypes = asset.Items{
asset.Spot,
}
if !b.IsAssetTypeSupported(asset.Spot) {
if !b.SupportsAsset(asset.Spot) {
t.Error("spot should be supported")
}
if b.IsAssetTypeSupported(asset.Index) {
if b.SupportsAsset(asset.Index) {
t.Error("index shouldn't be supported")
}
}

View File

@@ -313,8 +313,7 @@ func (e *EXMO) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (e *EXMO) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -277,13 +277,17 @@ func (g *Gateio) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbo
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x].Amount,
Price: orderbookNew.Bids[x].Price,
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x].Amount,
Price: orderbookNew.Asks[x].Price,
})
}
orderBook.Pair = p
@@ -366,8 +370,7 @@ func (g *Gateio) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (g *Gateio) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -91,7 +91,11 @@ func TestGetAuctionHistory(t *testing.T) {
func TestNewOrder(t *testing.T) {
t.Parallel()
_, err := g.NewOrder(testCurrency, 1, 9000, "buy", "exchange limit")
_, err := g.NewOrder(testCurrency,
1,
9000000,
order.Sell.Lower(),
"exchange limit")
if err != nil && mockTests {
t.Error("NewOrder() error", err)
} else if err == nil && !mockTests {

View File

@@ -307,8 +307,7 @@ func (g *Gemini) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbo
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (g *Gemini) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -232,22 +232,23 @@ func (h *HitBTC) WsProcessOrderbookSnapshot(ob WsOrderbook) error {
return errors.New("hitbtc.go error - no orderbooks to process")
}
var bids []orderbook.Item
var newOrderBook orderbook.Base
for i := range ob.Params.Bid {
bids = append(bids, orderbook.Item{Amount: ob.Params.Bid[i].Size, Price: ob.Params.Bid[i].Price})
newOrderBook.Bids = append(newOrderBook.Bids, orderbook.Item{
Amount: ob.Params.Bid[i].Size,
Price: ob.Params.Bid[i].Price,
})
}
var asks []orderbook.Item
for i := range ob.Params.Ask {
asks = append(asks, orderbook.Item{Amount: ob.Params.Ask[i].Size, Price: ob.Params.Ask[i].Price})
newOrderBook.Asks = append(newOrderBook.Asks, orderbook.Item{
Amount: ob.Params.Ask[i].Size,
Price: ob.Params.Ask[i].Price,
})
}
p := currency.NewPairFromFormattedPairs(ob.Params.Symbol,
h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true))
var newOrderBook orderbook.Base
newOrderBook.Asks = asks
newOrderBook.Bids = bids
newOrderBook.AssetType = asset.Spot
newOrderBook.Pair = p
newOrderBook.ExchangeName = h.Name
@@ -274,11 +275,17 @@ func (h *HitBTC) WsProcessOrderbookUpdate(update WsOrderbook) error {
var bids, asks []orderbook.Item
for i := range update.Params.Bid {
bids = append(bids, orderbook.Item{Price: update.Params.Bid[i].Price, Amount: update.Params.Bid[i].Size})
bids = append(bids, orderbook.Item{
Price: update.Params.Bid[i].Price,
Amount: update.Params.Bid[i].Size,
})
}
for i := range update.Params.Ask {
asks = append(asks, orderbook.Item{Price: update.Params.Ask[i].Price, Amount: update.Params.Ask[i].Size})
asks = append(asks, orderbook.Item{
Price: update.Params.Ask[i].Price,
Amount: update.Params.Ask[i].Size,
})
}
p := currency.NewPairFromFormattedPairs(update.Params.Symbol,

View File

@@ -309,13 +309,17 @@ func (h *HitBTC) UpdateOrderbook(currencyPair currency.Pair, assetType asset.Ite
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x].Amount,
Price: orderbookNew.Bids[x].Price,
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x].Amount,
Price: orderbookNew.Asks[x].Price,
})
}
orderBook.Pair = currencyPair
@@ -360,8 +364,7 @@ func (h *HitBTC) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (h *HitBTC) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
@@ -379,8 +382,8 @@ func (h *HitBTC) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
response, err := h.PlaceOrder(s.Pair.String(),
s.Price,
s.Amount,
strings.ToLower(s.OrderType.String()),
strings.ToLower(s.OrderSide.String()))
s.OrderType.Lower(),
s.OrderSide.Lower())
if response.OrderNumber > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10)
}

View File

@@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
@@ -350,13 +349,17 @@ func (h *HUOBI) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderboo
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x][1],
Price: orderbookNew.Bids[x][0],
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x][1],
Price: orderbookNew.Asks[x][0],
})
}
orderBook.Pair = p
@@ -452,8 +455,7 @@ func (h *HUOBI) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (h *HUOBI) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
@@ -600,7 +602,7 @@ func (h *HUOBI) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er
if req.OrderSide == order.AnySide || req.OrderSide == "" {
side = ""
} else if req.OrderSide == order.Sell {
side = strings.ToLower(string(req.OrderSide))
side = req.OrderSide.Lower()
}
var orders []order.Detail

View File

@@ -66,4 +66,5 @@ type IBotExchange interface {
GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error)
GetDefaultConfig() (*config.ExchangeConfig, error)
GetBase() *Base
SupportsAsset(assetType asset.Item) bool
}

View File

@@ -193,13 +193,12 @@ func (i *ItBit) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderboo
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
var price, amount float64
price, err = strconv.ParseFloat(data[0], 64)
price, err = strconv.ParseFloat(orderbookNew.Bids[x][0], 64)
if err != nil {
return orderBook, err
}
amount, err = strconv.ParseFloat(data[1], 64)
amount, err = strconv.ParseFloat(orderbookNew.Bids[x][1], 64)
if err != nil {
return orderBook, err
}
@@ -211,13 +210,12 @@ func (i *ItBit) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderboo
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
var price, amount float64
price, err = strconv.ParseFloat(data[0], 64)
price, err = strconv.ParseFloat(orderbookNew.Asks[x][0], 64)
if err != nil {
return orderBook, err
}
amount, err = strconv.ParseFloat(data[1], 64)
amount, err = strconv.ParseFloat(orderbookNew.Asks[x][1], 64)
if err != nil {
return orderBook, err
}
@@ -287,8 +285,7 @@ func (i *ItBit) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (i *ItBit) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
@@ -444,7 +441,7 @@ func (i *ItBit) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er
side := order.Side(strings.ToUpper(allOrders[j].Side))
orderDate, err := time.Parse(time.RFC3339, allOrders[j].CreatedTime)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
i.Name,
"GetActiveOrders",
@@ -498,7 +495,7 @@ func (i *ItBit) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er
side := order.Side(strings.ToUpper(allOrders[j].Side))
orderDate, err := time.Parse(time.RFC3339, allOrders[j].CreatedTime)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
i.Name,
"GetActiveOrders",

View File

@@ -386,8 +386,7 @@ func (k *Kraken) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (k *Kraken) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -10,6 +10,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
@@ -21,11 +22,9 @@ const (
marketGlobalEndpoint = "market-global"
marketSubstring = "market-"
globalSubstring = "-global"
tickerBuyString = "buy"
tickerHighString = "high"
tickerLastString = "last"
tickerLowString = "low"
tickerSellString = "sell"
tickerVolumeString = "volume"
wssSchem = "wss"
)
@@ -270,11 +269,11 @@ func (l *LakeBTC) processTicker(ticker string) error {
l.Websocket.DataHandler <- wshandler.TickerData{
Exchange: l.Name,
Bid: processTickerItem(tickerData, tickerBuyString),
Bid: processTickerItem(tickerData, order.Buy.Lower()),
High: processTickerItem(tickerData, tickerHighString),
Last: processTickerItem(tickerData, tickerLastString),
Low: processTickerItem(tickerData, tickerLowString),
Ask: processTickerItem(tickerData, tickerSellString),
Ask: processTickerItem(tickerData, order.Sell.Lower()),
Volume: processTickerItem(tickerData, tickerVolumeString),
AssetType: asset.Spot,
Pair: returnCurrency,

View File

@@ -316,8 +316,7 @@ func (l *LakeBTC) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (l *LakeBTC) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -47,22 +47,22 @@ main.go
```go
var l exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "Lbank" {
l = bot.exchanges[i]
for i := range Bot.Exchanges {
if Bot.Exchanges[i].GetName() == "Lbank" {
l = Bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := l.GetTickerPrice()
tick, err := l.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := l.GetOrderbookEx()
ob, err := l.FetchOrderbook()
if err != nil {
// Handle error
}

View File

@@ -17,6 +17,7 @@ import (
gctcrypto "github.com/thrasher-corp/gocryptotrader/common/crypto"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
)
@@ -195,7 +196,8 @@ func (l *Lbank) GetUserInfo() (InfoFinalResponse, error) {
// CreateOrder creates an order
func (l *Lbank) CreateOrder(pair, side string, amount, price float64) (CreateOrderResponse, error) {
var resp CreateOrderResponse
if !strings.EqualFold(side, "buy") && !strings.EqualFold(side, "sell") {
if !strings.EqualFold(side, order.Buy.String()) &&
!strings.EqualFold(side, order.Sell.String()) {
return resp, errors.New("side type invalid can only be 'buy' or 'sell'")
}
if amount <= 0 {
@@ -546,6 +548,10 @@ func (l *Lbank) sign(data string) (string, error) {
// SendAuthHTTPRequest sends an authenticated request
func (l *Lbank) SendAuthHTTPRequest(method, endpoint string, vals url.Values, result interface{}) error {
if !l.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
}
if vals == nil {
vals = url.Values{}
}

View File

@@ -407,7 +407,7 @@ func (l *Lbank) GetOrderInfo(orderID string) (order.Detail, error) {
}
resp.Exchange = l.Name
resp.CurrencyPair = currency.NewPairFromString(key)
if strings.EqualFold(tempResp.Orders[0].Type, "buy") {
if strings.EqualFold(tempResp.Orders[0].Type, order.Buy.String()) {
resp.OrderSide = order.Buy
} else {
resp.OrderSide = order.Sell
@@ -491,7 +491,7 @@ func (l *Lbank) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]ord
}
resp.Exchange = l.Name
resp.CurrencyPair = currency.NewPairFromString(key)
if strings.EqualFold(tempResp.Orders[0].Type, "buy") {
if strings.EqualFold(tempResp.Orders[0].Type, order.Buy.String()) {
resp.OrderSide = order.Buy
} else {
resp.OrderSide = order.Sell
@@ -567,7 +567,7 @@ func (l *Lbank) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]ord
for x := 0; x < len(tempResp.Orders); x++ {
resp.Exchange = l.Name
resp.CurrencyPair = currency.NewPairFromString(tempResp.Orders[x].Symbol)
if strings.EqualFold(tempResp.Orders[x].Type, "buy") {
if strings.EqualFold(tempResp.Orders[x].Type, order.Buy.String()) {
resp.OrderSide = order.Buy
} else {
resp.OrderSide = order.Sell

View File

@@ -218,13 +218,17 @@ func (l *LocalBitcoins) UpdateOrderbook(p currency.Pair, assetType asset.Item) (
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount / data.Price, Price: data.Price})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x].Amount / orderbookNew.Bids[x].Price,
Price: orderbookNew.Bids[x].Price,
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount / data.Price, Price: data.Price})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x].Amount / orderbookNew.Asks[x].Price,
Price: orderbookNew.Asks[x].Price,
})
}
orderBook.Pair = p
@@ -261,8 +265,7 @@ func (l *LocalBitcoins) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (l *LocalBitcoins) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
@@ -434,7 +437,7 @@ func (l *LocalBitcoins) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest
for i := range resp {
orderDate, err := time.Parse(time.RFC3339, resp[i].Data.CreatedAt)
if err != nil {
log.Warnf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
log.Errorf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
l.Name,
"GetActiveOrders",
resp[i].Data.Advertisement.ID,
@@ -495,7 +498,7 @@ func (l *LocalBitcoins) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest
for i := range allTrades {
orderDate, err := time.Parse(time.RFC3339, allTrades[i].Data.CreatedAt)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
l.Name,
"GetActiveOrders",

View File

@@ -146,7 +146,7 @@ func TestGetAccountWalletInformationForCurrency(t *testing.T) {
func TestTransferAccountFunds(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.TransferAccountFundsRequest{
Amount: 10,
Amount: -10,
Currency: currency.BTC.String(),
From: 6,
To: 1,
@@ -159,7 +159,7 @@ func TestTransferAccountFunds(t *testing.T) {
func TestAccountWithdrawRequest(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.AccountWithdrawRequest{
Amount: 10,
Amount: -10,
Currency: currency.BTC.String(),
TradePwd: "1234",
Destination: 4,
@@ -285,13 +285,12 @@ func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) {
// TestPlaceSpotOrderLimit API endpoint test
func TestPlaceSpotOrderLimit(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Price: "100",
Size: "100",
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Price: "-100",
Size: "100",
}
_, err := o.PlaceSpotOrder(&request)
@@ -301,13 +300,12 @@ func TestPlaceSpotOrderLimit(t *testing.T) {
// TestPlaceSpotOrderMarket API endpoint test
func TestPlaceSpotOrderMarket(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "-100",
Notional: "100",
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Notional: "100",
}
_, err := o.PlaceSpotOrder(&request)
@@ -317,16 +315,15 @@ func TestPlaceSpotOrderMarket(t *testing.T) {
// TestPlaceMultipleSpotOrders API endpoint test
func TestPlaceMultipleSpotOrders(t *testing.T) {
TestSetRealOrderDefaults(t)
order := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Notional: "100",
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
@@ -339,16 +336,15 @@ func TestPlaceMultipleSpotOrders(t *testing.T) {
// TestPlaceMultipleSpotOrdersOverCurrencyLimits API logic test
func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Notional: "100",
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
order,
order,
@@ -365,27 +361,29 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
// TestPlaceMultipleSpotOrdersOverPairLimits API logic test
func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Notional: "100",
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
order.InstrumentID = currency.NewPairWithDelimiter(currency.LTC.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.DOGE.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.XMR.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.BCH.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
pairs := currency.Pairs{
currency.NewPair(currency.LTC, currency.USDT),
currency.NewPair(currency.ETH, currency.USDT),
currency.NewPair(currency.BCH, currency.USDT),
currency.NewPair(currency.XMR, currency.USDT),
}
for x := range pairs {
order.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, order)
}
_, errs := o.PlaceMultipleSpotOrders(request)
if errs[0].Error() != "up to 4 trading pairs" {
@@ -574,7 +572,7 @@ func TestGetMarginAccountSettingsForCurrency(t *testing.T) {
func TestOpenMarginLoan(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.OpenMarginLoanRequest{
Amount: 100,
Amount: -100,
InstrumentID: spotCurrency,
QuoteCurrency: currency.USD.String(),
}
@@ -587,7 +585,7 @@ func TestOpenMarginLoan(t *testing.T) {
func TestRepayMarginLoan(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.RepayMarginLoanRequest{
Amount: 100,
Amount: -100,
InstrumentID: spotCurrency,
QuoteCurrency: currency.USD.String(),
BorrowID: 1,
@@ -600,12 +598,12 @@ func TestRepayMarginLoan(t *testing.T) {
// TestPlaceMarginOrderLimit API endpoint test
func TestPlaceMarginOrderLimit(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.PlaceSpotOrderRequest{
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "2",
Price: "100",
Price: "-100",
Size: "100",
}
@@ -616,7 +614,7 @@ func TestPlaceMarginOrderLimit(t *testing.T) {
// TestPlaceMarginOrderMarket API endpoint test
func TestPlaceMarginOrderMarket(t *testing.T) {
TestSetRealOrderDefaults(t)
request := okgroup.PlaceSpotOrderRequest{
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
@@ -632,16 +630,16 @@ func TestPlaceMarginOrderMarket(t *testing.T) {
// TestPlaceMultipleMarginOrders API endpoint test
func TestPlaceMultipleMarginOrders(t *testing.T) {
TestSetRealOrderDefaults(t)
order := okgroup.PlaceSpotOrderRequest{
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Size: "-100",
Notional: "100",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
@@ -654,16 +652,16 @@ func TestPlaceMultipleMarginOrders(t *testing.T) {
// TestPlaceMultipleMarginOrdersOverCurrencyLimits API logic test
func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceSpotOrderRequest{
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Size: "-100",
Notional: "100",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
order,
order,
@@ -680,27 +678,30 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
// TestPlaceMultipleMarginOrdersOverPairLimits API logic test
func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceSpotOrderRequest{
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Size: "-100",
Notional: "100",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
order.InstrumentID = currency.NewPairWithDelimiter(currency.LTC.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.DOGE.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.XMR.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.BCH.String(), currency.USD.String(), "-").Lower().String()
request = append(request, order)
pairs := currency.Pairs{
currency.NewPair(currency.LTC, currency.USDT),
currency.NewPair(currency.ETH, currency.USDT),
currency.NewPair(currency.BCH, currency.USDT),
currency.NewPair(currency.XMR, currency.USDT),
}
for x := range pairs {
order.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, order)
}
_, errs := o.PlaceMultipleMarginOrders(request)
if errs[0].Error() != "up to 4 trading pairs" {
@@ -1049,7 +1050,7 @@ func TestSubmitOrder(t *testing.T) {
},
OrderSide: order.Buy,
OrderType: order.Limit,
Price: 1,
Price: -1,
Amount: 1,
ClientID: "meowOrder",
}

View File

@@ -3,6 +3,7 @@ package okex
import (
"fmt"
"net/http"
"strconv"
"strings"
"sync"
"testing"
@@ -154,7 +155,7 @@ func TestTransferAccountFunds(t *testing.T) {
Amount: 10,
Currency: currency.BTC.String(),
From: 6,
To: 1,
To: -1,
}
_, err := o.TransferAccountFunds(request)
@@ -166,7 +167,7 @@ func TestAccountWithdrawRequest(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
request := okgroup.AccountWithdrawRequest{
Amount: 10,
Amount: -1,
Currency: currency.BTC.String(),
TradePwd: "1234",
Destination: 4,
@@ -306,13 +307,12 @@ func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) {
func TestPlaceSpotOrderLimit(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
request := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Price: "100",
Size: "100",
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Price: "-1",
Size: "0.001",
}
_, err := o.PlaceSpotOrder(&request)
@@ -323,13 +323,12 @@ func TestPlaceSpotOrderLimit(t *testing.T) {
func TestPlaceSpotOrderMarket(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
request := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "-100",
Notional: "100",
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Notional: "100",
}
_, err := o.PlaceSpotOrder(&request)
@@ -340,16 +339,15 @@ func TestPlaceSpotOrderMarket(t *testing.T) {
func TestPlaceMultipleSpotOrders(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
order := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Notional: "100",
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
@@ -363,16 +361,15 @@ func TestPlaceMultipleSpotOrders(t *testing.T) {
func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
TestSetDefaults(t)
t.Parallel()
order := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Notional: "100",
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
order,
order,
@@ -390,27 +387,29 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
TestSetDefaults(t)
t.Parallel()
order := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Notional: "100",
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-1",
Price: "1",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
order.InstrumentID = currency.NewPairWithDelimiter(currency.LTC.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.DOGE.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.XMR.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.BCH.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
pairs := currency.Pairs{
currency.NewPair(currency.LTC, currency.USDT),
currency.NewPair(currency.ETH, currency.USDT),
currency.NewPair(currency.BCH, currency.USDT),
currency.NewPair(currency.XMR, currency.USDT),
}
for x := range pairs {
order.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, order)
}
_, errs := o.PlaceMultipleSpotOrders(request)
if errs[0].Error() != "up to 4 trading pairs" {
@@ -619,7 +618,7 @@ func TestOpenMarginLoan(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
request := okgroup.OpenMarginLoanRequest{
Amount: 100,
Amount: -100,
InstrumentID: spotCurrency,
QuoteCurrency: currency.USDT.String(),
}
@@ -633,7 +632,7 @@ func TestRepayMarginLoan(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
request := okgroup.RepayMarginLoanRequest{
Amount: 100,
Amount: -100,
InstrumentID: spotCurrency,
QuoteCurrency: currency.USDT.String(),
BorrowID: 1,
@@ -647,13 +646,13 @@ func TestRepayMarginLoan(t *testing.T) {
func TestPlaceMarginOrderLimit(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
request := okgroup.PlaceSpotOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "2",
Price: "100",
Size: "100",
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
OrderType: strconv.Itoa(okgroup.NormalOrder),
Price: "-100",
Size: "100",
}
_, err := o.PlaceMarginOrder(&request)
@@ -664,7 +663,7 @@ func TestPlaceMarginOrderLimit(t *testing.T) {
func TestPlaceMarginOrderMarket(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
request := okgroup.PlaceSpotOrderRequest{
request := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
@@ -681,16 +680,16 @@ func TestPlaceMarginOrderMarket(t *testing.T) {
func TestPlaceMultipleMarginOrders(t *testing.T) {
TestSetRealOrderDefaults(t)
t.Parallel()
order := okgroup.PlaceSpotOrderRequest{
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Size: "-100",
Notional: "100",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
@@ -704,16 +703,16 @@ func TestPlaceMultipleMarginOrders(t *testing.T) {
func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
TestSetDefaults(t)
t.Parallel()
order := okgroup.PlaceSpotOrderRequest{
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Size: "-100",
Notional: "100",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
order,
order,
@@ -731,27 +730,30 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
TestSetDefaults(t)
t.Parallel()
order := okgroup.PlaceSpotOrderRequest{
order := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
Size: "100",
Size: "-100",
Notional: "100",
}
request := []okgroup.PlaceSpotOrderRequest{
request := []okgroup.PlaceOrderRequest{
order,
}
order.InstrumentID = currency.NewPairWithDelimiter(currency.LTC.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.DOGE.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.XMR.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
order.InstrumentID = currency.NewPairWithDelimiter(currency.BCH.String(), currency.USDT.String(), "-").Lower().String()
request = append(request, order)
pairs := currency.Pairs{
currency.NewPair(currency.LTC, currency.USDT),
currency.NewPair(currency.ETH, currency.USDT),
currency.NewPair(currency.BCH, currency.USDT),
currency.NewPair(currency.XMR, currency.USDT),
}
for x := range pairs {
order.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, order)
}
_, errs := o.PlaceMultipleMarginOrders(request)
if errs[0].Error() != "up to 4 trading pairs" {
@@ -935,7 +937,7 @@ func TestPlaceFuturesOrder(t *testing.T) {
Leverage: 10,
Type: 1,
Size: 2,
Price: 432.11,
Price: -432.11,
ClientOid: "12233456",
})
testStandardErrorHandling(t, err)
@@ -951,7 +953,7 @@ func TestPlaceFuturesOrderBatch(t *testing.T) {
{
ClientOid: "1",
MatchPrice: "0",
Price: "100",
Price: "-100",
Size: "100",
Type: "1",
},
@@ -1212,13 +1214,13 @@ func TestPlaceMultipleSwapOrders(t *testing.T) {
ClientOID: "hello",
MatchPrice: "0",
Price: "10",
Size: "1",
Size: "-1",
Type: "1",
}, {
ClientOID: "hello2",
MatchPrice: "0",
Price: "10",
Size: "1",
Size: "-1",
Type: "1",
}},
})
@@ -1463,7 +1465,7 @@ func TestPlaceETTOrder(t *testing.T) {
QuoteCurrency: spotCurrency,
Type: 0,
Size: "100",
Amount: 1,
Amount: -1,
ETT: "OK06",
}

View File

@@ -47,22 +47,22 @@ main.go
```go
var o exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "OKex" {
y = bot.exchanges[i]
for i := range Bot.Exchanges {
if Bot.Exchanges[i].GetName() == "OKex" {
y = Bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := o.GetTickerPrice()
tick, err := o.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := o.GetOrderbookEx()
ob, err := o.FetchOrderbook()
if err != nil {
// Handle error
}

View File

@@ -103,7 +103,7 @@ type OKGroup struct {
// GetAccountCurrencies returns a list of tradable spot instruments and their properties
func (o *OKGroup) GetAccountCurrencies() (resp []GetAccountCurrenciesResponse, _ error) {
return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, okGroupGetAccountCurrencies, nil, &resp, false)
return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, okGroupGetAccountCurrencies, nil, &resp, true)
}
// GetAccountWalletInformation returns a list of wallets and their properties
@@ -173,7 +173,7 @@ func (o *OKGroup) GetAccountDepositHistory(currency string) (resp []GetAccountDe
if currency != "" {
requestURL = fmt.Sprintf("%v/%v", OKGroupGetAccountDepositHistory, currency)
} else {
requestURL = okGroupGetWithdrawalHistory
requestURL = OKGroupGetAccountDepositHistory
}
return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
}
@@ -198,17 +198,23 @@ func (o *OKGroup) GetSpotBillDetailsForCurrency(request GetSpotBillDetailsForCur
// PlaceSpotOrder token trading only supports limit and market orders (more order types will become available in the future).
// You can place an order only if you have enough funds.
// Once your order is placed, the amount will be put on hold.
func (o *OKGroup) PlaceSpotOrder(request *PlaceSpotOrderRequest) (resp PlaceSpotOrderResponse, _ error) {
func (o *OKGroup) PlaceSpotOrder(request *PlaceOrderRequest) (resp PlaceOrderResponse, _ error) {
if request.OrderType == "" {
request.OrderType = strconv.Itoa(NormalOrder)
}
return resp, o.SendHTTPRequest(http.MethodPost, okGroupTokenSubsection, OKGroupOrders, request, &resp, true)
}
// PlaceMultipleSpotOrders supports placing multiple orders for specific trading pairs
// up to 4 trading pairs, maximum 4 orders for each pair
func (o *OKGroup) PlaceMultipleSpotOrders(request []PlaceSpotOrderRequest) (map[string][]PlaceSpotOrderResponse, []error) {
func (o *OKGroup) PlaceMultipleSpotOrders(request []PlaceOrderRequest) (map[string][]PlaceOrderResponse, []error) {
currencyPairOrders := make(map[string]int)
resp := make(map[string][]PlaceSpotOrderResponse)
resp := make(map[string][]PlaceOrderResponse)
for i := range request {
if request[i].OrderType == "" {
request[i].OrderType = strconv.Itoa(NormalOrder)
}
currencyPairOrders[request[i].InstrumentID]++
}
@@ -422,14 +428,14 @@ func (o *OKGroup) RepayMarginLoan(request RepayMarginLoanRequest) (resp RepayMar
// PlaceMarginOrder OKEx API only supports limit and market orders (more orders will become available in the future).
// You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold.
func (o *OKGroup) PlaceMarginOrder(request *PlaceSpotOrderRequest) (resp PlaceSpotOrderResponse, _ error) {
func (o *OKGroup) PlaceMarginOrder(request *PlaceOrderRequest) (resp PlaceOrderResponse, _ error) {
return resp, o.SendHTTPRequest(http.MethodPost, okGroupMarginTradingSubsection, OKGroupOrders, request, &resp, true)
}
// PlaceMultipleMarginOrders Place multiple orders for specific trading pairs (up to 4 trading pairs, maximum 4 orders each)
func (o *OKGroup) PlaceMultipleMarginOrders(request []PlaceSpotOrderRequest) (map[string][]PlaceSpotOrderResponse, []error) {
func (o *OKGroup) PlaceMultipleMarginOrders(request []PlaceOrderRequest) (map[string][]PlaceOrderResponse, []error) {
currencyPairOrders := make(map[string]int)
resp := make(map[string][]PlaceSpotOrderResponse)
resp := make(map[string][]PlaceOrderResponse)
for i := range request {
currencyPairOrders[request[i].InstrumentID]++
}
@@ -556,10 +562,7 @@ func (o *OKGroup) SendHTTPRequest(httpMethod, requestType, requestPath string, d
o.Name)
}
utcTime := time.Now().UTC()
iso := utcTime.String()
isoBytes := []byte(iso)
iso = string(isoBytes[:10]) + "T" + string(isoBytes[11:23]) + "Z"
utcTime := time.Now().UTC().Format(time.RFC3339)
payload := []byte("")
if data != nil {
@@ -584,11 +587,11 @@ func (o *OKGroup) SendHTTPRequest(httpMethod, requestType, requestPath string, d
signPath := fmt.Sprintf("/%v%v%v%v", OKGroupAPIPath,
requestType, o.APIVersion, requestPath)
hmac := crypto.GetHMAC(crypto.HashSHA256,
[]byte(iso+httpMethod+signPath+string(payload)),
[]byte(utcTime+httpMethod+signPath+string(payload)),
[]byte(o.API.Credentials.Secret))
headers["OK-ACCESS-KEY"] = o.API.Credentials.Key
headers["OK-ACCESS-SIGN"] = crypto.Base64Encode(hmac)
headers["OK-ACCESS-TIMESTAMP"] = iso
headers["OK-ACCESS-TIMESTAMP"] = utcTime
headers["OK-ACCESS-PASSPHRASE"] = o.API.Credentials.ClientID
}

View File

@@ -6,13 +6,21 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency"
)
// Order types
const (
NormalOrder = iota
PostOnlyOrder
FillOrKillOrder
ImmediateOrCancelOrder
)
// GetAccountCurrenciesResponse response data for GetAccountCurrencies
type GetAccountCurrenciesResponse struct {
CanDeposit int64 `json:"can_deposit"`
CanWithdraw int64 `json:"can_withdraw"`
Currency string `json:"currency"`
MinWithdrawal float64 `json:"min_withdrawal"`
Name string `json:"name"`
Currency string `json:"currency"`
CanDeposit int `json:"can_deposit,string"`
CanWithdraw int `json:"can_withdraw,string"`
MinWithdrawal float64 `json:"min_withdrawal,string"`
}
// WalletInformationResponse response data for WalletInformation
@@ -64,22 +72,22 @@ type AccountWithdrawResponse struct {
// GetAccountWithdrawalFeeResponse response data for GetAccountWithdrawalFee
type GetAccountWithdrawalFeeResponse struct {
Currency string `json:"currency"`
MinFee float64 `json:"min_fee"`
MaxFee float64 `json:"max_fee"`
MinFee float64 `json:"min_fee,string"`
MaxFee float64 `json:"max_fee,string"`
}
// WithdrawalHistoryResponse response data for WithdrawalHistoryResponse
type WithdrawalHistoryResponse struct {
Amount float64 `json:"amount"`
Currency string `json:"currency"`
Fee string `json:"fee"`
From string `json:"from"`
Status int64 `json:"status"`
Timestamp time.Time `json:"timestamp"`
To string `json:"to"`
Txid string `json:"txid"`
PaymentID string `json:"payment_id"`
Tag string `json:"tag"`
Amount float64 `json:"amount,string"`
Currency string `json:"currency"`
Fee string `json:"fee"`
From string `json:"from"`
Status int64 `json:"status,string"`
Timestamp time.Time `json:"timestamp"`
To string `json:"to"`
TransactionID string `json:"txid"`
PaymentID string `json:"payment_id"`
Tag string `json:"tag"`
}
// GetAccountBillDetailsRequest request data for GetAccountBillDetailsRequest
@@ -112,11 +120,12 @@ type GetDepositAddressResponse struct {
// GetAccountDepositHistoryResponse response data for GetAccountDepositHistory
type GetAccountDepositHistoryResponse struct {
Amount float64 `json:"amount"`
Amount float64 `json:"amount,string"`
Currency string `json:"currency"`
Status int64 `json:"status"`
Timestamp time.Time `json:"timestamp"`
From string `json:"from"`
To string `json:"to"`
Timestamp time.Time `json:"timestamp"`
Status int64 `json:"status,string"`
TransactionID string `json:"txid"`
}
@@ -156,20 +165,21 @@ type SpotBillDetails struct {
InstrumentID string `json:"instrument_id"`
}
// PlaceSpotOrderRequest request data for PlaceSpotOrder
type PlaceSpotOrderRequest struct {
// PlaceOrderRequest request data for placing an order
type PlaceOrderRequest struct {
ClientOID string `json:"client_oid,omitempty"` // the order ID customized by yourself
Type string `json:"type"` // limit / market(default: limit)
Side string `json:"side"` // buy or sell
InstrumentID string `json:"instrument_id"` // trading pair
MarginTrading string `json:"margin_trading"` // order type (The request value is 1)
MarginTrading string `json:"margin_trading"` // margin trading
OrderType string `json:"order_type"` // order type (0: Normal order (Unfilled and 0 imply normal limit order) 1: Post only 2: Fill or Kill 3: Immediate Or Cancel
Size string `json:"size"`
Notional string `json:"notional,omitempty"` //
Price string `json:"price,omitempty"` // price (Limit order only)
}
// PlaceSpotOrderResponse response data for PlaceSpotOrder
type PlaceSpotOrderResponse struct {
// PlaceOrderResponse response data for PlaceSpotOrder
type PlaceOrderResponse struct {
ClientOid string `json:"client_oid"`
OrderID string `json:"order_id"`
Result bool `json:"result"`
@@ -1497,7 +1507,7 @@ type WebsocketSpotOrderResponse struct {
Notional float64 `json:"notional,string"`
Size float64 `json:"size,string"`
Status string `json:"status"`
MarginTrading int64 `json:"margin_trading"`
MarginTrading int64 `json:"margin_trading,omitempty"`
Type string `json:"type"`
// Price A member, but part already exists as part of WebsocketDataResponse
// InstrumentID A member, but part already exists as part of WebsocketDataResponse

View File

@@ -267,19 +267,21 @@ func (o *OKGroup) WsHandleData(wg *sync.WaitGroup) {
// WsLogin sends a login request to websocket to enable access to authenticated endpoints
func (o *OKGroup) WsLogin() error {
o.Websocket.SetCanUseAuthenticatedEndpoints(true)
utcTime := time.Now().UTC()
unixTime := utcTime.Unix()
unixTime := time.Now().UTC().Unix()
signPath := "/users/self/verify"
hmac := crypto.GetHMAC(crypto.HashSHA256,
[]byte(fmt.Sprintf("%v", unixTime)+http.MethodGet+signPath),
[]byte(o.API.Credentials.Secret))
[]byte(strconv.FormatInt(unixTime, 10)+http.MethodGet+signPath),
[]byte(o.API.Credentials.Secret),
)
base64 := crypto.Base64Encode(hmac)
request := WebsocketEventRequest{
Operation: "login",
Arguments: []string{o.API.Credentials.Key,
Arguments: []string{
o.API.Credentials.Key,
o.API.Credentials.ClientID,
fmt.Sprintf("%v", unixTime),
base64},
strconv.FormatInt(unixTime, 10),
base64,
},
}
err := o.WebsocketConn.SendMessage(request)
if err != nil {
@@ -470,7 +472,7 @@ func (o *OKGroup) wsProcessCandles(response *WebsocketDataResponse) {
timeData, err := time.Parse(time.RFC3339Nano,
response.Data[i].WebsocketCandleResponse.Candle[0])
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"%v Time data could not be parsed: %v",
o.Name,
response.Data[i].Candle[0])

View File

@@ -236,7 +236,7 @@ func (o *OKGroup) GetFundingHistory() (resp []exchange.FundHistory, err error) {
ExchangeName: o.Name,
Status: OrderStatus[accountWithdrawlHistory[i].Status],
Timestamp: accountWithdrawlHistory[i].Timestamp,
TransferID: accountWithdrawlHistory[i].Txid,
TransferID: accountWithdrawlHistory[i].TransactionID,
TransferType: "withdrawal",
})
}
@@ -255,11 +255,11 @@ func (o *OKGroup) SubmitOrder(s *order.Submit) (resp order.SubmitResponse, err e
return resp, err
}
request := PlaceSpotOrderRequest{
request := PlaceOrderRequest{
ClientOID: s.ClientID,
InstrumentID: o.FormatExchangeCurrency(s.Pair, asset.Spot).String(),
Side: strings.ToLower(s.OrderSide.String()),
Type: strings.ToLower(s.OrderType.String()),
Side: s.OrderSide.Lower(),
Type: s.OrderType.Lower(),
Size: strconv.FormatFloat(s.Amount, 'f', -1, 64),
}
if s.OrderType == order.Limit {

View File

@@ -129,25 +129,27 @@ func (p *Poloniex) GetOrderbook(currencyPair string, depth int) (OrderbookAll, e
return oba, err
}
for currency, orderbook := range resp.Data {
ob := Orderbook{}
var ob Orderbook
for x := range orderbook.Asks {
data := orderbook.Asks[x]
price, err := strconv.ParseFloat(data[0].(string), 64)
price, err := strconv.ParseFloat(orderbook.Asks[x][0].(string), 64)
if err != nil {
return oba, err
}
amount := data[1].(float64)
ob.Asks = append(ob.Asks, OrderbookItem{Price: price, Amount: amount})
ob.Asks = append(ob.Asks, OrderbookItem{
Price: price,
Amount: orderbook.Asks[x][1].(float64),
})
}
for x := range orderbook.Bids {
data := orderbook.Bids[x]
price, err := strconv.ParseFloat(data[0].(string), 64)
price, err := strconv.ParseFloat(orderbook.Bids[x][0].(string), 64)
if err != nil {
return oba, err
}
amount := data[1].(float64)
ob.Bids = append(ob.Bids, OrderbookItem{Price: price, Amount: amount})
ob.Asks = append(ob.Asks, OrderbookItem{
Price: price,
Amount: orderbook.Bids[x][1].(float64),
})
}
oba.Data[currency] = Orderbook{Bids: ob.Bids, Asks: ob.Asks}
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook"
@@ -186,11 +187,11 @@ func (p *Poloniex) WsHandleData() {
trade.Symbol = currencyIDMap[chanID]
trade.TradeID, _ = strconv.ParseInt(dataL3[1].(string), 10, 64)
// 1 for buy 0 for sell
side := "buy"
side := order.Buy
if dataL3[2].(float64) != 1 {
side = "sell"
side = order.Sell
}
trade.Side = side
trade.Side = side.Lower()
trade.Volume, err = strconv.ParseFloat(dataL3[3].(string), 64)
if err != nil {
p.Websocket.DataHandler <- err

View File

@@ -296,21 +296,18 @@ func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.I
var obItems []orderbook.Item
for y := range data.Bids {
obData := data.Bids[y]
obItems = append(obItems,
orderbook.Item{Amount: obData.Amount, Price: obData.Price})
obItems = append(obItems, orderbook.Item{
Amount: data.Bids[y].Amount, Price: data.Bids[y].Price})
}
orderBook.Bids = obItems
obItems = []orderbook.Item{}
for y := range data.Asks {
obData := data.Asks[y]
obItems = append(obItems,
orderbook.Item{Amount: obData.Amount, Price: obData.Price})
obItems = append(obItems, orderbook.Item{
Amount: data.Asks[y].Amount, Price: data.Asks[y].Price})
}
orderBook.Pair = x
orderBook.Asks = obItems
orderBook.Pair = x
orderBook.ExchangeName = p.Name
orderBook.AssetType = assetType
@@ -350,8 +347,7 @@ func (p *Poloniex) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (p *Poloniex) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
@@ -507,7 +503,7 @@ func (p *Poloniex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail,
orderSide := order.Side(strings.ToUpper(resp.Data[key][i].Type))
orderDate, err := time.Parse(poloniexDateLayout, resp.Data[key][i].Date)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
p.Name,
"GetActiveOrders",
@@ -554,7 +550,7 @@ func (p *Poloniex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
orderDate, err := time.Parse(poloniexDateLayout,
resp.Data[key][i].Date)
if err != nil {
log.Warnf(log.ExchangeSys,
log.Errorf(log.ExchangeSys,
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
p.Name,
"GetActiveOrders",

View File

@@ -25,6 +25,7 @@ var Exchanges = []string{
"btc markets",
"btse",
"coinbasepro",
"coinbene",
"coinut",
"exmo",
"gateio",

View File

@@ -299,8 +299,7 @@ func (y *Yobit) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (y *Yobit) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -268,23 +268,24 @@ func (z *ZB) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Ba
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (z *ZB) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
currency := z.FormatExchangeCurrency(p, assetType).String()
orderbookNew, err := z.GetOrderbook(currency)
orderbookNew, err := z.GetOrderbook(z.FormatExchangeCurrency(p,
assetType).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[1], Price: data[0]})
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x][1],
Price: orderbookNew.Bids[x][0],
})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks,
orderbook.Item{Amount: data[1], Price: data[0]})
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderbookNew.Asks[x][1],
Price: orderbookNew.Asks[x][0],
})
}
orderBook.Pair = p
@@ -338,8 +339,7 @@ func (z *ZB) GetAccountInfo() (exchange.AccountInfo, error) {
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (z *ZB) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.

View File

@@ -9,6 +9,8 @@ import (
proto "github.com/golang/protobuf/proto"
_ "google.golang.org/genproto/googleapis/api/annotations"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
@@ -5188,316 +5190,316 @@ func init() {
proto.RegisterType((*GetExchangeTickerStreamRequest)(nil), "gctrpc.GetExchangeTickerStreamRequest")
proto.RegisterType((*GetAuditEventRequest)(nil), "gctrpc.GetAuditEventRequest")
proto.RegisterType((*GetAuditEventResponse)(nil), "gctrpc.GetAuditEventResponse")
proto.RegisterType((*AuditEvent)(nil), "gctrpc.audit_event")
proto.RegisterType((*AuditEvent)(nil), "gctrpc.AuditEvent")
}
func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) }
var fileDescriptor_77a6da22d6a3feb1 = []byte{
// 4844 bytes of a gzipped FileDescriptorProto
// 4838 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x3c, 0x4d, 0x8f, 0x24, 0x47,
0x56, 0xca, 0xea, 0x9e, 0xee, 0xae, 0x57, 0xd5, 0x5f, 0xd1, 0x5f, 0x35, 0xd5, 0xdd, 0xf3, 0x91,
0x5e, 0x8f, 0x67, 0xfc, 0xd1, 0x63, 0x8f, 0x07, 0xd6, 0xac, 0xcd, 0x2e, 0xed, 0x1e, 0xbb, 0xd7,
0xd8, 0xeb, 0x69, 0xb2, 0x67, 0xc7, 0x92, 0x17, 0xb9, 0xc8, 0xae, 0x8c, 0xea, 0x4e, 0xa6, 0x2a,
0x33, 0x9d, 0x19, 0xd5, 0x3d, 0x65, 0x40, 0xac, 0x2c, 0x81, 0x38, 0x20, 0x38, 0xac, 0x90, 0x40,
0xe2, 0xc4, 0x11, 0x89, 0x0b, 0xe2, 0xc4, 0x61, 0xc5, 0x15, 0x71, 0xe4, 0xc2, 0x0f, 0x40, 0xdc,
0x00, 0x69, 0x25, 0x2e, 0x9c, 0x50, 0xbc, 0xf8, 0xc8, 0x88, 0xcc, 0xac, 0xea, 0xea, 0xdd, 0x59,
0x73, 0xb1, 0x2b, 0x5f, 0xbc, 0x78, 0xef, 0xc5, 0x8b, 0x17, 0x2f, 0xde, 0x7b, 0xf1, 0x7a, 0xa0,
0x9e, 0x26, 0xdd, 0xbd, 0x24, 0x8d, 0x59, 0x4c, 0xe6, 0x4e, 0xbb, 0x2c, 0x4d, 0xba, 0xed, 0x9d,
0xd3, 0x38, 0x3e, 0xed, 0xd3, 0xfb, 0x7e, 0x12, 0xde, 0xf7, 0xa3, 0x28, 0x66, 0x3e, 0x0b, 0xe3,
0x28, 0x13, 0x58, 0xee, 0x0a, 0x2c, 0x1d, 0x52, 0xf6, 0x51, 0xd4, 0x8b, 0x3d, 0xfa, 0xe5, 0x90,
0x66, 0xcc, 0xfd, 0x87, 0x59, 0x58, 0xd6, 0xa0, 0x2c, 0x89, 0xa3, 0x8c, 0x92, 0x4d, 0x98, 0x1b,
0x26, 0x2c, 0x1c, 0xd0, 0x96, 0x73, 0xcb, 0xb9, 0x5b, 0xf7, 0xe4, 0x17, 0xb9, 0x0f, 0x6b, 0xfe,
0xb9, 0x1f, 0xf6, 0xfd, 0x93, 0x3e, 0xed, 0xd0, 0xe7, 0xdd, 0x33, 0x3f, 0x3a, 0xa5, 0x59, 0xab,
0x76, 0xcb, 0xb9, 0x3b, 0xe3, 0x11, 0x3d, 0xf4, 0x81, 0x1a, 0x21, 0xaf, 0xc1, 0x2a, 0x8d, 0x38,
0x28, 0x30, 0xd0, 0x67, 0x10, 0x7d, 0x45, 0x0e, 0xe4, 0xc8, 0x0f, 0x61, 0x33, 0xa0, 0x3d, 0x7f,
0xd8, 0x67, 0x9d, 0x5e, 0x9c, 0xd2, 0xe7, 0x9d, 0x24, 0x8d, 0xcf, 0xc3, 0x80, 0xa6, 0xad, 0x59,
0x94, 0x62, 0x5d, 0x8e, 0x7e, 0xc8, 0x07, 0x8f, 0xe4, 0x18, 0x79, 0x00, 0x1b, 0x7a, 0x56, 0xe8,
0xb3, 0x4e, 0x77, 0x98, 0xa6, 0x34, 0xea, 0x8e, 0x5a, 0xd7, 0x70, 0xd2, 0x9a, 0x9a, 0x14, 0xfa,
0xec, 0x40, 0x0e, 0x91, 0xcf, 0x60, 0x25, 0x1b, 0x9e, 0x64, 0xa3, 0x8c, 0xd1, 0x41, 0x27, 0x63,
0x3e, 0x1b, 0x66, 0xad, 0xb9, 0x5b, 0x33, 0x77, 0x1b, 0x0f, 0x5e, 0xdf, 0x13, 0x6a, 0xdc, 0x2b,
0xa8, 0x64, 0xef, 0x58, 0xe1, 0x1f, 0x23, 0xfa, 0x07, 0x11, 0x4b, 0x47, 0xde, 0x72, 0x66, 0x43,
0xc9, 0xa7, 0xb0, 0x98, 0x26, 0xdd, 0x0e, 0x8d, 0x82, 0x24, 0x0e, 0x23, 0x96, 0xb5, 0xe6, 0x91,
0xea, 0xbd, 0x71, 0x54, 0xbd, 0xa4, 0xfb, 0x81, 0xc2, 0x15, 0x24, 0x9b, 0xa9, 0x01, 0x6a, 0xbf,
0x0f, 0xeb, 0x55, 0x8c, 0xc9, 0x0a, 0xcc, 0x3c, 0xa3, 0x23, 0xb9, 0x3b, 0xfc, 0x27, 0x59, 0x87,
0x6b, 0xe7, 0x7e, 0x7f, 0x48, 0x71, 0x33, 0x16, 0x3c, 0xf1, 0xf1, 0x9d, 0xda, 0x3b, 0x4e, 0xfb,
0x09, 0xac, 0x96, 0xd8, 0x54, 0x10, 0xb8, 0x67, 0x12, 0x68, 0x3c, 0x58, 0x53, 0x22, 0x7b, 0x47,
0x07, 0x6a, 0xae, 0x41, 0xd5, 0xbd, 0x0d, 0x37, 0x0f, 0x29, 0x3b, 0x88, 0x07, 0x83, 0x61, 0x14,
0x76, 0xd1, 0xc6, 0x3c, 0xda, 0xf7, 0x47, 0x34, 0xcd, 0x94, 0x65, 0x7d, 0x0a, 0xeb, 0x55, 0xe3,
0xa4, 0x05, 0xf3, 0x72, 0xef, 0x91, 0xff, 0x82, 0xa7, 0x3e, 0xc9, 0x0e, 0xd4, 0xbb, 0x71, 0x14,
0xd1, 0x2e, 0xa3, 0x81, 0x5c, 0x48, 0x0e, 0x70, 0xff, 0xb8, 0x06, 0xb7, 0xc6, 0xf3, 0x94, 0xa6,
0xfb, 0x15, 0x6c, 0x76, 0x4d, 0x84, 0x4e, 0x2a, 0x31, 0x5a, 0x0e, 0x6e, 0xc5, 0x81, 0xb1, 0x15,
0x13, 0x29, 0xed, 0x55, 0x8e, 0x8a, 0x4d, 0xda, 0xe8, 0x56, 0x8d, 0xb5, 0x7b, 0xd0, 0x1e, 0x3f,
0xa9, 0x42, 0xe5, 0x0f, 0x6c, 0x95, 0xef, 0x28, 0xd1, 0xaa, 0x88, 0x98, 0xba, 0xff, 0x36, 0x6c,
0x1d, 0xd2, 0x88, 0xa6, 0x61, 0x57, 0x1b, 0x87, 0xd4, 0x39, 0xd7, 0xa0, 0xb6, 0x49, 0xc9, 0x2a,
0x07, 0xb8, 0x6d, 0x68, 0x95, 0x27, 0x8a, 0xe5, 0xba, 0x9b, 0xb0, 0x7e, 0x48, 0x99, 0x86, 0xeb,
0x5d, 0xfc, 0xa9, 0x03, 0x1b, 0x38, 0x90, 0x9d, 0x64, 0x23, 0x31, 0x20, 0x55, 0xfd, 0x3b, 0xb0,
0xaa, 0x49, 0x67, 0xea, 0x18, 0x09, 0x2d, 0xbf, 0x6d, 0x68, 0xb9, 0x3c, 0x33, 0x3f, 0x4c, 0x99,
0x79, 0x9a, 0xf2, 0x33, 0x29, 0xc1, 0xed, 0x03, 0xd8, 0xa8, 0x44, 0xbd, 0x8a, 0xfd, 0xbb, 0x2d,
0xd8, 0x3c, 0xa4, 0xcc, 0x30, 0x63, 0xc3, 0x40, 0x1b, 0x06, 0x98, 0xdb, 0x65, 0xc6, 0xfc, 0x94,
0xe5, 0x76, 0x29, 0x3f, 0xc9, 0xcb, 0xb0, 0xd4, 0x0f, 0x33, 0x46, 0xa3, 0x8e, 0x1f, 0x04, 0x29,
0xcd, 0x84, 0xcb, 0xab, 0x7b, 0x8b, 0x02, 0xba, 0x2f, 0x80, 0xee, 0x3f, 0x3a, 0x7c, 0x63, 0x0a,
0xac, 0xa4, 0xb2, 0x3e, 0x81, 0x7a, 0xee, 0x15, 0x84, 0x92, 0xf6, 0x0c, 0x25, 0x55, 0xcd, 0xd9,
0x2b, 0xb8, 0x86, 0x9c, 0x40, 0xfb, 0xb7, 0x60, 0xe9, 0x45, 0x1f, 0xe8, 0x77, 0xa0, 0x2d, 0x6d,
0x43, 0x79, 0xe4, 0x4f, 0xfd, 0x01, 0x55, 0x76, 0xd5, 0x86, 0x05, 0xe5, 0xc0, 0x25, 0x0f, 0xfd,
0xed, 0xee, 0xc2, 0x76, 0xe5, 0x4c, 0x69, 0x58, 0xf7, 0x61, 0xed, 0x90, 0x32, 0xed, 0xe6, 0x15,
0xc5, 0xb1, 0x5e, 0xc0, 0x7d, 0x88, 0x96, 0x68, 0x4c, 0x90, 0x2a, 0xdc, 0x81, 0x7a, 0x7e, 0x89,
0x48, 0xdb, 0xd6, 0x00, 0xf7, 0x01, 0x9a, 0xa9, 0x9a, 0xf5, 0xf8, 0xc9, 0x91, 0x47, 0xc5, 0xb4,
0xeb, 0xb0, 0x10, 0xb3, 0xa4, 0xd3, 0x8d, 0x03, 0x25, 0xfa, 0x7c, 0xcc, 0x92, 0x83, 0x38, 0xa0,
0xd2, 0x34, 0x8c, 0x39, 0xda, 0x34, 0xfe, 0x46, 0x6c, 0xa5, 0x3d, 0x24, 0xe5, 0xf8, 0x4d, 0xa8,
0x2b, 0x82, 0x6a, 0x2b, 0xdf, 0x30, 0xb6, 0xb2, 0x6a, 0xce, 0xde, 0x63, 0xc1, 0x51, 0xee, 0xe4,
0x82, 0x14, 0x20, 0x6b, 0xbf, 0x0b, 0x8b, 0xd6, 0xd0, 0x65, 0x96, 0x5d, 0x37, 0xb7, 0xec, 0x21,
0x6c, 0x3e, 0x0a, 0x33, 0xf3, 0xc6, 0x9d, 0x66, 0xbb, 0xbe, 0x80, 0xa5, 0x23, 0x3f, 0x4c, 0xb3,
0xe3, 0x61, 0x92, 0xc4, 0x68, 0xde, 0xaf, 0xc0, 0x72, 0x7e, 0xad, 0x27, 0x7c, 0x4c, 0x4e, 0x5a,
0xd2, 0x60, 0x9c, 0x41, 0x5e, 0x82, 0x45, 0x75, 0x9d, 0x0b, 0x34, 0x21, 0x52, 0x53, 0x02, 0x11,
0xc9, 0xfd, 0x7a, 0xd6, 0x52, 0x9d, 0x15, 0x58, 0x10, 0x98, 0x8d, 0x7c, 0x1d, 0x56, 0xe0, 0x6f,
0xd3, 0x10, 0x6a, 0xf6, 0x75, 0xd0, 0x82, 0xf9, 0x73, 0x9a, 0x9e, 0xc4, 0x19, 0xc5, 0x98, 0x61,
0xc1, 0x53, 0x9f, 0x5c, 0x90, 0x61, 0x16, 0x46, 0xa7, 0x9d, 0xcc, 0x8f, 0x82, 0x93, 0xf8, 0x39,
0x46, 0x08, 0x0b, 0x5e, 0x13, 0x81, 0xc7, 0x02, 0x46, 0x6e, 0x43, 0xf3, 0x8c, 0xb1, 0xa4, 0xc3,
0x43, 0x97, 0x78, 0xc8, 0x64, 0x40, 0xd0, 0xe0, 0xb0, 0x27, 0x02, 0xc4, 0x0f, 0x36, 0xa2, 0x0c,
0x33, 0x9a, 0xfa, 0xa7, 0x34, 0x62, 0xad, 0x39, 0x71, 0xb0, 0x39, 0xf4, 0x87, 0x0a, 0x48, 0x76,
0x01, 0x10, 0x2d, 0x49, 0xe3, 0xe7, 0xa3, 0xd6, 0xbc, 0x30, 0x3d, 0x0e, 0x39, 0xe2, 0x00, 0xae,
0xbf, 0x13, 0x3f, 0xa3, 0x2a, 0xf4, 0x08, 0x69, 0xd6, 0x5a, 0x10, 0xfa, 0xe3, 0xe0, 0x03, 0x0d,
0x25, 0x1d, 0x1e, 0x77, 0x48, 0xad, 0x77, 0xfc, 0x2c, 0xa3, 0x2c, 0x6b, 0xd5, 0xd1, 0x80, 0x1e,
0x56, 0x18, 0x50, 0x21, 0xfe, 0x90, 0xf3, 0xf6, 0x71, 0x9a, 0x8e, 0x3f, 0x2c, 0x28, 0x8f, 0xb7,
0xfc, 0x21, 0x3b, 0xa3, 0x11, 0xe3, 0xb7, 0x07, 0x67, 0x92, 0x84, 0x2d, 0x40, 0xdd, 0xac, 0x58,
0x03, 0xfb, 0x49, 0xd8, 0xfe, 0x9c, 0x07, 0x17, 0x65, 0xaa, 0x15, 0x26, 0xf8, 0xba, 0xed, 0x4a,
0x36, 0x95, 0xb0, 0xb6, 0x1d, 0x99, 0xa6, 0x79, 0x01, 0x2b, 0x87, 0x94, 0x3d, 0x09, 0xbb, 0xcf,
0x68, 0x3a, 0x85, 0x51, 0x92, 0xbb, 0x30, 0xcb, 0x2d, 0x4a, 0x32, 0x58, 0xd7, 0x37, 0xa1, 0x8c,
0xd8, 0x38, 0x23, 0x0f, 0x31, 0xf8, 0x5e, 0xa0, 0xe6, 0x3a, 0x6c, 0x94, 0x08, 0xbb, 0xa8, 0x7b,
0x75, 0x84, 0x3c, 0x19, 0x25, 0xd4, 0x7d, 0x0a, 0x4d, 0x73, 0x12, 0x77, 0x1a, 0x01, 0xed, 0x87,
0x83, 0x90, 0xd1, 0x54, 0x39, 0x0d, 0x0d, 0xe0, 0xf6, 0xc8, 0xb7, 0x48, 0xda, 0x31, 0xfe, 0xe6,
0xe7, 0xed, 0xcb, 0x61, 0xcc, 0x14, 0x6d, 0xf1, 0xe1, 0xfe, 0x45, 0x0d, 0x96, 0xd4, 0x72, 0xa4,
0x31, 0x2b, 0x99, 0x9d, 0x4b, 0x65, 0xbe, 0x0d, 0xcd, 0xbe, 0x9f, 0xb1, 0xce, 0x30, 0x09, 0x7c,
0x15, 0xda, 0xcc, 0x78, 0x0d, 0x0e, 0xfb, 0xa1, 0x00, 0x71, 0x8b, 0x56, 0x91, 0x2b, 0x9e, 0x2d,
0xc9, 0xbd, 0xd9, 0x35, 0x17, 0x43, 0x60, 0x96, 0xcf, 0x41, 0x6b, 0x77, 0x3c, 0xfc, 0xcd, 0x61,
0x67, 0xe1, 0xe9, 0x19, 0x5a, 0xb7, 0xe3, 0xe1, 0x6f, 0xbe, 0x83, 0xfd, 0xf8, 0x02, 0x6d, 0xd9,
0xf1, 0xf8, 0x4f, 0x0e, 0x39, 0x09, 0x03, 0x34, 0x5d, 0xc7, 0xe3, 0x3f, 0x39, 0xc4, 0xcf, 0x9e,
0xa1, 0xa1, 0x3a, 0x1e, 0xff, 0xc9, 0xa3, 0xfe, 0xf3, 0xb8, 0x3f, 0x1c, 0xd0, 0x56, 0x1d, 0x81,
0xf2, 0x8b, 0x6c, 0x43, 0x3d, 0x49, 0xc3, 0x2e, 0xed, 0xf8, 0xec, 0x0c, 0x8d, 0xc9, 0xf1, 0x16,
0x10, 0xb0, 0xcf, 0xce, 0xdc, 0x35, 0x58, 0xd5, 0x1b, 0xad, 0xbd, 0xe7, 0x67, 0x30, 0x2f, 0x21,
0x13, 0x37, 0xfd, 0x4d, 0x98, 0x67, 0x02, 0xad, 0x55, 0xc3, 0x53, 0xa0, 0x0d, 0xcb, 0xd6, 0xb4,
0xa7, 0xd0, 0xdc, 0xef, 0x01, 0x31, 0xb9, 0xc9, 0x8d, 0xb8, 0x97, 0xd3, 0x11, 0xee, 0x78, 0xd9,
0xa6, 0x93, 0xe5, 0x04, 0xbe, 0xc2, 0xcb, 0xe8, 0x71, 0x1a, 0x70, 0x47, 0x12, 0x3f, 0xfb, 0x46,
0x4d, 0xf3, 0x07, 0xb0, 0xa8, 0x19, 0x7f, 0xc4, 0xe8, 0x80, 0x2b, 0xdc, 0x1f, 0xc4, 0xc3, 0x88,
0x56, 0xca, 0xea, 0xcf, 0x7a, 0x5d, 0xfd, 0x15, 0xfd, 0x55, 0x53, 0xdd, 0x3d, 0x3d, 0x93, 0x5e,
0x8f, 0x67, 0x66, 0xbd, 0x3d, 0xf6, 0x78, 0x60, 0xcd, 0xda, 0xec, 0xd2, 0x6e, 0xdb, 0xbd, 0xc6,
0x5e, 0x4f, 0x93, 0x3d, 0x3b, 0x96, 0xbc, 0xc8, 0x45, 0x76, 0x65, 0x54, 0x77, 0x32, 0x55, 0x99,
0xe9, 0xcc, 0xa8, 0xee, 0x29, 0x03, 0x62, 0x65, 0x09, 0xc4, 0x01, 0xc1, 0x61, 0x85, 0x04, 0x12,
0x27, 0x8e, 0x48, 0x5c, 0x10, 0x27, 0x0e, 0x2b, 0xae, 0x88, 0x23, 0x17, 0x7e, 0x00, 0xe2, 0x06,
0x48, 0x2b, 0x71, 0xe1, 0x84, 0xe2, 0xc5, 0x47, 0x46, 0x64, 0x66, 0x55, 0x57, 0xef, 0xce, 0x0e,
0x17, 0xbb, 0xf2, 0xc5, 0x8b, 0xf7, 0x5e, 0xbc, 0x78, 0xf1, 0xe2, 0xbd, 0x17, 0xaf, 0x07, 0xea,
0x69, 0xd2, 0xd9, 0x4f, 0xd2, 0x98, 0xc5, 0x64, 0xf6, 0xac, 0xc3, 0xd2, 0xa4, 0xd3, 0xda, 0x39,
0x8b, 0xe3, 0xb3, 0x1e, 0x7d, 0xe0, 0x27, 0xe1, 0x03, 0x3f, 0x8a, 0x62, 0xe6, 0xb3, 0x30, 0x8e,
0x32, 0x81, 0xe5, 0xae, 0xc0, 0xd2, 0x11, 0x65, 0x1f, 0x45, 0xdd, 0xd8, 0xa3, 0x5f, 0x0e, 0x68,
0xc6, 0xdc, 0x7f, 0x98, 0x86, 0x65, 0x0d, 0xca, 0x92, 0x38, 0xca, 0x28, 0xd9, 0x84, 0xd9, 0x41,
0xc2, 0xc2, 0x3e, 0x6d, 0x3a, 0xb7, 0x9c, 0xbb, 0x75, 0x4f, 0x7e, 0x91, 0x07, 0xb0, 0xe6, 0x5f,
0xf8, 0x61, 0xcf, 0x3f, 0xed, 0xd1, 0x36, 0x7d, 0xde, 0x39, 0xf7, 0xa3, 0x33, 0x9a, 0x35, 0x6b,
0xb7, 0x9c, 0xbb, 0x53, 0x1e, 0xd1, 0x43, 0x1f, 0xa8, 0x11, 0xf2, 0x4d, 0x58, 0xa5, 0x11, 0x07,
0x05, 0x06, 0xfa, 0x14, 0xa2, 0xaf, 0xc8, 0x81, 0x1c, 0xf9, 0x11, 0x6c, 0x06, 0xb4, 0xeb, 0x0f,
0x7a, 0xac, 0xdd, 0x8d, 0x53, 0xfa, 0xbc, 0x9d, 0xa4, 0xf1, 0x45, 0x18, 0xd0, 0xb4, 0x39, 0x8d,
0x52, 0xac, 0xcb, 0xd1, 0x0f, 0xf9, 0xe0, 0xb1, 0x1c, 0x23, 0x0f, 0x61, 0x43, 0xcf, 0x0a, 0x7d,
0xd6, 0xee, 0x0c, 0xd2, 0x94, 0x46, 0x9d, 0x61, 0x73, 0x06, 0x27, 0xad, 0xa9, 0x49, 0xa1, 0xcf,
0x0e, 0xe5, 0x10, 0xf9, 0x0c, 0x56, 0xb2, 0xc1, 0x69, 0x36, 0xcc, 0x18, 0xed, 0xb7, 0x33, 0xe6,
0xb3, 0x41, 0xd6, 0x9c, 0xbd, 0x35, 0x75, 0x77, 0xe1, 0xe1, 0xeb, 0xfb, 0x42, 0x8d, 0xfb, 0x05,
0x95, 0xec, 0x9f, 0x28, 0xfc, 0x13, 0x44, 0xff, 0x20, 0x62, 0xe9, 0xd0, 0x5b, 0xce, 0x6c, 0x28,
0xf9, 0x14, 0x16, 0xd3, 0xa4, 0xd3, 0xa6, 0x51, 0x90, 0xc4, 0x61, 0xc4, 0xb2, 0xe6, 0x1c, 0x52,
0xbd, 0x37, 0x8a, 0xaa, 0x97, 0x74, 0x3e, 0x50, 0xb8, 0x82, 0x64, 0x23, 0x35, 0x40, 0xad, 0xf7,
0x60, 0xbd, 0x8a, 0x31, 0x59, 0x81, 0xa9, 0x67, 0x74, 0x28, 0x77, 0x87, 0xff, 0x24, 0xeb, 0x30,
0x73, 0xe1, 0xf7, 0x06, 0x14, 0x37, 0x63, 0xde, 0x13, 0x1f, 0xdf, 0xa9, 0xbd, 0xed, 0xb4, 0x9e,
0xc0, 0x6a, 0x89, 0x4d, 0x05, 0x81, 0x7b, 0x26, 0x81, 0x85, 0x87, 0x6b, 0x4a, 0x64, 0xef, 0xf8,
0x50, 0xcd, 0x35, 0xa8, 0xba, 0xb7, 0x61, 0xef, 0x88, 0xb2, 0xc3, 0xb8, 0xdf, 0x1f, 0x44, 0x61,
0x07, 0x6d, 0xcc, 0xa3, 0x3d, 0x7f, 0x48, 0xd3, 0x4c, 0x59, 0xd6, 0xa7, 0xb0, 0x5e, 0x35, 0x4e,
0x9a, 0x30, 0x27, 0xf7, 0x1e, 0xf9, 0xcf, 0x7b, 0xea, 0x93, 0xec, 0x40, 0xbd, 0x13, 0x47, 0x11,
0xed, 0x30, 0x1a, 0xc8, 0x85, 0xe4, 0x00, 0xf7, 0x8f, 0x6b, 0x70, 0x6b, 0x34, 0x4f, 0x69, 0xba,
0x5f, 0xc1, 0x66, 0xc7, 0x44, 0x68, 0xa7, 0x12, 0xa3, 0xe9, 0xe0, 0x56, 0x1c, 0x1a, 0x5b, 0x31,
0x96, 0xd2, 0x7e, 0xe5, 0xa8, 0xd8, 0xa4, 0x8d, 0x4e, 0xd5, 0x58, 0xab, 0x0b, 0xad, 0xd1, 0x93,
0x2a, 0x54, 0xfe, 0xd0, 0x56, 0xf9, 0x8e, 0x12, 0xad, 0x8a, 0x88, 0xa9, 0xfb, 0x6f, 0xc3, 0xd6,
0x11, 0x8d, 0x68, 0x1a, 0x76, 0xb4, 0x71, 0x48, 0x9d, 0x73, 0x0d, 0x6a, 0x9b, 0x94, 0xac, 0x72,
0x80, 0xdb, 0x82, 0x66, 0x79, 0xa2, 0x58, 0xae, 0xbb, 0x09, 0xeb, 0x47, 0x94, 0x69, 0xb8, 0xde,
0xc5, 0x9f, 0x3a, 0xb0, 0x81, 0x03, 0xd9, 0x69, 0x36, 0x14, 0x03, 0x52, 0xd5, 0xbf, 0x03, 0xab,
0x9a, 0x74, 0xa6, 0x8e, 0x91, 0xd0, 0xf2, 0x5b, 0x86, 0x96, 0xcb, 0x33, 0xf3, 0xc3, 0x94, 0x99,
0xa7, 0x29, 0x3f, 0x93, 0x12, 0xdc, 0x3a, 0x84, 0x8d, 0x4a, 0xd4, 0xeb, 0xd8, 0xbf, 0xdb, 0x84,
0xcd, 0x23, 0xca, 0x0c, 0x33, 0x36, 0x0c, 0x74, 0xc1, 0x00, 0x73, 0xbb, 0xcc, 0x98, 0x9f, 0xb2,
0xdc, 0x2e, 0xe5, 0x27, 0x79, 0x15, 0x96, 0x7a, 0x61, 0xc6, 0x68, 0xd4, 0xf6, 0x83, 0x20, 0xa5,
0x99, 0x70, 0x79, 0x75, 0x6f, 0x51, 0x40, 0x0f, 0x04, 0xd0, 0xfd, 0x47, 0x87, 0x6f, 0x4c, 0x81,
0x95, 0x54, 0xd6, 0x27, 0x50, 0xcf, 0xbd, 0x82, 0x50, 0xd2, 0xbe, 0xa1, 0xa4, 0xaa, 0x39, 0xfb,
0x05, 0xd7, 0x90, 0x13, 0x68, 0xfd, 0x16, 0x2c, 0xbd, 0xe8, 0x03, 0xfd, 0x36, 0xb4, 0xa4, 0x6d,
0x28, 0x8f, 0xfc, 0xa9, 0xdf, 0xa7, 0xca, 0xae, 0x5a, 0x30, 0xaf, 0x1c, 0xb8, 0xe4, 0xa1, 0xbf,
0xdd, 0x5d, 0xd8, 0xae, 0x9c, 0x29, 0x0d, 0xeb, 0x01, 0xac, 0x1d, 0x51, 0xa6, 0xdd, 0xbc, 0xa2,
0x38, 0xd2, 0x0b, 0xb8, 0x8f, 0xd0, 0x12, 0x8d, 0x09, 0x52, 0x85, 0x3b, 0x50, 0xcf, 0x2f, 0x11,
0x69, 0xdb, 0x1a, 0xe0, 0x3e, 0x44, 0x33, 0x55, 0xb3, 0x1e, 0x3f, 0x39, 0xf6, 0xa8, 0x98, 0x76,
0x03, 0xe6, 0x63, 0x96, 0xb4, 0x3b, 0x71, 0xa0, 0x44, 0x9f, 0x8b, 0x59, 0x72, 0x18, 0x07, 0x54,
0x9a, 0x86, 0x31, 0x47, 0x9b, 0xc6, 0xdf, 0x88, 0xad, 0xb4, 0x87, 0xa4, 0x1c, 0xbf, 0x09, 0x75,
0x45, 0x50, 0x6d, 0xe5, 0xb7, 0x8c, 0xad, 0xac, 0x9a, 0xb3, 0xff, 0x58, 0x70, 0x94, 0x3b, 0x39,
0x2f, 0x05, 0xc8, 0x5a, 0xef, 0xc0, 0xa2, 0x35, 0x74, 0x95, 0x65, 0xd7, 0xcd, 0x2d, 0x7b, 0x04,
0x9b, 0xef, 0x87, 0x99, 0x79, 0xe3, 0x4e, 0xb2, 0x5d, 0x5f, 0xc0, 0xd2, 0xb1, 0x1f, 0xa6, 0xd9,
0xc9, 0x20, 0x49, 0x62, 0x34, 0xef, 0xd7, 0x60, 0x39, 0xbf, 0xd6, 0x13, 0x3e, 0x26, 0x27, 0x2d,
0x69, 0x30, 0xce, 0x20, 0xaf, 0xc0, 0xa2, 0xba, 0xce, 0x05, 0x9a, 0x10, 0xa9, 0x21, 0x81, 0x88,
0xe4, 0x7e, 0x3d, 0x6d, 0xa9, 0xce, 0x0a, 0x2c, 0x08, 0x4c, 0x47, 0xbe, 0x0e, 0x2b, 0xf0, 0xb7,
0x69, 0x08, 0x35, 0xfb, 0x3a, 0x68, 0xc2, 0xdc, 0x05, 0x4d, 0x4f, 0xe3, 0x8c, 0x62, 0xcc, 0x30,
0xef, 0xa9, 0x4f, 0x2e, 0xc8, 0x20, 0x0b, 0xa3, 0xb3, 0x76, 0xe6, 0x47, 0xc1, 0x69, 0xfc, 0x1c,
0x23, 0x84, 0x79, 0xaf, 0x81, 0xc0, 0x13, 0x01, 0x23, 0xb7, 0xa1, 0x71, 0xce, 0x58, 0xd2, 0xe6,
0xa1, 0x4b, 0x3c, 0x60, 0x32, 0x20, 0x58, 0xe0, 0xb0, 0x27, 0x02, 0xc4, 0x0f, 0x36, 0xa2, 0x0c,
0x32, 0x9a, 0xfa, 0x67, 0x34, 0x62, 0xcd, 0x59, 0x71, 0xb0, 0x39, 0xf4, 0x87, 0x0a, 0x48, 0x76,
0x01, 0x10, 0x2d, 0x49, 0xe3, 0xe7, 0xc3, 0xe6, 0x9c, 0x30, 0x3d, 0x0e, 0x39, 0xe6, 0x00, 0xae,
0xbf, 0x53, 0x3f, 0xa3, 0x2a, 0xf4, 0x08, 0x69, 0xd6, 0x9c, 0x17, 0xfa, 0xe3, 0xe0, 0x43, 0x0d,
0x25, 0x6d, 0x1e, 0x77, 0x48, 0xad, 0xb7, 0xfd, 0x2c, 0xa3, 0x2c, 0x6b, 0xd6, 0xd1, 0x80, 0x1e,
0x55, 0x18, 0x50, 0x21, 0xfe, 0x90, 0xf3, 0x0e, 0x70, 0x9a, 0x8e, 0x3f, 0x2c, 0x28, 0x8f, 0xb7,
0xfc, 0x01, 0x3b, 0xa7, 0x11, 0xe3, 0xb7, 0x07, 0x67, 0x92, 0x84, 0x4d, 0x40, 0xdd, 0xac, 0x58,
0x03, 0x07, 0x49, 0xd8, 0xfa, 0x9c, 0x07, 0x17, 0x65, 0xaa, 0x15, 0x26, 0xf8, 0xba, 0xed, 0x4a,
0x36, 0x95, 0xb0, 0xb6, 0x1d, 0x99, 0xa6, 0x79, 0x09, 0x2b, 0x47, 0x94, 0x3d, 0x09, 0x3b, 0xcf,
0x68, 0x3a, 0x81, 0x51, 0x92, 0xbb, 0x30, 0xcd, 0x2d, 0x4a, 0x32, 0x58, 0xd7, 0x37, 0xa1, 0x8c,
0xd8, 0x38, 0x23, 0x0f, 0x31, 0xf8, 0x5e, 0xa0, 0xe6, 0xda, 0x6c, 0x98, 0x08, 0xbb, 0xa8, 0x7b,
0x75, 0x84, 0x3c, 0x19, 0x26, 0xd4, 0x7d, 0x0a, 0x0d, 0x73, 0x12, 0x77, 0x1a, 0x01, 0xed, 0x85,
0xfd, 0x90, 0xd1, 0x54, 0x39, 0x0d, 0x0d, 0xe0, 0xf6, 0xc8, 0xb7, 0x48, 0xda, 0x31, 0xfe, 0xe6,
0xe7, 0xed, 0xcb, 0x41, 0xcc, 0x14, 0x6d, 0xf1, 0xe1, 0xfe, 0x45, 0x0d, 0x96, 0xd4, 0x72, 0xa4,
0x31, 0x2b, 0x99, 0x9d, 0x2b, 0x65, 0xbe, 0x0d, 0x8d, 0x9e, 0x9f, 0xb1, 0xf6, 0x20, 0x09, 0x7c,
0x15, 0xda, 0x4c, 0x79, 0x0b, 0x1c, 0xf6, 0x43, 0x01, 0xe2, 0x16, 0xad, 0x22, 0x57, 0x3c, 0x5b,
0x92, 0x7b, 0xa3, 0x63, 0x2e, 0x86, 0xc0, 0x34, 0x9f, 0x83, 0xd6, 0xee, 0x78, 0xf8, 0x9b, 0xc3,
0xce, 0xc3, 0xb3, 0x73, 0xb4, 0x6e, 0xc7, 0xc3, 0xdf, 0x7c, 0x07, 0x7b, 0xf1, 0x25, 0xda, 0xb2,
0xe3, 0xf1, 0x9f, 0x1c, 0x72, 0x1a, 0x06, 0x68, 0xba, 0x8e, 0xc7, 0x7f, 0x72, 0x88, 0x9f, 0x3d,
0x43, 0x43, 0x75, 0x3c, 0xfe, 0x93, 0x47, 0xfd, 0x17, 0x71, 0x6f, 0xd0, 0xa7, 0xcd, 0x3a, 0x02,
0xe5, 0x17, 0xd9, 0x86, 0x7a, 0x92, 0x86, 0x1d, 0xda, 0xf6, 0xd9, 0x39, 0x1a, 0x93, 0xe3, 0xcd,
0x23, 0xe0, 0x80, 0x9d, 0xbb, 0x6b, 0xb0, 0xaa, 0x37, 0x5a, 0x7b, 0xcf, 0xcf, 0x60, 0x4e, 0x42,
0xc6, 0x6e, 0xfa, 0x1b, 0x30, 0xc7, 0x04, 0x5a, 0xb3, 0x86, 0xa7, 0x40, 0x1b, 0x96, 0xad, 0x69,
0x4f, 0xa1, 0xb9, 0xdf, 0x03, 0x62, 0x72, 0x93, 0x1b, 0x71, 0x2f, 0xa7, 0x23, 0xdc, 0xf1, 0xb2,
0x4d, 0x27, 0xcb, 0x09, 0x7c, 0x85, 0x97, 0xd1, 0xe3, 0x34, 0xe0, 0x8e, 0x24, 0x7e, 0xf6, 0x52,
0x4d, 0xf3, 0x07, 0xb0, 0xa8, 0x19, 0x7f, 0xc4, 0x68, 0x9f, 0x2b, 0xdc, 0xef, 0xc7, 0x83, 0x88,
0x21, 0x4f, 0xc7, 0x93, 0x5f, 0xdc, 0x02, 0x51, 0xbf, 0xc8, 0xd2, 0xf1, 0xc4, 0x07, 0x59, 0x82,
0x5a, 0x18, 0xc8, 0xe4, 0xa9, 0x16, 0x06, 0xee, 0xff, 0x3a, 0xb0, 0x6a, 0x2c, 0xe4, 0xca, 0x46,
0x59, 0xb2, 0xb8, 0x5a, 0x85, 0xc5, 0xdd, 0x83, 0xd9, 0x93, 0x30, 0xe0, 0x39, 0x1b, 0xd7, 0xeb,
0x86, 0x22, 0x67, 0xad, 0xc3, 0x43, 0x14, 0x8e, 0xea, 0x67, 0xcf, 0xb2, 0xd6, 0xec, 0x44, 0x54,
0x8e, 0x52, 0x3a, 0x0f, 0xd7, 0xca, 0xe7, 0xc1, 0xd6, 0xe5, 0x5c, 0x51, 0x97, 0x22, 0x5a, 0xd5,
0xb4, 0xb5, 0xe5, 0x75, 0x01, 0x72, 0xe0, 0xc4, 0x6d, 0xfd, 0x35, 0x80, 0x58, 0x63, 0x4a, 0xfb,
0xbb, 0x5e, 0x12, 0x5a, 0x9b, 0xa0, 0x81, 0xec, 0x7e, 0x8c, 0xa1, 0x86, 0xc9, 0x5c, 0x2a, 0xff,
0x81, 0x45, 0x53, 0xd8, 0x22, 0x29, 0xd1, 0xcc, 0x2c, 0x62, 0x6f, 0x23, 0xb1, 0xfd, 0x6e, 0x97,
0x6f, 0xbd, 0x91, 0x98, 0x4f, 0xbc, 0xc3, 0x9f, 0xc2, 0xbc, 0x9c, 0x21, 0xcd, 0x42, 0x20, 0xd4,
0xc2, 0x80, 0xbc, 0x0b, 0x60, 0xdc, 0x43, 0x62, 0x5d, 0xdb, 0x4a, 0x06, 0x39, 0x49, 0x59, 0x03,
0xb2, 0x33, 0xd0, 0xdd, 0x1e, 0xac, 0x55, 0xa0, 0x70, 0x51, 0x74, 0x5a, 0x2d, 0x45, 0x51, 0xdf,
0xe4, 0x26, 0x34, 0x58, 0xcc, 0xfc, 0x7e, 0x27, 0xbf, 0x21, 0x1c, 0x0f, 0x10, 0xf4, 0x94, 0x43,
0xd0, 0x41, 0xc5, 0x7d, 0x61, 0xb9, 0xdc, 0x41, 0xc5, 0xfd, 0xc0, 0xf5, 0x31, 0xf0, 0xb2, 0x16,
0x2d, 0x55, 0x38, 0x69, 0xcb, 0x5e, 0x83, 0x05, 0x5f, 0x4c, 0x51, 0x0b, 0x5b, 0x2e, 0x2c, 0xcc,
0xd3, 0x08, 0x2e, 0xc1, 0x1b, 0xe8, 0x20, 0x8e, 0x7a, 0xe1, 0xa9, 0xb2, 0x8e, 0x57, 0xd0, 0x59,
0x29, 0x58, 0x1e, 0x93, 0x04, 0x3e, 0xf3, 0x91, 0x5b, 0xd3, 0xc3, 0xdf, 0xee, 0x1f, 0x39, 0xb0,
0x72, 0x14, 0xa7, 0xac, 0x17, 0xf7, 0xc3, 0x58, 0x86, 0xf7, 0x3c, 0x1c, 0x51, 0xe1, 0xbf, 0x8c,
0x23, 0xe5, 0x27, 0xf7, 0x90, 0xdd, 0x38, 0x8c, 0x84, 0xad, 0xd6, 0xa4, 0x82, 0xe2, 0x30, 0xe2,
0xa6, 0x4a, 0x6e, 0x41, 0x23, 0xa0, 0x59, 0x37, 0x0d, 0x13, 0x9e, 0xce, 0x49, 0xb7, 0x60, 0x82,
0x38, 0xe1, 0x13, 0xbf, 0xef, 0x47, 0x5d, 0x2a, 0x3d, 0xbb, 0xfa, 0x74, 0x37, 0xd0, 0x5d, 0x69,
0x49, 0x8c, 0xcc, 0xda, 0x06, 0xcb, 0xa5, 0xfc, 0x2a, 0xd4, 0x13, 0x05, 0x94, 0xe6, 0xd7, 0xd2,
0x77, 0x75, 0x61, 0x39, 0x5e, 0x8e, 0xea, 0xee, 0xf0, 0xd8, 0x3f, 0xa7, 0x77, 0x3c, 0x1c, 0x0c,
0xfc, 0x74, 0xa4, 0xb8, 0x45, 0x30, 0x7b, 0x10, 0x87, 0x11, 0x57, 0x14, 0x5f, 0x94, 0x0a, 0xde,
0xf8, 0x6f, 0x53, 0xf4, 0x9a, 0x25, 0xba, 0xa9, 0xad, 0x19, 0x5b, 0x5b, 0x37, 0x00, 0x12, 0x9a,
0x76, 0x69, 0xc4, 0xfc, 0x53, 0xb5, 0x62, 0x03, 0xe2, 0x9e, 0x01, 0x79, 0xdc, 0xeb, 0xf5, 0xc3,
0x88, 0x72, 0xb6, 0x52, 0x98, 0x09, 0xda, 0x1f, 0x2f, 0x83, 0xcd, 0x69, 0xa6, 0xc4, 0xe9, 0x07,
0xb0, 0xfa, 0x38, 0xaa, 0x60, 0xa4, 0xc8, 0x39, 0x93, 0xc8, 0xd5, 0x4a, 0xe4, 0xbe, 0x0f, 0x4d,
0x43, 0xf0, 0x8c, 0xbc, 0x03, 0x75, 0x29, 0xa3, 0x4e, 0x14, 0xda, 0xda, 0x1b, 0x94, 0x56, 0xe8,
0xe5, 0xc8, 0xee, 0x5f, 0x3a, 0xd0, 0xc8, 0x25, 0xcb, 0xc8, 0x43, 0xb8, 0xc6, 0xd5, 0xad, 0xa8,
0xdc, 0xd0, 0x54, 0x72, 0x9c, 0x3d, 0xfc, 0xaf, 0x88, 0x0b, 0x05, 0x72, 0xfb, 0x18, 0x20, 0x07,
0x56, 0x84, 0x75, 0xf7, 0xed, 0xb0, 0xee, 0x7a, 0x99, 0xaa, 0x12, 0xcd, 0x88, 0xec, 0xfe, 0x65,
0x96, 0xa7, 0x7b, 0x15, 0xc6, 0x22, 0x6d, 0xf0, 0x0d, 0x68, 0x88, 0xb3, 0xc0, 0x3d, 0x80, 0x12,
0xb8, 0x99, 0x97, 0x36, 0xc2, 0xc8, 0x03, 0x3c, 0x1b, 0x38, 0x4e, 0xde, 0x82, 0x45, 0x14, 0xb6,
0x13, 0x0b, 0x85, 0xc8, 0x83, 0x6d, 0x4f, 0x68, 0x22, 0x8a, 0x54, 0x19, 0x49, 0x60, 0xc3, 0x9a,
0xd2, 0xc9, 0x84, 0x08, 0xf2, 0x92, 0x7a, 0xcf, 0x08, 0xa5, 0xc7, 0x49, 0x29, 0x94, 0x25, 0x09,
0xca, 0x31, 0xa1, 0xba, 0xb5, 0x6e, 0x79, 0x84, 0xdc, 0x87, 0xa6, 0xe4, 0x88, 0x9a, 0x91, 0x57,
0x9c, 0x2d, 0x63, 0x43, 0x4c, 0x44, 0x04, 0x32, 0x80, 0x75, 0x73, 0x82, 0x96, 0xf0, 0x1a, 0x4e,
0x7c, 0x77, 0x7a, 0x09, 0xa3, 0x92, 0x80, 0xa4, 0x5b, 0x1a, 0x68, 0xff, 0x36, 0xb4, 0xc6, 0x2d,
0xa8, 0x62, 0xdb, 0x5f, 0xb5, 0xb7, 0x7d, 0xbd, 0xc2, 0x24, 0x33, 0xb3, 0x80, 0xf8, 0x39, 0x6c,
0x8d, 0x11, 0xe6, 0x0a, 0x55, 0x07, 0xc3, 0x52, 0x4d, 0x6b, 0xfa, 0x73, 0x07, 0xda, 0xfb, 0x41,
0x50, 0x72, 0x4e, 0x79, 0x91, 0xe0, 0x9b, 0x76, 0xb9, 0xbb, 0xb0, 0x5d, 0x29, 0x90, 0xac, 0x66,
0x3c, 0x87, 0x5d, 0x8f, 0x0e, 0xe2, 0x73, 0xfa, 0x4d, 0x8b, 0xec, 0xde, 0x82, 0x1b, 0xe3, 0x38,
0x5a, 0x18, 0xc8, 0xe4, 0xa9, 0x16, 0x06, 0xee, 0xff, 0x3a, 0xb0, 0x6a, 0x2c, 0xe4, 0xda, 0x46,
0x59, 0xb2, 0xb8, 0x5a, 0x85, 0xc5, 0xdd, 0x83, 0xe9, 0xd3, 0x30, 0xe0, 0x39, 0x1b, 0xd7, 0xeb,
0x86, 0x22, 0x67, 0xad, 0xc3, 0x43, 0x14, 0x8e, 0xea, 0x67, 0xcf, 0xb2, 0xe6, 0xf4, 0x58, 0x54,
0x8e, 0x52, 0x3a, 0x0f, 0x33, 0xe5, 0xf3, 0x60, 0xeb, 0x72, 0xb6, 0xa8, 0x4b, 0x11, 0xad, 0x6a,
0xda, 0xda, 0xf2, 0x3a, 0x00, 0x39, 0x70, 0xec, 0xb6, 0xfe, 0x1a, 0x40, 0xac, 0x31, 0xa5, 0xfd,
0xdd, 0x28, 0x09, 0xad, 0x4d, 0xd0, 0x40, 0x76, 0x3f, 0xc6, 0x50, 0xc3, 0x64, 0x2e, 0x95, 0xff,
0xd0, 0xa2, 0x29, 0x6c, 0x91, 0x94, 0x68, 0x66, 0x16, 0xb1, 0xb7, 0x90, 0xd8, 0x41, 0xa7, 0xc3,
0xb7, 0xde, 0x48, 0xcc, 0xc7, 0xde, 0xe1, 0x4f, 0x61, 0x4e, 0xce, 0x90, 0x66, 0x21, 0x10, 0x6a,
0x61, 0x40, 0xde, 0x01, 0x30, 0xee, 0x21, 0xb1, 0xae, 0x6d, 0x25, 0x83, 0x9c, 0xa4, 0xac, 0x01,
0xd9, 0x19, 0xe8, 0x6e, 0x17, 0xd6, 0x2a, 0x50, 0xb8, 0x28, 0x3a, 0xad, 0x96, 0xa2, 0xa8, 0x6f,
0xb2, 0x07, 0x0b, 0x2c, 0x66, 0x7e, 0xaf, 0x9d, 0xdf, 0x10, 0x8e, 0x07, 0x08, 0x7a, 0xca, 0x21,
0xe8, 0xa0, 0xe2, 0x9e, 0xb0, 0x5c, 0xee, 0xa0, 0xe2, 0x5e, 0xe0, 0xfa, 0x18, 0x78, 0x59, 0x8b,
0x96, 0x2a, 0x1c, 0xb7, 0x65, 0xdf, 0x84, 0x79, 0x5f, 0x4c, 0x51, 0x0b, 0x5b, 0x2e, 0x2c, 0xcc,
0xd3, 0x08, 0x2e, 0xc1, 0x1b, 0xe8, 0x30, 0x8e, 0xba, 0xe1, 0x99, 0xb2, 0x8e, 0xd7, 0xd0, 0x59,
0x29, 0x58, 0x1e, 0x93, 0x04, 0x3e, 0xf3, 0x91, 0x5b, 0xc3, 0xc3, 0xdf, 0xee, 0x1f, 0x39, 0xb0,
0x72, 0x1c, 0xa7, 0xac, 0x1b, 0xf7, 0xc2, 0x58, 0x86, 0xf7, 0x3c, 0x1c, 0x51, 0xe1, 0xbf, 0x8c,
0x23, 0xe5, 0x27, 0xf7, 0x90, 0x9d, 0x38, 0x8c, 0x84, 0xad, 0xd6, 0xa4, 0x82, 0xe2, 0x30, 0xe2,
0xa6, 0x4a, 0x6e, 0xc1, 0x42, 0x40, 0xb3, 0x4e, 0x1a, 0x26, 0x3c, 0x9d, 0x93, 0x6e, 0xc1, 0x04,
0x71, 0xc2, 0xa7, 0x7e, 0xcf, 0x8f, 0x3a, 0x54, 0x7a, 0x76, 0xf5, 0xe9, 0x6e, 0xa0, 0xbb, 0xd2,
0x92, 0x18, 0x99, 0xb5, 0x0d, 0x96, 0x4b, 0xf9, 0x55, 0xa8, 0x27, 0x0a, 0x28, 0xcd, 0xaf, 0xa9,
0xef, 0xea, 0xc2, 0x72, 0xbc, 0x1c, 0xd5, 0xdd, 0xe1, 0xb1, 0x7f, 0x4e, 0xef, 0x64, 0xd0, 0xef,
0xfb, 0xe9, 0x50, 0x71, 0x8b, 0x60, 0xfa, 0x30, 0x0e, 0x23, 0xae, 0x28, 0xbe, 0x28, 0x15, 0xbc,
0xf1, 0xdf, 0xa6, 0xe8, 0x35, 0x4b, 0x74, 0x53, 0x5b, 0x53, 0xb6, 0xb6, 0x6e, 0x02, 0x24, 0x34,
0xed, 0xd0, 0x88, 0xf9, 0x67, 0x6a, 0xc5, 0x06, 0xc4, 0x3d, 0x07, 0xf2, 0xb8, 0xdb, 0xed, 0x85,
0x11, 0xe5, 0x6c, 0xa5, 0x30, 0x63, 0xb4, 0x3f, 0x5a, 0x06, 0x9b, 0xd3, 0x54, 0x89, 0xd3, 0x0f,
0x60, 0xf5, 0x71, 0x54, 0xc1, 0x48, 0x91, 0x73, 0xc6, 0x91, 0xab, 0x95, 0xc8, 0x7d, 0x1f, 0x1a,
0x86, 0xe0, 0x19, 0x79, 0x1b, 0xea, 0x52, 0x46, 0x9d, 0x28, 0xb4, 0xb4, 0x37, 0x28, 0xad, 0xd0,
0xcb, 0x91, 0xdd, 0xbf, 0x74, 0x60, 0x21, 0x97, 0x2c, 0x23, 0x8f, 0x60, 0x86, 0xab, 0x5b, 0x51,
0xb9, 0xa9, 0xa9, 0xe4, 0x38, 0xfb, 0xf8, 0x5f, 0x11, 0x17, 0x0a, 0xe4, 0xd6, 0x09, 0x40, 0x0e,
0xac, 0x08, 0xeb, 0x1e, 0xd8, 0x61, 0xdd, 0x8d, 0x32, 0x55, 0x25, 0x9a, 0x11, 0xd9, 0xfd, 0xcb,
0x34, 0x4f, 0xf7, 0x2a, 0x8c, 0x45, 0xda, 0xe0, 0xb7, 0x60, 0x41, 0x9c, 0x05, 0xee, 0x01, 0x94,
0xc0, 0x8d, 0xbc, 0xb4, 0x11, 0x46, 0x1e, 0xe0, 0xd9, 0xc0, 0x71, 0xf2, 0x26, 0x2c, 0xa2, 0xb0,
0xed, 0x58, 0x28, 0x44, 0x1e, 0x6c, 0x7b, 0x42, 0x03, 0x51, 0xa4, 0xca, 0x48, 0x02, 0x1b, 0xd6,
0x94, 0x76, 0x26, 0x44, 0x90, 0x97, 0xd4, 0xbb, 0x46, 0x28, 0x3d, 0x4a, 0x4a, 0xa1, 0x2c, 0x49,
0x50, 0x8e, 0x09, 0xd5, 0xad, 0x75, 0xca, 0x23, 0xe4, 0x01, 0x34, 0x24, 0x47, 0xd4, 0x8c, 0xbc,
0xe2, 0x6c, 0x19, 0x17, 0xc4, 0x44, 0x44, 0x20, 0x7d, 0x58, 0x37, 0x27, 0x68, 0x09, 0x67, 0x70,
0xe2, 0x3b, 0x93, 0x4b, 0x18, 0x95, 0x04, 0x24, 0x9d, 0xd2, 0x40, 0xeb, 0xb7, 0xa1, 0x39, 0x6a,
0x41, 0x15, 0xdb, 0x7e, 0xdf, 0xde, 0xf6, 0xf5, 0x0a, 0x93, 0xcc, 0xcc, 0x02, 0xe2, 0xe7, 0xb0,
0x35, 0x42, 0x98, 0x6b, 0x54, 0x1d, 0x0c, 0x4b, 0x35, 0xad, 0xe9, 0xcf, 0x1d, 0x68, 0x1d, 0x04,
0x41, 0xc9, 0x39, 0xe5, 0x45, 0x82, 0x97, 0xed, 0x72, 0x77, 0x61, 0xbb, 0x52, 0x20, 0x59, 0xcd,
0x78, 0x0e, 0xbb, 0x1e, 0xed, 0xc7, 0x17, 0xf4, 0x65, 0x8b, 0xec, 0xde, 0x82, 0x9b, 0xa3, 0x38,
0x4b, 0xd9, 0xb0, 0xbc, 0x67, 0x97, 0xc7, 0x75, 0x60, 0xf4, 0x9f, 0x0e, 0x2c, 0xda, 0x85, 0xf3,
0x17, 0x95, 0x8b, 0xbf, 0x0e, 0x24, 0xa5, 0x19, 0xeb, 0x24, 0x71, 0xbf, 0xcf, 0x53, 0xf2, 0x80,
0xf6, 0xfd, 0x91, 0x2c, 0xd9, 0xaf, 0xf0, 0x91, 0x23, 0x31, 0xf0, 0x88, 0xc3, 0xc9, 0x16, 0xcc,
0xfb, 0x49, 0xd8, 0xe1, 0x56, 0x23, 0xf2, 0xf1, 0x39, 0x3f, 0x09, 0x3f, 0xa6, 0x23, 0xe2, 0xc2,
0xa2, 0x1c, 0xe8, 0xf4, 0xe9, 0x39, 0xed, 0x63, 0xcc, 0x37, 0xe3, 0x35, 0xc4, 0xf0, 0x27, 0x1c,
0x44, 0xee, 0xc1, 0x4a, 0x92, 0x86, 0xdc, 0xfc, 0xf2, 0xb7, 0x81, 0x79, 0x94, 0x66, 0x59, 0xc2,
0xd5, 0xea, 0xdc, 0x1f, 0xc1, 0xf5, 0x0a, 0x5d, 0x48, 0x1f, 0xf5, 0x5d, 0x58, 0xb6, 0x5f, 0x18,
0x94, 0x9f, 0xd2, 0x51, 0xab, 0x35, 0xd1, 0x5b, 0xea, 0x59, 0x74, 0x64, 0xf4, 0x89, 0x38, 0x9e,
0xcf, 0x74, 0x4d, 0xcb, 0xfd, 0x12, 0xd6, 0x73, 0xe0, 0x41, 0x1c, 0x9d, 0xd3, 0x34, 0xe3, 0xd6,
0x46, 0x60, 0xb6, 0x97, 0xc6, 0xaa, 0x20, 0x8b, 0xbf, 0x79, 0xdc, 0xc6, 0x62, 0x69, 0x06, 0x35,
0x16, 0x73, 0x9c, 0xd4, 0x67, 0xea, 0x96, 0xc2, 0xdf, 0x3c, 0x4e, 0x0e, 0x91, 0x08, 0xed, 0xe0,
0x98, 0x30, 0xd5, 0x86, 0x84, 0x71, 0x2e, 0xee, 0x53, 0x0c, 0x1f, 0x4d, 0x51, 0xe4, 0x1a, 0x7f,
0x1d, 0x1a, 0x62, 0x8d, 0x7c, 0xa6, 0x5a, 0xdf, 0x8e, 0xb5, 0xbe, 0x82, 0x98, 0x1e, 0xf4, 0x34,
0xd4, 0xfd, 0xef, 0x1a, 0x34, 0x31, 0x62, 0x7d, 0x44, 0x99, 0x1f, 0xf6, 0x27, 0xc7, 0xd2, 0x22,
0x06, 0xad, 0xe9, 0x18, 0xf4, 0x25, 0x58, 0x34, 0x0b, 0x22, 0x23, 0x95, 0xcc, 0x1a, 0xe5, 0x90,
0x11, 0x79, 0x19, 0x96, 0x30, 0xb5, 0xce, 0xb1, 0x84, 0xcd, 0x2c, 0x22, 0x54, 0xa3, 0xd9, 0x89,
0xc0, 0xb5, 0x42, 0x22, 0xc0, 0x87, 0x31, 0x98, 0xee, 0x64, 0x61, 0xa0, 0xf3, 0x04, 0x84, 0x1c,
0x87, 0x81, 0x31, 0x8c, 0xb3, 0xe7, 0x8d, 0x61, 0x9c, 0xcd, 0x73, 0xa0, 0x94, 0x8a, 0x87, 0x02,
0x7c, 0xef, 0x5a, 0x40, 0xa3, 0x6b, 0x2a, 0xe0, 0x93, 0x70, 0x80, 0xaf, 0x61, 0xb2, 0xb8, 0x5d,
0x17, 0x16, 0x2b, 0xbe, 0xf2, 0x34, 0x0d, 0xcc, 0x34, 0x2d, 0x4f, 0xea, 0x1a, 0x56, 0x52, 0x77,
0x13, 0x1a, 0x71, 0x42, 0xa3, 0x8e, 0x4c, 0xb1, 0x9b, 0x22, 0x7a, 0xe0, 0xa0, 0xa7, 0x08, 0x91,
0x25, 0x13, 0xd4, 0x79, 0x36, 0x4d, 0x5e, 0x6a, 0x2b, 0xa6, 0x56, 0x54, 0x8c, 0x4a, 0x04, 0x67,
0x2e, 0x4b, 0x04, 0xdd, 0x7d, 0x8c, 0x8a, 0x15, 0x63, 0x69, 0x3e, 0xaf, 0xc3, 0x1c, 0xaa, 0x49,
0x59, 0xce, 0xba, 0x95, 0xc6, 0x48, 0xa3, 0xf0, 0x24, 0x8e, 0xfb, 0x7d, 0x7c, 0x43, 0xc4, 0xa1,
0x69, 0x44, 0xbf, 0x0e, 0x0b, 0x62, 0x57, 0xb4, 0xd5, 0xcc, 0xe3, 0xf7, 0x47, 0x81, 0xfb, 0x6f,
0x0e, 0x90, 0xe3, 0xe1, 0xc9, 0x20, 0x9c, 0x9e, 0xda, 0xf4, 0x09, 0x3a, 0x81, 0x59, 0x34, 0x13,
0x61, 0x8e, 0xf8, 0xbb, 0x60, 0x21, 0xb3, 0x45, 0x0b, 0xc9, 0xb7, 0xf3, 0x5a, 0x75, 0x8e, 0x3e,
0x67, 0x6e, 0x3e, 0x77, 0xf1, 0xfd, 0x90, 0x46, 0xac, 0x23, 0x8b, 0x2d, 0xdc, 0xc5, 0x23, 0xe0,
0xa3, 0xc0, 0x3d, 0x86, 0x35, 0x6b, 0x65, 0x52, 0xd3, 0xb7, 0xa1, 0x29, 0x04, 0x48, 0xfa, 0x7e,
0x57, 0x57, 0xc3, 0x1b, 0x08, 0x3b, 0x42, 0xd0, 0x24, 0x7d, 0xfd, 0x89, 0x03, 0xeb, 0xc7, 0xe1,
0x60, 0xd8, 0xf7, 0x19, 0xfd, 0x25, 0x68, 0x2c, 0x5f, 0xfe, 0x8c, 0xb5, 0x7c, 0xa5, 0xc9, 0xd9,
0x5c, 0x93, 0xee, 0xcf, 0x1c, 0xd8, 0x28, 0x88, 0xa2, 0x63, 0x42, 0xdb, 0x98, 0xc6, 0x14, 0x07,
0x24, 0x92, 0xc1, 0xb4, 0x66, 0x31, 0x7d, 0x09, 0x16, 0x07, 0x61, 0x14, 0x0e, 0x86, 0x83, 0x8e,
0xd0, 0xbd, 0x90, 0xa9, 0x29, 0x81, 0x47, 0xb8, 0x05, 0x1c, 0xc9, 0x7f, 0x6e, 0x20, 0xcd, 0x4a,
0x24, 0x01, 0x14, 0x48, 0x6f, 0xc2, 0x7a, 0x1e, 0xb7, 0x77, 0x4e, 0xfd, 0x30, 0xea, 0xf4, 0xe3,
0x2c, 0x93, 0x7b, 0x4c, 0xf2, 0xb1, 0x43, 0x3f, 0x8c, 0x3e, 0x89, 0xb3, 0xcc, 0x70, 0x02, 0x73,
0xa6, 0x13, 0xe0, 0x01, 0xcc, 0xca, 0x67, 0x67, 0x7e, 0x9f, 0xbe, 0x1f, 0x0f, 0x4e, 0x5e, 0xac,
0xee, 0x6f, 0x43, 0x53, 0xd4, 0xdd, 0x98, 0x9f, 0x9e, 0x52, 0xb5, 0x03, 0x0d, 0x84, 0x3d, 0x41,
0x50, 0xe5, 0x36, 0xfc, 0x97, 0x03, 0xe4, 0x80, 0x87, 0x32, 0xfd, 0xa9, 0xed, 0x81, 0xbb, 0x12,
0x91, 0x37, 0xe7, 0x16, 0x56, 0x97, 0x90, 0x8f, 0x6c, 0xf3, 0x9b, 0xb1, 0xcc, 0x4f, 0xaf, 0x66,
0xf6, 0x8a, 0xc5, 0xb1, 0x92, 0x1f, 0x7f, 0x19, 0x96, 0x2e, 0xfc, 0x7e, 0x9f, 0x32, 0xfd, 0xc4,
0x26, 0x2b, 0xf1, 0x02, 0xaa, 0x72, 0x70, 0xb5, 0xe0, 0x79, 0x63, 0xc1, 0x1b, 0xb0, 0x66, 0xad,
0x57, 0x46, 0x43, 0x0f, 0x61, 0x53, 0x80, 0xf7, 0xfb, 0xfd, 0xa9, 0xbd, 0xaa, 0xfb, 0xd7, 0x35,
0xd8, 0x2a, 0x4d, 0xd3, 0x61, 0x83, 0x6d, 0xc6, 0x77, 0xf4, 0x72, 0xab, 0x27, 0xec, 0xc9, 0x4f,
0x39, 0xab, 0xfd, 0x4f, 0x0e, 0xcc, 0x09, 0xd0, 0xc4, 0xdd, 0xf8, 0x5c, 0x39, 0x04, 0x69, 0x70,
0x22, 0x23, 0xfa, 0xf6, 0x74, 0xcc, 0xc4, 0xff, 0xcc, 0x67, 0x55, 0xe1, 0x49, 0xe4, 0x8b, 0xea,
0x77, 0x61, 0xa5, 0x88, 0x70, 0xa5, 0x27, 0x27, 0x51, 0x55, 0xf9, 0xe0, 0x9c, 0x1a, 0xcf, 0xa8,
0x3f, 0x75, 0x60, 0xf9, 0x20, 0x8e, 0x82, 0x90, 0xdf, 0x98, 0x47, 0x7e, 0xea, 0x0f, 0x32, 0xf9,
0x92, 0x2f, 0x40, 0xaa, 0xec, 0xae, 0x01, 0x63, 0x0a, 0x9c, 0xbb, 0x00, 0xdd, 0x33, 0xda, 0x7d,
0xd6, 0x91, 0x15, 0x47, 0xf1, 0xfc, 0xcf, 0x21, 0xef, 0x87, 0x41, 0x46, 0xde, 0x80, 0xb5, 0x7c,
0xb8, 0xe3, 0x47, 0x41, 0x47, 0x96, 0x1b, 0xf1, 0x75, 0x43, 0xe3, 0xed, 0x47, 0xc1, 0x7e, 0xf6,
0x2c, 0xe3, 0xb1, 0xa2, 0xae, 0xb2, 0x75, 0x2c, 0x17, 0xbe, 0xac, 0xe1, 0xfb, 0x08, 0x76, 0xff,
0xc7, 0xc1, 0x1b, 0x50, 0xad, 0x4a, 0xee, 0x76, 0x5e, 0x58, 0xc3, 0x7a, 0xab, 0xb5, 0x65, 0xb5,
0xc2, 0x96, 0x11, 0x98, 0x0d, 0x19, 0x1d, 0xa8, 0x8b, 0x85, 0xff, 0x26, 0xef, 0xc3, 0x8a, 0x5e,
0x71, 0x27, 0x41, 0xb5, 0xc8, 0x63, 0xb2, 0x95, 0x27, 0x8e, 0x96, 0xd6, 0xbc, 0xe5, 0x6e, 0x41,
0x8d, 0xea, 0x78, 0x5d, 0x9b, 0xca, 0x51, 0x77, 0x51, 0xdb, 0xd2, 0x3f, 0x89, 0x2f, 0x21, 0x35,
0xed, 0x0e, 0x19, 0x0d, 0x64, 0xa8, 0xac, 0xbf, 0xdd, 0xff, 0x70, 0x60, 0x79, 0x3f, 0x08, 0x70,
0xdd, 0xd3, 0xb8, 0x09, 0xb5, 0xca, 0xda, 0x25, 0xab, 0x9c, 0xf9, 0x39, 0x57, 0xf9, 0x0b, 0x3b,
0x91, 0x31, 0x4a, 0x70, 0x5d, 0x58, 0xc9, 0xd7, 0x59, 0xbd, 0xbd, 0xee, 0xb7, 0x80, 0x88, 0xf4,
0xca, 0x52, 0x47, 0x11, 0x6b, 0x03, 0xd6, 0x2c, 0x2c, 0xe9, 0x6b, 0x3e, 0x84, 0xbb, 0x87, 0x94,
0x1d, 0xa4, 0xa3, 0x84, 0xc5, 0x2a, 0x9c, 0x7d, 0x44, 0x93, 0x38, 0x0b, 0x95, 0xe7, 0xa2, 0x53,
0x79, 0x9f, 0x7f, 0x76, 0xe0, 0xde, 0x14, 0x84, 0xe4, 0x12, 0xbe, 0x28, 0xd7, 0x97, 0x7e, 0xc3,
0x6c, 0x6f, 0x99, 0x8a, 0xca, 0x9e, 0x86, 0xc8, 0x2e, 0x03, 0x4d, 0xb2, 0xfd, 0x1e, 0x2c, 0xd9,
0x83, 0x57, 0x72, 0x15, 0x7d, 0xb8, 0x73, 0x89, 0x10, 0xd3, 0xd8, 0xdc, 0x1d, 0x58, 0xea, 0x5a,
0x24, 0x24, 0xa3, 0x02, 0xd4, 0x3d, 0x80, 0x57, 0x2e, 0xe5, 0x26, 0xd5, 0x36, 0x36, 0x43, 0x77,
0xff, 0x6e, 0x16, 0xb6, 0x3e, 0x0b, 0xd9, 0x59, 0x90, 0xfa, 0x17, 0xca, 0xfa, 0xa6, 0x11, 0xb2,
0x90, 0xbc, 0xd7, 0xca, 0xf5, 0x86, 0x57, 0x61, 0x35, 0x8e, 0x28, 0xe6, 0x18, 0x9d, 0xc4, 0xcf,
0xb2, 0x8b, 0x38, 0x55, 0x77, 0xe9, 0x72, 0x1c, 0x51, 0x9e, 0x67, 0x1c, 0x49, 0x70, 0xe1, 0x36,
0x9e, 0x2d, 0xde, 0xc6, 0x2b, 0x30, 0x93, 0x84, 0x91, 0x7c, 0x33, 0xe1, 0x3f, 0xf9, 0xdd, 0xc9,
0x52, 0x3f, 0x30, 0x28, 0xcb, 0xbb, 0x13, 0xa1, 0x9a, 0xae, 0x59, 0xc5, 0x9f, 0x2f, 0x54, 0xf1,
0x0d, 0x9d, 0x2c, 0xd8, 0x55, 0x8b, 0x9b, 0xd0, 0x90, 0x3f, 0x3b, 0xcc, 0x3f, 0x95, 0x29, 0x10,
0x48, 0xd0, 0x13, 0xff, 0xd4, 0x88, 0xd6, 0xc0, 0x8a, 0xd6, 0x76, 0x01, 0x7a, 0x94, 0x76, 0xac,
0x64, 0xa8, 0xde, 0xa3, 0x54, 0x38, 0x5d, 0x1e, 0x2a, 0x9f, 0xf8, 0xd1, 0xb3, 0x0e, 0xd6, 0x20,
0x9a, 0x42, 0x1c, 0x0e, 0xf8, 0xd4, 0x1f, 0x60, 0x4c, 0x8c, 0x83, 0x4a, 0xa6, 0x45, 0xa1, 0x51,
0x0e, 0xdb, 0xcf, 0xab, 0x29, 0x88, 0xd2, 0x0d, 0xd9, 0xa8, 0xb5, 0x94, 0xcf, 0x3f, 0x08, 0xd9,
0x48, 0xcf, 0x47, 0x9d, 0xa5, 0xa3, 0xd6, 0x72, 0x3e, 0xff, 0x40, 0x80, 0xb8, 0x78, 0xd9, 0x45,
0xd8, 0xa3, 0xa2, 0x31, 0x64, 0x45, 0xb6, 0x4a, 0x71, 0xc8, 0x41, 0x1c, 0x60, 0x18, 0x79, 0x11,
0xa6, 0x46, 0x72, 0xba, 0x2a, 0x52, 0x58, 0x0e, 0x54, 0xa6, 0xe1, 0xbe, 0x0a, 0x2b, 0xca, 0x5c,
0xcc, 0xde, 0xc9, 0x94, 0x66, 0xc3, 0x3e, 0x53, 0xbd, 0x93, 0xe2, 0xcb, 0x7d, 0x0b, 0xbb, 0x22,
0x3e, 0x89, 0x4f, 0x4f, 0xf3, 0xf4, 0x49, 0x9a, 0xd6, 0x26, 0xcc, 0xf5, 0x11, 0xae, 0xa6, 0x88,
0x2f, 0x37, 0xc2, 0x7a, 0x4e, 0x61, 0x4a, 0xfe, 0x6a, 0x11, 0x46, 0xbd, 0x58, 0x66, 0x0b, 0xf8,
0x9b, 0x9f, 0xc5, 0x80, 0x9e, 0x0c, 0x4f, 0x55, 0x0f, 0x14, 0x7e, 0x70, 0xcc, 0x0b, 0x3f, 0x8d,
0xe4, 0x85, 0x8a, 0xbf, 0x39, 0x26, 0x4d, 0xd3, 0x38, 0x95, 0xb7, 0xa7, 0xf8, 0x70, 0x0f, 0x61,
0xeb, 0xf8, 0x6a, 0x22, 0x72, 0x42, 0xa2, 0x5a, 0x23, 0x8f, 0x3f, 0x7e, 0xb8, 0x1f, 0x5b, 0x1d,
0x20, 0xd8, 0x25, 0x30, 0xcd, 0x31, 0x5a, 0x87, 0x6b, 0xe8, 0xcb, 0x15, 0x31, 0xfc, 0xe0, 0x19,
0x61, 0xab, 0x4c, 0x4d, 0xf7, 0xa0, 0x95, 0x3b, 0x2a, 0x84, 0x27, 0xfc, 0x95, 0x8a, 0x8e, 0x0a,
0x6b, 0xee, 0x74, 0x2d, 0x15, 0xbf, 0xd4, 0x2e, 0x89, 0xaf, 0x60, 0xcd, 0x14, 0xed, 0x1b, 0xcd,
0xfa, 0x7f, 0xec, 0x60, 0x85, 0x4c, 0x67, 0x60, 0xc7, 0x2c, 0xa5, 0xfe, 0xe0, 0x1b, 0x7d, 0x10,
0xff, 0x1e, 0xdc, 0x36, 0xfb, 0xa5, 0xae, 0x2c, 0x89, 0xfb, 0x07, 0xf8, 0x8c, 0x28, 0x1e, 0xf9,
0xff, 0x1f, 0xe4, 0x7f, 0x0f, 0x6e, 0x18, 0xf2, 0x5f, 0x51, 0x0c, 0xf7, 0xaf, 0x1c, 0xac, 0x22,
0xee, 0x0f, 0x83, 0x90, 0x59, 0x31, 0x07, 0xf7, 0x4c, 0xcc, 0x4f, 0x59, 0x27, 0xf0, 0x19, 0xd5,
0x4d, 0x9c, 0x1c, 0xf2, 0xc8, 0x67, 0x58, 0x3c, 0xa1, 0x51, 0x20, 0x06, 0x65, 0x31, 0x80, 0x46,
0x81, 0x1a, 0x12, 0x99, 0xc3, 0xc9, 0xc8, 0x4a, 0xd4, 0xde, 0xc7, 0x7b, 0x1a, 0x9b, 0x5e, 0xf0,
0xc4, 0x5f, 0xf3, 0xc4, 0x07, 0x3f, 0xd6, 0x71, 0xaf, 0xc7, 0x8f, 0xdc, 0x35, 0x04, 0xcb, 0x2f,
0xf7, 0x91, 0x78, 0x94, 0x36, 0x44, 0x93, 0xe7, 0xed, 0x35, 0x98, 0xa3, 0x18, 0x26, 0xcb, 0x53,
0xa6, 0xeb, 0xfb, 0x3e, 0xc7, 0xed, 0xe0, 0x98, 0x27, 0x51, 0xdc, 0x11, 0x34, 0x0c, 0x30, 0x77,
0x44, 0xa8, 0x47, 0x59, 0x05, 0xe5, 0xbf, 0xc9, 0x0d, 0x80, 0x30, 0xa0, 0x11, 0x0b, 0x7b, 0x21,
0x55, 0x1d, 0x08, 0x06, 0x84, 0xdf, 0x4b, 0x03, 0x9a, 0x65, 0xea, 0xf9, 0xae, 0xee, 0xa9, 0x4f,
0x9e, 0x61, 0xf0, 0xdb, 0x34, 0x63, 0xfe, 0x20, 0x51, 0x97, 0xa4, 0x06, 0x3c, 0xf8, 0xd9, 0xab,
0xb0, 0x74, 0x18, 0x8b, 0x70, 0xe0, 0x09, 0xbf, 0x05, 0x53, 0xf2, 0x18, 0xe6, 0x65, 0xfb, 0x35,
0xd9, 0x2c, 0xf5, 0x63, 0xa3, 0xe6, 0xdb, 0x5b, 0x63, 0xfa, 0xb4, 0xdd, 0xb5, 0xaf, 0xff, 0xf5,
0xdf, 0x7f, 0x52, 0x5b, 0x24, 0x8d, 0xfb, 0xe7, 0x6f, 0xdd, 0x3f, 0xa5, 0x0c, 0xdd, 0xed, 0x29,
0x2c, 0x5a, 0x1d, 0xb3, 0x64, 0xc7, 0xea, 0x7a, 0x2d, 0x34, 0xd2, 0xb6, 0x77, 0x27, 0xf6, 0xc4,
0xba, 0xd7, 0x91, 0xc5, 0x1a, 0x59, 0x95, 0x2c, 0xf2, 0x66, 0x58, 0xf2, 0x25, 0x2c, 0x7f, 0x80,
0x65, 0x78, 0x4d, 0x94, 0xdc, 0xcc, 0x89, 0x55, 0x36, 0x02, 0xb7, 0x6f, 0x8d, 0x47, 0x90, 0x0c,
0xb7, 0x91, 0xe1, 0x06, 0x59, 0xe3, 0x0c, 0x45, 0x99, 0x5f, 0xf3, 0x24, 0x19, 0xac, 0xc8, 0xd6,
0xc2, 0x17, 0xca, 0x73, 0x07, 0x79, 0x6e, 0x92, 0x75, 0xce, 0x33, 0x10, 0x0c, 0x72, 0xa6, 0x31,
0x56, 0x11, 0xcd, 0x56, 0x58, 0x72, 0x63, 0x6c, 0x8f, 0xac, 0x60, 0x79, 0xf3, 0x92, 0x1e, 0x5a,
0x7b, 0x95, 0xa7, 0x94, 0xe3, 0xea, 0x36, 0x5a, 0xf2, 0x13, 0x71, 0xb5, 0x54, 0x36, 0x6d, 0x93,
0x57, 0x2e, 0xef, 0x14, 0x17, 0x32, 0xdc, 0x9d, 0xb6, 0xa5, 0xdc, 0xfd, 0x16, 0x0a, 0x73, 0x83,
0xec, 0x48, 0x61, 0xac, 0x36, 0x72, 0xd5, 0xa8, 0x4e, 0xba, 0xd0, 0x34, 0xfb, 0x5f, 0xc9, 0x76,
0xc5, 0x4d, 0xa6, 0x99, 0xef, 0x54, 0x0f, 0x4a, 0x86, 0x2d, 0x64, 0x48, 0xc8, 0x8a, 0x64, 0xa8,
0xdb, 0x65, 0xc9, 0x57, 0xb0, 0x5c, 0xe8, 0x1d, 0x25, 0x6e, 0x61, 0xfb, 0x2a, 0xfa, 0x80, 0xdb,
0x2f, 0x4d, 0xc4, 0x91, 0x5c, 0x6f, 0x20, 0xd7, 0x96, 0xbb, 0x66, 0xec, 0xb2, 0xe2, 0xfc, 0x1d,
0xe7, 0x55, 0x92, 0xe1, 0x3e, 0x9b, 0x6d, 0x8e, 0x53, 0xf1, 0xbe, 0x79, 0x49, 0x8f, 0x64, 0x69,
0xaf, 0x15, 0x4f, 0x3c, 0xad, 0x19, 0xb6, 0x8e, 0x19, 0xcd, 0xb9, 0x18, 0xe6, 0x4d, 0xc3, 0x77,
0xb7, 0xba, 0xb9, 0x57, 0xf6, 0x17, 0xbb, 0x6d, 0xe4, 0xba, 0x4e, 0x48, 0x81, 0x6b, 0xcc, 0x12,
0x92, 0x59, 0xbd, 0xcf, 0x92, 0xa9, 0x6d, 0xd5, 0x15, 0xdd, 0xc7, 0x95, 0x2b, 0x35, 0xdb, 0x89,
0xc7, 0xae, 0x34, 0x66, 0x49, 0x46, 0x9e, 0xc3, 0x92, 0x70, 0x17, 0x2f, 0x7e, 0x67, 0x77, 0x91,
0xef, 0x96, 0x4b, 0x72, 0x9f, 0x61, 0x6e, 0xec, 0x67, 0x50, 0xd7, 0xf7, 0x31, 0x69, 0x19, 0x8b,
0xb0, 0x1a, 0x41, 0xdb, 0x63, 0xda, 0xfc, 0x94, 0xb5, 0xba, 0x8b, 0x72, 0x55, 0xa2, 0x69, 0x8f,
0x13, 0xfe, 0x11, 0x40, 0xde, 0xf7, 0x47, 0xae, 0x97, 0x28, 0x6b, 0xcd, 0xb5, 0xab, 0x86, 0xd4,
0x5f, 0x38, 0x20, 0xf9, 0x15, 0xb2, 0x64, 0x91, 0x57, 0xe7, 0x4d, 0x87, 0x1f, 0xd6, 0x79, 0x2b,
0x76, 0x0a, 0xb6, 0xc7, 0xb7, 0x88, 0xa9, 0x4d, 0x71, 0xd5, 0x61, 0xd3, 0x65, 0x26, 0xbe, 0x02,
0x71, 0x59, 0x18, 0xbd, 0x69, 0x3b, 0x55, 0x5c, 0x2a, 0x2f, 0x8b, 0x72, 0xa3, 0x59, 0xe9, 0xb2,
0xc8, 0xfb, 0xc9, 0xc8, 0x33, 0xfc, 0x0b, 0x2f, 0xa3, 0xb5, 0x8a, 0x98, 0xb4, 0xca, 0x7d, 0x66,
0xed, 0x1b, 0xe3, 0x86, 0xb3, 0x6a, 0xfb, 0x96, 0x99, 0x28, 0x1e, 0x2a, 0xb1, 0xe1, 0xa2, 0xa1,
0xca, 0xda, 0x70, 0xab, 0xef, 0xaa, 0x7d, 0xbd, 0x62, 0x44, 0x52, 0xdf, 0x40, 0xea, 0xcb, 0x64,
0x51, 0xbb, 0x44, 0xa4, 0x25, 0xf6, 0x44, 0xbf, 0x74, 0x5b, 0x7b, 0x52, 0x6c, 0x87, 0xb2, 0x7c,
0x60, 0xa9, 0x29, 0xaa, 0xe4, 0x03, 0x75, 0xdb, 0x13, 0xf9, 0x43, 0xbb, 0xbb, 0x4a, 0x75, 0x7b,
0xb8, 0x13, 0xdb, 0x33, 0x4a, 0xa7, 0x65, 0x6c, 0x0b, 0x87, 0x7b, 0x13, 0x39, 0x5f, 0x27, 0x5b,
0x45, 0xce, 0xb2, 0x1d, 0x84, 0x7c, 0xed, 0xc0, 0x5a, 0x45, 0xb3, 0x41, 0x2e, 0xc1, 0xf8, 0xd6,
0x88, 0x5c, 0x82, 0x49, 0xdd, 0x0a, 0x2e, 0x4a, 0xb0, 0xe3, 0xa2, 0x04, 0x7e, 0x10, 0x68, 0x09,
0x64, 0x62, 0xcd, 0x2d, 0xf3, 0xcf, 0x1c, 0xd8, 0xac, 0x6e, 0x2c, 0x20, 0x2f, 0xeb, 0xbf, 0x19,
0x99, 0xd4, 0xf2, 0xd0, 0xbe, 0x73, 0x19, 0x9a, 0x94, 0xe6, 0x65, 0x94, 0xe6, 0xa6, 0xdb, 0xe6,
0xd2, 0xa4, 0x88, 0x5b, 0x25, 0xd0, 0x05, 0x56, 0x63, 0xed, 0xa7, 0x7b, 0x62, 0xc4, 0x16, 0xd5,
0x1d, 0x0e, 0xed, 0xdb, 0x13, 0x30, 0x6c, 0xf7, 0x45, 0x36, 0xe4, 0x86, 0xe0, 0x7b, 0xb7, 0xee,
0x01, 0x90, 0x67, 0x34, 0x7f, 0x1a, 0xb7, 0xce, 0x68, 0xe9, 0xb5, 0xdf, 0x3a, 0xa3, 0xe5, 0x07,
0xf8, 0xd2, 0x19, 0x45, 0x66, 0xf8, 0x18, 0x4f, 0x3e, 0xc7, 0x63, 0x23, 0x9f, 0x02, 0x5a, 0xc5,
0xa3, 0x9e, 0x55, 0x1d, 0x1b, 0xbb, 0xd8, 0x5f, 0x72, 0x95, 0xe2, 0x85, 0x81, 0x6b, 0xcf, 0x83,
0x05, 0x85, 0x4e, 0xb6, 0x8a, 0x04, 0x14, 0xe5, 0xca, 0xd7, 0x5c, 0x77, 0x0b, 0x89, 0xae, 0xba,
0x4d, 0x93, 0x28, 0xa7, 0x79, 0x02, 0x0d, 0xe3, 0xe5, 0x92, 0x68, 0x27, 0x5b, 0x7e, 0xa8, 0x6d,
0x6f, 0x57, 0x8e, 0xd9, 0xae, 0xc4, 0x5d, 0xe6, 0x0c, 0x32, 0x44, 0xd0, 0x3c, 0x7e, 0x17, 0x16,
0xad, 0xc7, 0xc3, 0x5c, 0xf9, 0x55, 0xcf, 0x9b, 0xb9, 0xf2, 0x2b, 0x5f, 0x1c, 0x55, 0xa0, 0xe9,
0xa2, 0xf2, 0x33, 0x89, 0xa2, 0x79, 0x7d, 0x01, 0x75, 0xfd, 0x66, 0x97, 0xeb, 0xbf, 0xf8, 0x8c,
0x77, 0x19, 0x0f, 0x6b, 0x0f, 0x2e, 0xf8, 0xe4, 0x93, 0x78, 0x70, 0x22, 0xf5, 0x65, 0xbc, 0x48,
0xe5, 0xfa, 0x2a, 0x3f, 0xcb, 0xe5, 0xfa, 0xaa, 0x7a, 0xc2, 0xb2, 0xf4, 0xd5, 0x45, 0x04, 0xbd,
0x86, 0x14, 0x96, 0x0b, 0x2f, 0x41, 0x79, 0x58, 0x51, 0xfd, 0xee, 0x95, 0x87, 0x15, 0x63, 0x9e,
0x90, 0xec, 0xc0, 0x4d, 0xf0, 0xf3, 0xfb, 0xfd, 0xdc, 0xb6, 0x84, 0xbb, 0x17, 0xef, 0x24, 0x96,
0xdd, 0x5a, 0x0f, 0x42, 0x96, 0xdd, 0xda, 0x8f, 0x2a, 0x25, 0x77, 0x2f, 0x32, 0x45, 0xf2, 0x14,
0x16, 0x54, 0x81, 0x3e, 0x37, 0xda, 0xc2, 0xd3, 0x44, 0xbb, 0x55, 0x1e, 0x90, 0x54, 0x2d, 0xc3,
0xf5, 0x83, 0x00, 0xa9, 0xca, 0x8d, 0x30, 0xca, 0xf5, 0xf9, 0x46, 0x94, 0x2b, 0xfd, 0xf9, 0x46,
0x54, 0xd5, 0xf7, 0xad, 0x8d, 0x10, 0x9e, 0x4b, 0xf3, 0xf8, 0x7b, 0x07, 0xcb, 0x18, 0x93, 0xab,
0xed, 0xe4, 0xcd, 0x2b, 0x14, 0xe6, 0x85, 0x40, 0x6f, 0x5d, 0xb9, 0x94, 0xef, 0xde, 0x45, 0x31,
0x5d, 0x77, 0x57, 0x5d, 0xa6, 0x38, 0x2d, 0x10, 0xe8, 0xba, 0xae, 0xcf, 0x85, 0xfe, 0x5b, 0x47,
0xfc, 0xfd, 0xee, 0x04, 0xba, 0x64, 0x6f, 0x4a, 0x01, 0x94, 0xc0, 0xf7, 0xa7, 0xc6, 0x97, 0xe2,
0xde, 0x41, 0x71, 0x6f, 0xb9, 0xdb, 0x13, 0xc4, 0xe5, 0xc2, 0xfe, 0x3e, 0x6c, 0xeb, 0xaa, 0xbc,
0x45, 0xf7, 0xc3, 0x61, 0x14, 0x64, 0x79, 0x5e, 0x3a, 0xa6, 0x74, 0x9f, 0x1b, 0x4e, 0xb1, 0x58,
0x6b, 0xdf, 0x8f, 0x17, 0x72, 0x54, 0x88, 0xd1, 0xe3, 0xb4, 0x39, 0xf7, 0x04, 0x56, 0xd5, 0xbc,
0x0f, 0x43, 0x9f, 0xfd, 0xc2, 0x3c, 0x6f, 0x21, 0xcf, 0xb6, 0xbb, 0x61, 0xf2, 0xec, 0x85, 0x3e,
0xd3, 0x1c, 0x33, 0x7c, 0x64, 0xb5, 0xea, 0xb0, 0x66, 0xf2, 0x5d, 0x59, 0xa1, 0x35, 0x93, 0xef,
0xea, 0x92, 0xb1, 0x9d, 0x7c, 0x9f, 0x52, 0x26, 0x4a, 0xb8, 0x81, 0x64, 0x70, 0x0e, 0x2b, 0xc7,
0x63, 0x99, 0x1e, 0xff, 0xdc, 0x4c, 0x65, 0x0c, 0xe4, 0x22, 0xd3, 0xac, 0xc0, 0x94, 0x2f, 0xf6,
0x5c, 0xbc, 0x28, 0x9b, 0x15, 0x5a, 0x72, 0x73, 0x7c, 0xed, 0xb6, 0xcc, 0xb7, 0xb2, 0xb8, 0x6b,
0xf3, 0x35, 0x32, 0x24, 0xfc, 0xbb, 0x45, 0xce, 0x77, 0x04, 0xc4, 0xce, 0x92, 0xf0, 0xef, 0x5d,
0xb4, 0x17, 0xa8, 0xa8, 0xcb, 0x4e, 0x97, 0x22, 0xdd, 0x46, 0xc6, 0xdb, 0xee, 0x66, 0x39, 0x45,
0xe2, 0xbc, 0x39, 0xeb, 0xdf, 0x83, 0xb5, 0x42, 0xee, 0xfd, 0x82, 0x78, 0x5b, 0xe6, 0x5c, 0x48,
0xbc, 0x15, 0x73, 0x86, 0x79, 0x70, 0xa1, 0xd8, 0x4a, 0x6e, 0x57, 0xe5, 0x1b, 0x56, 0x2d, 0x73,
0x52, 0xe6, 0x23, 0xef, 0x0d, 0xb2, 0x59, 0x4a, 0x47, 0x90, 0xc2, 0x9b, 0x0e, 0xf9, 0x53, 0x07,
0xff, 0xc4, 0x60, 0x4c, 0xad, 0x97, 0xdc, 0xab, 0x4a, 0x78, 0xaf, 0x2c, 0x86, 0xf4, 0x27, 0xe4,
0x46, 0x31, 0x2b, 0x2e, 0x89, 0x73, 0x86, 0x15, 0x08, 0xb3, 0x62, 0x6b, 0xe5, 0xe4, 0x15, 0xa5,
0xdc, 0xb1, 0x49, 0x6b, 0x31, 0x15, 0x97, 0x59, 0xa5, 0xe2, 0xf4, 0x63, 0xfb, 0x0f, 0x89, 0x2d,
0x96, 0x77, 0x2a, 0x56, 0x7d, 0x15, 0xd6, 0x2f, 0x21, 0xeb, 0x5d, 0xb2, 0x5d, 0x58, 0x6f, 0x41,
0x04, 0x11, 0xd6, 0xe6, 0xc5, 0x5c, 0x2b, 0xac, 0x2d, 0x95, 0x9f, 0xad, 0xb0, 0xb6, 0x5c, 0x01,
0x2e, 0x85, 0xb5, 0x58, 0xe1, 0xc5, 0xcb, 0xf0, 0x64, 0x0e, 0xff, 0x8d, 0x91, 0xb7, 0xff, 0x2f,
0x00, 0x00, 0xff, 0xff, 0xb9, 0x45, 0x93, 0xa0, 0x96, 0x44, 0x00, 0x00,
0x17, 0x95, 0x8b, 0xbf, 0x0e, 0x24, 0xa5, 0x19, 0x6b, 0x27, 0x71, 0xaf, 0xc7, 0x53, 0xf2, 0x80,
0xf6, 0xfc, 0xa1, 0x2c, 0xd9, 0xaf, 0xf0, 0x91, 0x63, 0x31, 0xf0, 0x3e, 0x87, 0x93, 0x2d, 0x98,
0xf3, 0x93, 0xb0, 0xcd, 0xad, 0x46, 0xe4, 0xe3, 0xb3, 0x7e, 0x12, 0x7e, 0x4c, 0x87, 0xc4, 0x85,
0x45, 0x39, 0xd0, 0xee, 0xd1, 0x0b, 0xda, 0xc3, 0x98, 0x6f, 0xca, 0x5b, 0x10, 0xc3, 0x9f, 0x70,
0x10, 0xb9, 0x07, 0x2b, 0x49, 0x1a, 0x72, 0xf3, 0xcb, 0xdf, 0x06, 0xe6, 0x50, 0x9a, 0x65, 0x09,
0x57, 0xab, 0x73, 0x7f, 0x04, 0x37, 0x2a, 0x74, 0x21, 0x7d, 0xd4, 0x77, 0x61, 0xd9, 0x7e, 0x61,
0x50, 0x7e, 0x4a, 0x47, 0xad, 0xd6, 0x44, 0x6f, 0xa9, 0x6b, 0xd1, 0x91, 0xd1, 0x27, 0xe2, 0x78,
0x3e, 0xd3, 0x35, 0x2d, 0xf7, 0x4b, 0x58, 0xcf, 0x81, 0x87, 0x71, 0x74, 0x41, 0xd3, 0x8c, 0x5b,
0x1b, 0x81, 0xe9, 0x6e, 0x1a, 0xab, 0x82, 0x2c, 0xfe, 0xe6, 0x71, 0x1b, 0x8b, 0xa5, 0x19, 0xd4,
0x58, 0xcc, 0x71, 0x52, 0x9f, 0xa9, 0x5b, 0x0a, 0x7f, 0xf3, 0x38, 0x39, 0x44, 0x22, 0xb4, 0x8d,
0x63, 0xc2, 0x54, 0x17, 0x24, 0x8c, 0x73, 0x71, 0x9f, 0x62, 0xf8, 0x68, 0x8a, 0x22, 0xd7, 0xf8,
0xeb, 0xb0, 0x20, 0xd6, 0xc8, 0x67, 0xaa, 0xf5, 0xed, 0x58, 0xeb, 0x2b, 0x88, 0xe9, 0x41, 0x57,
0x43, 0xdd, 0xff, 0xae, 0x41, 0x03, 0x23, 0xd6, 0xf7, 0x29, 0xf3, 0xc3, 0xde, 0xf8, 0x58, 0x5a,
0xc4, 0xa0, 0x35, 0x1d, 0x83, 0xbe, 0x02, 0x8b, 0x66, 0x41, 0x64, 0xa8, 0x92, 0x59, 0xa3, 0x1c,
0x32, 0x24, 0xaf, 0xc2, 0x12, 0xa6, 0xd6, 0x39, 0x96, 0xb0, 0x99, 0x45, 0x84, 0x6a, 0x34, 0x3b,
0x11, 0x98, 0x29, 0x24, 0x02, 0x7c, 0x18, 0x83, 0xe9, 0x76, 0x16, 0x06, 0x3a, 0x4f, 0x40, 0xc8,
0x49, 0x18, 0x18, 0xc3, 0x38, 0x7b, 0xce, 0x18, 0xc6, 0xd9, 0x3c, 0x07, 0x4a, 0xa9, 0x78, 0x28,
0xc0, 0xf7, 0xae, 0x79, 0x34, 0xba, 0x86, 0x02, 0x3e, 0x09, 0xfb, 0xf8, 0x1a, 0x26, 0x8b, 0xdb,
0x75, 0x61, 0xb1, 0xe2, 0x2b, 0x4f, 0xd3, 0xc0, 0x4c, 0xd3, 0xf2, 0xa4, 0x6e, 0xc1, 0x4a, 0xea,
0xf6, 0x60, 0x21, 0x4e, 0x68, 0xd4, 0x96, 0x29, 0x76, 0x43, 0x44, 0x0f, 0x1c, 0xf4, 0x14, 0x21,
0xb2, 0x64, 0x82, 0x3a, 0xcf, 0x26, 0xc9, 0x4b, 0x6d, 0xc5, 0xd4, 0x8a, 0x8a, 0x51, 0x89, 0xe0,
0xd4, 0x55, 0x89, 0xa0, 0x7b, 0x80, 0x51, 0xb1, 0x62, 0x2c, 0xcd, 0xe7, 0x75, 0x98, 0x45, 0x35,
0x29, 0xcb, 0x59, 0xb7, 0xd2, 0x18, 0x69, 0x14, 0x9e, 0xc4, 0x71, 0xbf, 0x8f, 0x6f, 0x88, 0x38,
0x34, 0x89, 0xe8, 0x37, 0x60, 0x5e, 0xec, 0x8a, 0xb6, 0x9a, 0x39, 0xfc, 0xfe, 0x28, 0x70, 0xff,
0xcd, 0x01, 0x72, 0x32, 0x38, 0xed, 0x87, 0x93, 0x53, 0x9b, 0x3c, 0x41, 0x27, 0x30, 0x8d, 0x66,
0x22, 0xcc, 0x11, 0x7f, 0x17, 0x2c, 0x64, 0xba, 0x68, 0x21, 0xf9, 0x76, 0xce, 0x54, 0xe7, 0xe8,
0xb3, 0xe6, 0xe6, 0x73, 0x17, 0xdf, 0x0b, 0x69, 0xc4, 0xda, 0xb2, 0xd8, 0xc2, 0x5d, 0x3c, 0x02,
0x3e, 0x0a, 0xdc, 0x13, 0x58, 0xb3, 0x56, 0x26, 0x35, 0x7d, 0x1b, 0x1a, 0x42, 0x80, 0xa4, 0xe7,
0x77, 0x74, 0x35, 0x7c, 0x01, 0x61, 0xc7, 0x08, 0x1a, 0xa7, 0xaf, 0x3f, 0x71, 0x60, 0xfd, 0x24,
0xec, 0x0f, 0x7a, 0x3e, 0xa3, 0xbf, 0x04, 0x8d, 0xe5, 0xcb, 0x9f, 0xb2, 0x96, 0xaf, 0x34, 0x39,
0x9d, 0x6b, 0xd2, 0xfd, 0x99, 0x03, 0x1b, 0x05, 0x51, 0x74, 0x4c, 0x68, 0x1b, 0xd3, 0x88, 0xe2,
0x80, 0x44, 0x32, 0x98, 0xd6, 0x2c, 0xa6, 0xaf, 0xc0, 0x62, 0x3f, 0x8c, 0xc2, 0xfe, 0xa0, 0xdf,
0x16, 0xba, 0x17, 0x32, 0x35, 0x24, 0xf0, 0x18, 0xb7, 0x80, 0x23, 0xf9, 0xcf, 0x0d, 0xa4, 0x69,
0x89, 0x24, 0x80, 0x02, 0xe9, 0x0d, 0x58, 0xcf, 0xe3, 0xf6, 0xf6, 0x99, 0x1f, 0x46, 0xed, 0x5e,
0x9c, 0x65, 0x72, 0x8f, 0x49, 0x3e, 0x76, 0xe4, 0x87, 0xd1, 0x27, 0x71, 0x96, 0x19, 0x4e, 0x60,
0xd6, 0x74, 0x02, 0x3c, 0x80, 0x59, 0xf9, 0xec, 0xdc, 0xef, 0xd1, 0xf7, 0xe2, 0xfe, 0xe9, 0x8b,
0xd5, 0xfd, 0x6d, 0x68, 0x88, 0xba, 0x1b, 0xf3, 0xd3, 0x33, 0xaa, 0x76, 0x60, 0x01, 0x61, 0x4f,
0x10, 0x54, 0xb9, 0x0d, 0xff, 0xe5, 0x00, 0x39, 0xe4, 0xa1, 0x4c, 0x6f, 0x62, 0x7b, 0xe0, 0xae,
0x44, 0xe4, 0xcd, 0xb9, 0x85, 0xd5, 0x25, 0xe4, 0x23, 0xdb, 0xfc, 0xa6, 0x2c, 0xf3, 0xd3, 0xab,
0x99, 0xbe, 0x66, 0x71, 0xac, 0xe4, 0xc7, 0x5f, 0x85, 0xa5, 0x4b, 0xbf, 0xd7, 0xa3, 0x4c, 0x3f,
0xb1, 0xc9, 0x4a, 0xbc, 0x80, 0xaa, 0x1c, 0x5c, 0x2d, 0x78, 0xce, 0x58, 0xf0, 0x06, 0xac, 0x59,
0xeb, 0x95, 0xd1, 0xd0, 0x23, 0xd8, 0x14, 0xe0, 0x83, 0x5e, 0x6f, 0x62, 0xaf, 0xea, 0xfe, 0x75,
0x0d, 0xb6, 0x4a, 0xd3, 0x74, 0xd8, 0x60, 0x9b, 0xf1, 0x1d, 0xbd, 0xdc, 0xea, 0x09, 0xfb, 0xf2,
0x53, 0xce, 0x6a, 0xfd, 0x93, 0x03, 0xb3, 0x02, 0x34, 0x76, 0x37, 0x3e, 0x57, 0x0e, 0x41, 0x1a,
0x9c, 0xc8, 0x88, 0xbe, 0x3d, 0x19, 0x33, 0xf1, 0x3f, 0xf3, 0x59, 0x55, 0x78, 0x12, 0xf9, 0xa2,
0xfa, 0x5d, 0x58, 0x29, 0x22, 0x5c, 0xeb, 0xc9, 0x49, 0x54, 0x55, 0x3e, 0xb8, 0xa0, 0xc6, 0x33,
0xea, 0x4f, 0x1d, 0x58, 0x3e, 0x8c, 0xa3, 0x20, 0xe4, 0x37, 0xe6, 0xb1, 0x9f, 0xfa, 0xfd, 0x4c,
0xbe, 0xe4, 0x0b, 0x90, 0x2a, 0xbb, 0x6b, 0xc0, 0x88, 0x02, 0xe7, 0x2e, 0x40, 0xe7, 0x9c, 0x76,
0x9e, 0xb5, 0x65, 0xc5, 0x51, 0x3c, 0xff, 0x73, 0xc8, 0x7b, 0x61, 0x90, 0x91, 0x6f, 0xc1, 0x5a,
0x3e, 0xdc, 0xf6, 0xa3, 0xa0, 0x2d, 0xcb, 0x8d, 0xf8, 0xba, 0xa1, 0xf1, 0x0e, 0xa2, 0xe0, 0x20,
0x7b, 0x96, 0xf1, 0x58, 0x51, 0x57, 0xd9, 0xda, 0x96, 0x0b, 0x5f, 0xd6, 0xf0, 0x03, 0x04, 0xbb,
0xff, 0xe3, 0xe0, 0x0d, 0xa8, 0x56, 0x25, 0x77, 0x3b, 0x2f, 0xac, 0x61, 0xbd, 0xd5, 0xda, 0xb2,
0x5a, 0x61, 0xcb, 0x08, 0x4c, 0x87, 0x8c, 0xf6, 0xd5, 0xc5, 0xc2, 0x7f, 0x93, 0xf7, 0x60, 0x45,
0xaf, 0xb8, 0x9d, 0xa0, 0x5a, 0xe4, 0x31, 0xd9, 0xca, 0x13, 0x47, 0x4b, 0x6b, 0xde, 0x72, 0xa7,
0xa0, 0x46, 0x75, 0xbc, 0x66, 0x26, 0x72, 0xd4, 0x1d, 0xd4, 0xb6, 0xf4, 0x4f, 0xe2, 0x4b, 0x48,
0x4d, 0x3b, 0x03, 0x46, 0x03, 0x19, 0x2a, 0xeb, 0x6f, 0xf7, 0x3f, 0x1c, 0x58, 0x3e, 0x08, 0x02,
0x5c, 0xf7, 0x24, 0x6e, 0x42, 0xad, 0xb2, 0x76, 0xc5, 0x2a, 0xa7, 0x7e, 0xce, 0x55, 0xfe, 0xc2,
0x4e, 0x64, 0x84, 0x12, 0x5c, 0x17, 0x56, 0xf2, 0x75, 0x56, 0x6f, 0xaf, 0xfb, 0x0d, 0x20, 0x22,
0xbd, 0xb2, 0xd4, 0x51, 0xc4, 0xda, 0x80, 0x35, 0x0b, 0x4b, 0xfa, 0x9a, 0x0f, 0xe1, 0xee, 0x11,
0x65, 0x87, 0xe9, 0x30, 0x61, 0xb1, 0x0a, 0x67, 0xdf, 0xa7, 0x49, 0x9c, 0x85, 0xca, 0x73, 0xd1,
0x89, 0xbc, 0xcf, 0x3f, 0x3b, 0x70, 0x6f, 0x02, 0x42, 0x72, 0x09, 0x5f, 0x94, 0xeb, 0x4b, 0xbf,
0x61, 0xb6, 0xb7, 0x4c, 0x44, 0x65, 0x5f, 0x43, 0x64, 0x97, 0x81, 0x26, 0xd9, 0x7a, 0x17, 0x96,
0xec, 0xc1, 0x6b, 0xb9, 0x8a, 0x1e, 0xdc, 0xb9, 0x42, 0x88, 0x49, 0x6c, 0xee, 0x0e, 0x2c, 0x75,
0x2c, 0x12, 0x92, 0x51, 0x01, 0xea, 0x1e, 0xc2, 0x6b, 0x57, 0x72, 0x93, 0x6a, 0x1b, 0x99, 0xa1,
0xbb, 0x7f, 0x37, 0x0d, 0x5b, 0x9f, 0x85, 0xec, 0x3c, 0x48, 0xfd, 0x4b, 0x65, 0x7d, 0x93, 0x08,
0x59, 0x48, 0xde, 0x6b, 0xe5, 0x7a, 0xc3, 0x7d, 0x58, 0x8d, 0x23, 0x8a, 0x39, 0x46, 0x3b, 0xf1,
0xb3, 0xec, 0x32, 0x4e, 0xd5, 0x5d, 0xba, 0x1c, 0x47, 0x94, 0xe7, 0x19, 0xc7, 0x12, 0x5c, 0xb8,
0x8d, 0xa7, 0x8b, 0xb7, 0xf1, 0x0a, 0x4c, 0x25, 0x61, 0x24, 0xdf, 0x4c, 0xf8, 0x4f, 0x7e, 0x77,
0xb2, 0xd4, 0x0f, 0x0c, 0xca, 0xf2, 0xee, 0x44, 0xa8, 0xa6, 0x6b, 0x56, 0xf1, 0xe7, 0x0a, 0x55,
0x7c, 0x43, 0x27, 0xf3, 0x76, 0xd5, 0x62, 0x0f, 0x16, 0xe4, 0xcf, 0x36, 0xf3, 0xcf, 0x64, 0x0a,
0x04, 0x12, 0xf4, 0xc4, 0x3f, 0x33, 0xa2, 0x35, 0xb0, 0xa2, 0xb5, 0x5d, 0x80, 0x2e, 0xa5, 0x6d,
0x2b, 0x19, 0xaa, 0x77, 0x29, 0x15, 0x4e, 0x97, 0x87, 0xca, 0xa7, 0x7e, 0xf4, 0xac, 0x8d, 0x35,
0x88, 0x86, 0x10, 0x87, 0x03, 0x3e, 0xf5, 0xfb, 0x18, 0x13, 0xe3, 0xa0, 0x92, 0x69, 0x51, 0x68,
0x94, 0xc3, 0x0e, 0xf2, 0x6a, 0x0a, 0xa2, 0x74, 0x42, 0x36, 0x6c, 0x2e, 0xe5, 0xf3, 0x0f, 0x43,
0x36, 0xd4, 0xf3, 0x51, 0x67, 0xe9, 0xb0, 0xb9, 0x9c, 0xcf, 0x3f, 0x14, 0x20, 0x2e, 0x5e, 0x76,
0x19, 0x76, 0xa9, 0x68, 0x0c, 0x59, 0x91, 0xad, 0x52, 0x1c, 0x72, 0x18, 0x07, 0x18, 0x46, 0x5e,
0x86, 0xa9, 0x91, 0x9c, 0xae, 0x8a, 0x14, 0x96, 0x03, 0x95, 0x69, 0xb8, 0xf7, 0x61, 0x45, 0x99,
0x8b, 0xd9, 0x3b, 0x99, 0xd2, 0x6c, 0xd0, 0x63, 0xaa, 0x77, 0x52, 0x7c, 0xb9, 0x6f, 0x62, 0x57,
0xc4, 0x27, 0xf1, 0xd9, 0x59, 0x9e, 0x3e, 0x49, 0xd3, 0xda, 0x84, 0xd9, 0x1e, 0xc2, 0xd5, 0x14,
0xf1, 0xe5, 0x46, 0x58, 0xcf, 0x29, 0x4c, 0xc9, 0x5f, 0x2d, 0xc2, 0xa8, 0x1b, 0xcb, 0x6c, 0x01,
0x7f, 0xf3, 0xb3, 0x18, 0xd0, 0xd3, 0xc1, 0x99, 0xea, 0x81, 0xc2, 0x0f, 0x8e, 0x79, 0xe9, 0xa7,
0x91, 0xbc, 0x50, 0xf1, 0x37, 0xc7, 0xa4, 0x69, 0x1a, 0xa7, 0xf2, 0xf6, 0x14, 0x1f, 0xee, 0x11,
0x6c, 0x9d, 0x5c, 0x4f, 0x44, 0x4e, 0x48, 0x54, 0x6b, 0xe4, 0xf1, 0xc7, 0x0f, 0xf7, 0x63, 0xab,
0x03, 0x04, 0xbb, 0x04, 0x26, 0x39, 0x46, 0xeb, 0x30, 0x83, 0xbe, 0x5c, 0x11, 0xc3, 0x0f, 0x9e,
0x11, 0x36, 0xcb, 0xd4, 0x74, 0x0f, 0x5a, 0xb9, 0xa3, 0x42, 0x78, 0xc2, 0x5f, 0xa9, 0xe8, 0xa8,
0xb0, 0xe6, 0x4e, 0xd6, 0x52, 0xf1, 0x4b, 0xed, 0x92, 0xf8, 0x0a, 0xd6, 0x4c, 0xd1, 0x5e, 0x6a,
0xd6, 0xff, 0x63, 0x07, 0x2b, 0x64, 0x3a, 0x03, 0x3b, 0x61, 0x29, 0xf5, 0xfb, 0x2f, 0xf5, 0x41,
0xfc, 0x7b, 0x70, 0xdb, 0xec, 0x97, 0xba, 0xb6, 0x24, 0xee, 0x1f, 0xe0, 0x33, 0xa2, 0x78, 0xe4,
0xff, 0x7f, 0x90, 0xff, 0x5d, 0xb8, 0x69, 0xc8, 0x7f, 0x4d, 0x31, 0xdc, 0xbf, 0x72, 0xb0, 0x8a,
0x78, 0x30, 0x08, 0x42, 0x66, 0xc5, 0x1c, 0xdc, 0x33, 0x31, 0x3f, 0x65, 0xed, 0xc0, 0x67, 0x54,
0x37, 0x71, 0x72, 0xc8, 0xfb, 0x3e, 0xc3, 0xe2, 0x09, 0x8d, 0x02, 0x31, 0x28, 0x8b, 0x01, 0x34,
0x0a, 0xd4, 0x90, 0xc8, 0x1c, 0x4e, 0x87, 0x56, 0xa2, 0xf6, 0x1e, 0xde, 0xd3, 0xd8, 0xf4, 0x82,
0x27, 0x7e, 0xc6, 0x13, 0x1f, 0xfc, 0x58, 0xc7, 0xdd, 0x2e, 0x3f, 0x72, 0x33, 0x08, 0x96, 0x5f,
0xee, 0xa1, 0x78, 0x94, 0x36, 0x44, 0x93, 0xe7, 0xed, 0x3e, 0xcc, 0x52, 0x0c, 0x93, 0x8b, 0xaf,
0xdb, 0x06, 0xae, 0xc4, 0x70, 0x9f, 0x03, 0xe4, 0x50, 0xee, 0x86, 0x50, 0x8b, 0xb2, 0x06, 0xca,
0x7f, 0x93, 0x9b, 0x00, 0x61, 0x40, 0x23, 0x16, 0x76, 0x43, 0xaa, 0xfa, 0x0f, 0x0c, 0x08, 0xbf,
0x95, 0xfa, 0x34, 0xcb, 0xd4, 0xe3, 0x5d, 0xdd, 0x53, 0x9f, 0x3c, 0xbf, 0xe0, 0x77, 0x69, 0xc6,
0xfc, 0x7e, 0xa2, 0xae, 0x48, 0x0d, 0x78, 0xf8, 0xb3, 0xfb, 0xb0, 0x74, 0x14, 0x8b, 0x60, 0xe0,
0x09, 0xbf, 0x03, 0x53, 0xf2, 0x18, 0xe6, 0x64, 0xf3, 0x35, 0xd9, 0x2c, 0x75, 0x63, 0xa3, 0xde,
0x5b, 0x5b, 0x23, 0xba, 0xb4, 0xdd, 0xb5, 0xaf, 0xff, 0xf5, 0xdf, 0x7f, 0x52, 0x5b, 0x24, 0x0b,
0x0f, 0x2e, 0xde, 0x7c, 0x70, 0x46, 0x19, 0x3a, 0xdb, 0x33, 0x58, 0xb4, 0xfa, 0x65, 0xc9, 0x8e,
0xd5, 0xf3, 0x5a, 0x68, 0xa3, 0x6d, 0xed, 0x8e, 0xed, 0x88, 0x75, 0x6f, 0x20, 0x8b, 0x35, 0xb2,
0x2a, 0x59, 0xe4, 0xad, 0xb0, 0xe4, 0x4b, 0x58, 0xfe, 0x00, 0x8b, 0xf0, 0x9a, 0x28, 0xd9, 0xcb,
0x89, 0x55, 0xb6, 0x01, 0xb7, 0x6e, 0x8d, 0x46, 0x90, 0x0c, 0xb7, 0x91, 0xe1, 0x06, 0x59, 0xe3,
0x0c, 0x45, 0x91, 0x5f, 0xf3, 0x24, 0x19, 0xac, 0xc8, 0xc6, 0xc2, 0x17, 0xca, 0x73, 0x07, 0x79,
0x6e, 0x92, 0x75, 0xce, 0x33, 0x10, 0x0c, 0x72, 0xa6, 0x31, 0xd6, 0x10, 0xcd, 0x46, 0x58, 0x72,
0x73, 0x64, 0x87, 0xac, 0x60, 0xb9, 0x77, 0x45, 0x07, 0xad, 0xbd, 0xca, 0x33, 0xca, 0x71, 0x75,
0x13, 0x2d, 0xf9, 0x89, 0xb8, 0x58, 0x2a, 0x5b, 0xb6, 0xc9, 0x6b, 0x57, 0xf7, 0x89, 0x0b, 0x19,
0xee, 0x4e, 0xda, 0x50, 0xee, 0x7e, 0x03, 0x85, 0xb9, 0x49, 0x76, 0xa4, 0x30, 0x56, 0x13, 0xb9,
0x6a, 0x53, 0x27, 0x1d, 0x68, 0x98, 0xdd, 0xaf, 0x64, 0xbb, 0xe2, 0x1e, 0xd3, 0xcc, 0x77, 0xaa,
0x07, 0x25, 0xc3, 0x26, 0x32, 0x24, 0x64, 0x45, 0x32, 0xd4, 0xcd, 0xb2, 0xe4, 0x2b, 0x58, 0x2e,
0x74, 0x8e, 0x12, 0xb7, 0xb0, 0x7d, 0x15, 0x5d, 0xc0, 0xad, 0x57, 0xc6, 0xe2, 0x48, 0xae, 0x37,
0x91, 0x6b, 0xd3, 0x5d, 0x33, 0x76, 0x59, 0x71, 0xfe, 0x8e, 0x73, 0x9f, 0x64, 0xb8, 0xcf, 0x66,
0x93, 0xe3, 0x44, 0xbc, 0xf7, 0xae, 0xe8, 0x90, 0x2c, 0xed, 0xb5, 0xe2, 0x89, 0xa7, 0x35, 0xc3,
0xc6, 0x31, 0xa3, 0x35, 0x17, 0x83, 0xbc, 0x49, 0xf8, 0xee, 0x56, 0xb7, 0xf6, 0xca, 0xee, 0x62,
0xb7, 0x85, 0x5c, 0xd7, 0x09, 0x29, 0x70, 0x8d, 0x59, 0x42, 0x32, 0xab, 0xf3, 0x59, 0x32, 0xb5,
0xad, 0xba, 0xa2, 0xf7, 0xb8, 0x72, 0xa5, 0x66, 0x33, 0xf1, 0xc8, 0x95, 0xc6, 0x2c, 0xc9, 0xc8,
0x73, 0x58, 0x12, 0xee, 0xe2, 0xc5, 0xef, 0xec, 0x2e, 0xf2, 0xdd, 0x72, 0x49, 0xee, 0x33, 0xcc,
0x8d, 0xfd, 0x0c, 0xea, 0xfa, 0x36, 0x26, 0x4d, 0x63, 0x11, 0x56, 0x1b, 0x68, 0x6b, 0x44, 0x93,
0x9f, 0xb2, 0x56, 0x77, 0x51, 0xae, 0x4a, 0xb4, 0xec, 0x71, 0xc2, 0x3f, 0x02, 0xc8, 0xbb, 0xfe,
0xc8, 0x8d, 0x12, 0x65, 0xad, 0xb9, 0x56, 0xd5, 0x90, 0xfa, 0xfb, 0x06, 0x24, 0xbf, 0x42, 0x96,
0x2c, 0xf2, 0xea, 0xbc, 0xe9, 0xe0, 0xc3, 0x3a, 0x6f, 0xc5, 0x3e, 0xc1, 0xd6, 0xe8, 0x06, 0x31,
0xb5, 0x29, 0xae, 0x3a, 0x6c, 0xba, 0xc8, 0xc4, 0x57, 0x20, 0x2e, 0x0b, 0xa3, 0x33, 0x6d, 0xa7,
0x8a, 0x4b, 0xe5, 0x65, 0x51, 0x6e, 0x33, 0x2b, 0x5d, 0x16, 0x79, 0x37, 0x19, 0x79, 0x86, 0x7f,
0xdf, 0x65, 0x34, 0x56, 0x11, 0x93, 0x56, 0xb9, 0xcb, 0xac, 0x75, 0x73, 0xd4, 0x70, 0x56, 0x6d,
0xdf, 0x32, 0x0f, 0xc5, 0x43, 0x25, 0x36, 0x5c, 0xb4, 0x53, 0x59, 0x1b, 0x6e, 0x75, 0x5d, 0xb5,
0x6e, 0x54, 0x8c, 0x48, 0xea, 0x1b, 0x48, 0x7d, 0x99, 0x2c, 0x6a, 0x97, 0x88, 0xb4, 0xc4, 0x9e,
0xe8, 0x77, 0x6e, 0x6b, 0x4f, 0x8a, 0xcd, 0x50, 0x96, 0x0f, 0x2c, 0xb5, 0x44, 0x95, 0x7c, 0xa0,
0x6e, 0x7a, 0x22, 0x7f, 0x68, 0xf7, 0x56, 0xa9, 0x5e, 0x0f, 0x77, 0x6c, 0x73, 0x46, 0xe9, 0xb4,
0x8c, 0x6c, 0xe0, 0x70, 0xf7, 0x90, 0xf3, 0x0d, 0xb2, 0x55, 0xe4, 0x2c, 0x9b, 0x41, 0xc8, 0xd7,
0x0e, 0xac, 0x55, 0xb4, 0x1a, 0xe4, 0x12, 0x8c, 0x6e, 0x8c, 0xc8, 0x25, 0x18, 0xd7, 0xab, 0xe0,
0xa2, 0x04, 0x3b, 0x2e, 0x4a, 0xe0, 0x07, 0x81, 0x96, 0x40, 0xa6, 0xd5, 0xdc, 0x32, 0xff, 0xcc,
0x81, 0xcd, 0xea, 0xb6, 0x02, 0xf2, 0xaa, 0xfe, 0x8b, 0x91, 0x71, 0x0d, 0x0f, 0xad, 0x3b, 0x57,
0xa1, 0x49, 0x69, 0x5e, 0x45, 0x69, 0xf6, 0xdc, 0x16, 0x97, 0x26, 0x45, 0xdc, 0x2a, 0x81, 0x2e,
0xb1, 0x16, 0x6b, 0x3f, 0xdc, 0x13, 0x23, 0xb6, 0xa8, 0xee, 0x6f, 0x68, 0xdd, 0x1e, 0x83, 0x61,
0xbb, 0x2f, 0xb2, 0x21, 0x37, 0x04, 0x5f, 0xbb, 0x75, 0x07, 0x80, 0x3c, 0xa3, 0xf9, 0xc3, 0xb8,
0x75, 0x46, 0x4b, 0x6f, 0xfd, 0xd6, 0x19, 0x2d, 0x3f, 0xbf, 0x97, 0xce, 0x28, 0x32, 0xc3, 0xa7,
0x78, 0xf2, 0x39, 0x1e, 0x1b, 0xf9, 0x10, 0xd0, 0x2c, 0x1e, 0xf5, 0xac, 0xea, 0xd8, 0xd8, 0xa5,
0xfe, 0x92, 0xab, 0x14, 0xef, 0x0b, 0x5c, 0x7b, 0x1e, 0xcc, 0x2b, 0x74, 0xb2, 0x55, 0x24, 0xa0,
0x28, 0x57, 0xbe, 0xe5, 0xba, 0x5b, 0x48, 0x74, 0xd5, 0x6d, 0x98, 0x44, 0x39, 0xcd, 0x53, 0x58,
0x30, 0xde, 0x2d, 0x89, 0x76, 0xb2, 0xe5, 0x67, 0xda, 0xd6, 0x76, 0xe5, 0x98, 0xed, 0x4a, 0xdc,
0x65, 0xce, 0x20, 0x43, 0x04, 0xcd, 0xe3, 0x77, 0x61, 0xd1, 0x7a, 0x3a, 0xcc, 0x95, 0x5f, 0xf5,
0xb8, 0x99, 0x2b, 0xbf, 0xf2, 0xbd, 0x51, 0x05, 0x9a, 0x2e, 0x2a, 0x3f, 0x93, 0x28, 0x9a, 0xd7,
0x17, 0x50, 0xd7, 0x2f, 0x76, 0xb9, 0xfe, 0x8b, 0x8f, 0x78, 0x57, 0xf1, 0xb0, 0xf6, 0xe0, 0x92,
0x4f, 0x3e, 0x8d, 0xfb, 0xa7, 0x52, 0x5f, 0xc6, 0x7b, 0x54, 0xae, 0xaf, 0xf2, 0xa3, 0x5c, 0xae,
0xaf, 0xaa, 0x07, 0x2c, 0x4b, 0x5f, 0x1d, 0x44, 0xd0, 0x6b, 0x48, 0x61, 0xb9, 0xf0, 0x0e, 0x94,
0x87, 0x15, 0xd5, 0xaf, 0x5e, 0x79, 0x58, 0x31, 0xe2, 0x01, 0xc9, 0x0e, 0xdc, 0x04, 0x3f, 0xbf,
0xd7, 0xcb, 0x6d, 0x4b, 0xb8, 0x7b, 0xf1, 0x4a, 0x62, 0xd9, 0xad, 0xf5, 0x1c, 0x64, 0xd9, 0xad,
0xfd, 0xa4, 0x52, 0x72, 0xf7, 0x22, 0x51, 0x24, 0x4f, 0x61, 0x5e, 0x95, 0xe7, 0x73, 0xa3, 0x2d,
0x3c, 0x4c, 0xb4, 0x9a, 0xe5, 0x01, 0x49, 0xd5, 0x32, 0x5c, 0x3f, 0x08, 0x90, 0xaa, 0xdc, 0x08,
0xa3, 0x58, 0x9f, 0x6f, 0x44, 0xb9, 0xce, 0x9f, 0x6f, 0x44, 0x55, 0x75, 0xdf, 0xda, 0x08, 0xe1,
0xb9, 0x34, 0x8f, 0xbf, 0x77, 0xb0, 0x88, 0x31, 0xbe, 0xd6, 0x4e, 0xde, 0xb8, 0x46, 0x59, 0x5e,
0x08, 0xf4, 0xe6, 0xb5, 0x0b, 0xf9, 0xee, 0x5d, 0x14, 0xd3, 0x75, 0x77, 0xd5, 0x65, 0x8a, 0xd3,
0x02, 0x81, 0xae, 0xab, 0xfa, 0x5c, 0xe8, 0xbf, 0x75, 0xc4, 0x5f, 0xef, 0x8e, 0xa1, 0x4b, 0xf6,
0x27, 0x14, 0x40, 0x09, 0xfc, 0x60, 0x62, 0x7c, 0x29, 0xee, 0x1d, 0x14, 0xf7, 0x96, 0xbb, 0x3d,
0x46, 0x5c, 0x2e, 0xec, 0xef, 0xc3, 0xb6, 0xae, 0xc9, 0x5b, 0x74, 0x3f, 0x1c, 0x44, 0x41, 0x96,
0xe7, 0xa5, 0x23, 0x0a, 0xf7, 0xb9, 0xe1, 0x14, 0x4b, 0xb5, 0xf6, 0xfd, 0x78, 0x29, 0x47, 0x85,
0x18, 0x5d, 0x4e, 0x9b, 0x73, 0x4f, 0x60, 0x55, 0xcd, 0xfb, 0x30, 0xf4, 0xd9, 0x2f, 0xcc, 0xf3,
0x16, 0xf2, 0x6c, 0xb9, 0x1b, 0x26, 0xcf, 0x6e, 0xe8, 0x33, 0xcd, 0x31, 0xc3, 0x27, 0x56, 0xab,
0x0a, 0x6b, 0x26, 0xdf, 0x95, 0xf5, 0x59, 0x33, 0xf9, 0xae, 0x2e, 0x18, 0xdb, 0xc9, 0xf7, 0x19,
0x65, 0xa2, 0x80, 0x1b, 0x48, 0x06, 0x17, 0xb0, 0x72, 0x32, 0x92, 0xe9, 0xc9, 0xcf, 0xcd, 0x54,
0xc6, 0x40, 0x2e, 0x32, 0xcd, 0x0a, 0x4c, 0xf9, 0x62, 0x2f, 0xc4, 0x7b, 0xb2, 0x59, 0x9f, 0x25,
0x7b, 0xa3, 0x2b, 0xb7, 0x65, 0xbe, 0x95, 0xa5, 0x5d, 0x9b, 0xaf, 0x91, 0x21, 0xe1, 0x5f, 0x2d,
0x72, 0xbe, 0x43, 0x20, 0x76, 0x96, 0x84, 0x7f, 0xed, 0xa2, 0xbd, 0x40, 0x45, 0x55, 0x76, 0xb2,
0x14, 0xe9, 0x36, 0x32, 0xde, 0x76, 0x37, 0xcb, 0x29, 0x12, 0xe7, 0xcd, 0x59, 0xff, 0x1e, 0xac,
0x15, 0x72, 0xef, 0x17, 0xc4, 0xdb, 0x32, 0xe7, 0x42, 0xe2, 0xad, 0x98, 0x33, 0xcc, 0x83, 0x0b,
0xa5, 0x56, 0x72, 0xbb, 0x2a, 0xdf, 0xb0, 0x2a, 0x99, 0xe3, 0x32, 0x1f, 0x79, 0x6f, 0x90, 0xcd,
0x52, 0x3a, 0x82, 0x14, 0xde, 0x70, 0xc8, 0x9f, 0x3a, 0xf8, 0x07, 0x06, 0x23, 0x2a, 0xbd, 0xe4,
0x5e, 0x55, 0xc2, 0x7b, 0x6d, 0x31, 0xa4, 0x3f, 0x21, 0x37, 0x8b, 0x59, 0x71, 0x49, 0x9c, 0x73,
0xac, 0x40, 0x98, 0xf5, 0x5a, 0x2b, 0x27, 0xaf, 0x28, 0xe4, 0x8e, 0x4c, 0x5a, 0x8b, 0xa9, 0xb8,
0xcc, 0x2a, 0x15, 0xa7, 0x1f, 0xdb, 0x7f, 0x46, 0x6c, 0xb1, 0xbc, 0x53, 0xb1, 0xea, 0xeb, 0xb0,
0x7e, 0x05, 0x59, 0xef, 0x92, 0xed, 0xc2, 0x7a, 0x0b, 0x22, 0x88, 0xb0, 0xd6, 0x28, 0xc4, 0x9a,
0x61, 0x6d, 0xa9, 0xf8, 0x6c, 0x85, 0xb5, 0xe5, 0xfa, 0x6f, 0x29, 0xac, 0xf5, 0x39, 0x0a, 0x5e,
0x86, 0xa7, 0xb3, 0xf8, 0x2f, 0x8c, 0xbc, 0xf5, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x02, 0x27,
0x05, 0x6d, 0x94, 0x44, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -6146,6 +6148,155 @@ type GoCryptoTraderServer interface {
GetAuditEvent(context.Context, *GetAuditEventRequest) (*GetAuditEventResponse, error)
}
// UnimplementedGoCryptoTraderServer can be embedded to have forward compatible implementations.
type UnimplementedGoCryptoTraderServer struct {
}
func (*UnimplementedGoCryptoTraderServer) GetInfo(ctx context.Context, req *GetInfoRequest) (*GetInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetSubsystems(ctx context.Context, req *GetSubsystemsRequest) (*GetSusbsytemsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSubsystems not implemented")
}
func (*UnimplementedGoCryptoTraderServer) EnableSubsystem(ctx context.Context, req *GenericSubsystemRequest) (*GenericSubsystemResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method EnableSubsystem not implemented")
}
func (*UnimplementedGoCryptoTraderServer) DisableSubsystem(ctx context.Context, req *GenericSubsystemRequest) (*GenericSubsystemResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DisableSubsystem not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetRPCEndpoints(ctx context.Context, req *GetRPCEndpointsRequest) (*GetRPCEndpointsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRPCEndpoints not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetCommunicationRelayers(ctx context.Context, req *GetCommunicationRelayersRequest) (*GetCommunicationRelayersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCommunicationRelayers not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetExchanges(ctx context.Context, req *GetExchangesRequest) (*GetExchangesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetExchanges not implemented")
}
func (*UnimplementedGoCryptoTraderServer) DisableExchange(ctx context.Context, req *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DisableExchange not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetExchangeInfo(ctx context.Context, req *GenericExchangeNameRequest) (*GetExchangeInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetExchangeInfo not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetExchangeOTPCode(ctx context.Context, req *GenericExchangeNameRequest) (*GetExchangeOTPReponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetExchangeOTPCode not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetExchangeOTPCodes(ctx context.Context, req *GetExchangeOTPsRequest) (*GetExchangeOTPsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetExchangeOTPCodes not implemented")
}
func (*UnimplementedGoCryptoTraderServer) EnableExchange(ctx context.Context, req *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method EnableExchange not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetTicker(ctx context.Context, req *GetTickerRequest) (*TickerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetTicker not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetTickers(ctx context.Context, req *GetTickersRequest) (*GetTickersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetTickers not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetOrderbook(ctx context.Context, req *GetOrderbookRequest) (*OrderbookResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetOrderbook not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetOrderbooks(ctx context.Context, req *GetOrderbooksRequest) (*GetOrderbooksResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetOrderbooks not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetAccountInfo(ctx context.Context, req *GetAccountInfoRequest) (*GetAccountInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAccountInfo not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetConfig(ctx context.Context, req *GetConfigRequest) (*GetConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetPortfolio(ctx context.Context, req *GetPortfolioRequest) (*GetPortfolioResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPortfolio not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetPortfolioSummary(ctx context.Context, req *GetPortfolioSummaryRequest) (*GetPortfolioSummaryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPortfolioSummary not implemented")
}
func (*UnimplementedGoCryptoTraderServer) AddPortfolioAddress(ctx context.Context, req *AddPortfolioAddressRequest) (*AddPortfolioAddressResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddPortfolioAddress not implemented")
}
func (*UnimplementedGoCryptoTraderServer) RemovePortfolioAddress(ctx context.Context, req *RemovePortfolioAddressRequest) (*RemovePortfolioAddressResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemovePortfolioAddress not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetForexProviders(ctx context.Context, req *GetForexProvidersRequest) (*GetForexProvidersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetForexProviders not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetForexRates(ctx context.Context, req *GetForexRatesRequest) (*GetForexRatesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetForexRates not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetOrders(ctx context.Context, req *GetOrdersRequest) (*GetOrdersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetOrders not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetOrder(ctx context.Context, req *GetOrderRequest) (*OrderDetails, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetOrder not implemented")
}
func (*UnimplementedGoCryptoTraderServer) SubmitOrder(ctx context.Context, req *SubmitOrderRequest) (*SubmitOrderResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SubmitOrder not implemented")
}
func (*UnimplementedGoCryptoTraderServer) SimulateOrder(ctx context.Context, req *SimulateOrderRequest) (*SimulateOrderResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SimulateOrder not implemented")
}
func (*UnimplementedGoCryptoTraderServer) WhaleBomb(ctx context.Context, req *WhaleBombRequest) (*SimulateOrderResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method WhaleBomb not implemented")
}
func (*UnimplementedGoCryptoTraderServer) CancelOrder(ctx context.Context, req *CancelOrderRequest) (*CancelOrderResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CancelOrder not implemented")
}
func (*UnimplementedGoCryptoTraderServer) CancelAllOrders(ctx context.Context, req *CancelAllOrdersRequest) (*CancelAllOrdersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CancelAllOrders not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetEvents(ctx context.Context, req *GetEventsRequest) (*GetEventsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetEvents not implemented")
}
func (*UnimplementedGoCryptoTraderServer) AddEvent(ctx context.Context, req *AddEventRequest) (*AddEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddEvent not implemented")
}
func (*UnimplementedGoCryptoTraderServer) RemoveEvent(ctx context.Context, req *RemoveEventRequest) (*RemoveEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveEvent not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddresses(ctx context.Context, req *GetCryptocurrencyDepositAddressesRequest) (*GetCryptocurrencyDepositAddressesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCryptocurrencyDepositAddresses not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddress(ctx context.Context, req *GetCryptocurrencyDepositAddressRequest) (*GetCryptocurrencyDepositAddressResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCryptocurrencyDepositAddress not implemented")
}
func (*UnimplementedGoCryptoTraderServer) WithdrawCryptocurrencyFunds(ctx context.Context, req *WithdrawCurrencyRequest) (*WithdrawResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method WithdrawCryptocurrencyFunds not implemented")
}
func (*UnimplementedGoCryptoTraderServer) WithdrawFiatFunds(ctx context.Context, req *WithdrawCurrencyRequest) (*WithdrawResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method WithdrawFiatFunds not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetLoggerDetails(ctx context.Context, req *GetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLoggerDetails not implemented")
}
func (*UnimplementedGoCryptoTraderServer) SetLoggerDetails(ctx context.Context, req *SetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetLoggerDetails not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetExchangePairs(ctx context.Context, req *GetExchangePairsRequest) (*GetExchangePairsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetExchangePairs not implemented")
}
func (*UnimplementedGoCryptoTraderServer) EnableExchangePair(ctx context.Context, req *ExchangePairRequest) (*GenericExchangeNameResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method EnableExchangePair not implemented")
}
func (*UnimplementedGoCryptoTraderServer) DisableExchangePair(ctx context.Context, req *ExchangePairRequest) (*GenericExchangeNameResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DisableExchangePair not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetOrderbookStream(req *GetOrderbookStreamRequest, srv GoCryptoTrader_GetOrderbookStreamServer) error {
return status.Errorf(codes.Unimplemented, "method GetOrderbookStream not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetExchangeOrderbookStream(req *GetExchangeOrderbookStreamRequest, srv GoCryptoTrader_GetExchangeOrderbookStreamServer) error {
return status.Errorf(codes.Unimplemented, "method GetExchangeOrderbookStream not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetTickerStream(req *GetTickerStreamRequest, srv GoCryptoTrader_GetTickerStreamServer) error {
return status.Errorf(codes.Unimplemented, "method GetTickerStream not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetExchangeTickerStream(req *GetExchangeTickerStreamRequest, srv GoCryptoTrader_GetExchangeTickerStreamServer) error {
return status.Errorf(codes.Unimplemented, "method GetExchangeTickerStream not implemented")
}
func (*UnimplementedGoCryptoTraderServer) GetAuditEvent(ctx context.Context, req *GetAuditEventRequest) (*GetAuditEventResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAuditEvent not implemented")
}
func RegisterGoCryptoTraderServer(s *grpc.Server, srv GoCryptoTraderServer) {
s.RegisterService(&_GoCryptoTrader_serviceDesc, srv)
}

File diff suppressed because it is too large Load Diff

View File

@@ -518,10 +518,10 @@ message GetAuditEventRequest {
}
message GetAuditEventResponse {
repeated audit_event events = 1;
repeated AuditEvent events = 1;
}
message audit_event {
message AuditEvent {
string type = 1 ;
string identifier = 2;
string message = 3;

View File

@@ -1283,6 +1283,23 @@
"gctrpcAddPortfolioAddressResponse": {
"type": "object"
},
"gctrpcAuditEvent": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"identifier": {
"type": "string"
},
"message": {
"type": "string"
},
"timestamp": {
"type": "string"
}
}
},
"gctrpcCancelAllOrdersRequest": {
"type": "object",
"properties": {
@@ -1498,7 +1515,7 @@
"events": {
"type": "array",
"items": {
"$ref": "#/definitions/gctrpcaudit_event"
"$ref": "#/definitions/gctrpcAuditEvent"
}
}
}
@@ -2381,23 +2398,6 @@
}
}
},
"gctrpcaudit_event": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"identifier": {
"type": "string"
},
"message": {
"type": "string"
},
"timestamp": {
"type": "string"
}
}
},
"protobufAny": {
"type": "object",
"properties": {

14
main.go
View File

@@ -36,8 +36,6 @@ func main() {
flag.BoolVar(&settings.EnableCommsRelayer, "enablecommsrelayer", true, "enables available communications relayer")
flag.BoolVar(&settings.Verbose, "verbose", false, "increases logging verbosity for GoCryptoTrader")
flag.BoolVar(&settings.EnableExchangeSyncManager, "syncmanager", true, "enables to exchange sync manager")
flag.BoolVar(&settings.EnableTickerSyncing, "tickersync", true, "enables ticker syncing for all enabled exchanges")
flag.BoolVar(&settings.EnableOrderbookSyncing, "orderbooksync", true, "enables orderbook syncing for all enabled exchanges")
flag.BoolVar(&settings.EnableWebsocketRoutine, "websocketroutine", true, "enables the websocket routine for all loaded exchanges")
flag.BoolVar(&settings.EnableCoinmarketcapAnalysis, "coinmarketcap", false, "overrides config and runs currency analysis")
flag.BoolVar(&settings.EnableEventManager, "eventmanager", true, "enables the event manager")
@@ -51,6 +49,15 @@ func main() {
flag.IntVar(&settings.DispatchMaxWorkerAmount, "dispatchworkers", dispatch.DefaultMaxWorkers, "sets the dispatch package max worker generation limit")
flag.IntVar(&settings.DispatchJobsLimit, "dispatchjobslimit", dispatch.DefaultJobsLimit, "sets the dispatch package max jobs limit")
// Exchange syncer settings
flag.BoolVar(&settings.EnableTickerSyncing, "tickersync", true, "enables ticker syncing for all enabled exchanges")
flag.BoolVar(&settings.EnableOrderbookSyncing, "orderbooksync", true, "enables orderbook syncing for all enabled exchanges")
flag.BoolVar(&settings.EnableTradeSyncing, "tradesync", false, "enables trade syncing for all enabled exchanges")
flag.IntVar(&settings.SyncWorkers, "syncworkers", engine.DefaultSyncerWorkers, "the amount of workers (goroutines) to use for syncing exchange data")
flag.BoolVar(&settings.SyncContinuously, "synccontinuously", true, "whether to sync exchange data continuously (ticker, orderbook and trade history info")
flag.DurationVar(&settings.SyncTimeout, "synctimeout", engine.DefaultSyncerTimeout,
"the amount of time before the syncer will switch from one protocol to the other (e.g. from REST to websocket)")
// Forex provider settings
flag.BoolVar(&settings.EnableCurrencyConverter, "currencyconverter", false, "overrides config and sets up foreign exchange Currency Converter")
flag.BoolVar(&settings.EnableCurrencyLayer, "currencylayer", false, "overrides config and sets up foreign exchange Currency Layer")
@@ -65,7 +72,7 @@ func main() {
flag.BoolVar(&settings.EnableExchangeVerbose, "exchangeverbose", false, "increases exchange logging verbosity")
flag.BoolVar(&settings.ExchangePurgeCredentials, "exchangepurgecredentials", false, "purges the stored exchange API credentials")
flag.BoolVar(&settings.EnableExchangeHTTPRateLimiter, "ratelimiter", true, "enables the rate limiter for HTTP requests")
flag.IntVar(&settings.MaxHTTPRequestJobsLimit, "maxhttprequestjobslimit", request.DefaultMaxRequestJobs, "sets the max amount of jobs the HTTP request package stores")
flag.IntVar(&settings.MaxHTTPRequestJobsLimit, "requestjobslimit", request.DefaultMaxRequestJobs, "sets the max amount of jobs the HTTP request package stores")
flag.IntVar(&settings.RequestTimeoutRetryAttempts, "exchangehttptimeoutretryattempts", request.DefaultTimeoutRetryAttempts, "sets the amount of retry attempts after a HTTP request times out")
flag.DurationVar(&settings.ExchangeHTTPTimeout, "exchangehttptimeout", time.Duration(0), "sets the exchangs HTTP timeout value for HTTP requests")
flag.StringVar(&settings.ExchangeHTTPUserAgent, "exchangehttpuseragent", "", "sets the exchanges HTTP user agent")
@@ -88,6 +95,7 @@ func main() {
fmt.Println(core.Version(false))
var err error
settings.CheckParamInteraction = true
engine.Bot, err = engine.NewFromSettings(&settings)
if engine.Bot == nil || err != nil {
log.Errorf(log.Global, "Unable to initialise bot engine. Error: %s\n", err)

View File

@@ -2339,7 +2339,7 @@
"timestamp": "1565238020536"
},
"queryString": "",
"bodyParams": "{\"nonce\":\"1565238020075\",\"order\":{\"orderType\":\"MARKET\",\"buyTradedCurrency\":true,\"tradedCurrency\":\"BTC\",\"settlementCurrency\":\"USD\",\"tradedCurrencyAmount\":\"1\",\"settlementCurrencyAmount\":\"0\",\"limitPriceInSettlementCurrency\":\"0\",\"replaceExistingOrderUuid\":\"\",\"replaceOnlyIfActive\":false}}",
"bodyParams": "{\"nonce\":\"1565238020075\",\"order\":{\"orderType\":\"LIMIT\",\"buyTradedCurrency\":true,\"tradedCurrency\":\"BTC\",\"settlementCurrency\":\"USD\",\"tradedCurrencyAmount\":\"1\",\"settlementCurrencyAmount\":\"0\",\"limitPriceInSettlementCurrency\":\"1\",\"replaceExistingOrderUuid\":\"\",\"replaceOnlyIfActive\":false}}",
"headers": {
"Content-Type": [
"application/json"

View File

@@ -45378,7 +45378,7 @@
"POST": [
{
"data": {},
"queryString": "quantity=1000000000\u0026recvWindow=5000\u0026side=BUY\u0026signature=d122bfd46f4dcf8dda98974aed14221916aabc050a6aa30194d124e81ffe7f44\u0026symbol=LTCBTC\u0026timeInForce=GTC\u0026timestamp=1560236466000\u0026type=MARKET",
"queryString": "price=1\u0026quantity=1000000000\u0026recvWindow=5000\u0026side=BUY\u0026symbol=LTCBTC\u0026timeInForce=GTC\u0026timestamp=1572330506000\u0026type=LIMIT\u0026signature=00954d00c69761017b3440e6e26d9bf6b394bea354c658ca09254b8c28995c73",
"bodyParams": "",
"headers": {
"Key": [

View File

@@ -177,7 +177,7 @@
"error": "Order not found"
},
"queryString": "",
"bodyParams": "id=1\u0026key=\u0026nonce=1560467884949723197\u0026signature=79D88FC2BC3AECBFDD27F70EF5DDBB485349F45129C58EE725BAEE0DF7818452",
"bodyParams": "id=1234\u0026key=\u0026nonce=1560467884949723197\u0026signature=79D88FC2BC3AECBFDD27F70EF5DDBB485349F45129C58EE725BAEE0DF7818452",
"headers": {
"Content-Type": [
"application/x-www-form-urlencoded"

Some files were not shown because too many files have changed in this diff Show More