exchanges: Add Binance.us support (#949)

* public endpoints methods added

* Completing mapping REST endpoints

* Binanceus Wrapper methods -Partially

* BinaWra functions with test funs; Not Completed

* Finalizing wrapper methods & test

* Finalizing wrapper methods & test

* Fix & Complete wrapper functions

* Adding Stream Datas

* WS Test functions

* CI: Fix golangci-lint linter issues

* CI: Fix reverting unnessesary changes and type conversion issues

* CI: Fix reverting unnessesary changes and type conversion issues

* CI: Fix comment, method use, error handling, and code handling issues

* build(deps): bump github.com/urfave/cli/v2 from 2.4.0 to 2.4.8 (#932)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.4.0 to 2.4.8.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.4.0...v2.4.8)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump google.golang.org/grpc from 1.45.0 to 1.46.0 (#931)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.45.0 to 1.46.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.45.0...v1.46.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* asset: bitmask type optimisation (#922)

* asset: basic optim. bitmask

* glorious: nits

* currency: forgot parralel in testttttt

* ticker/orderbook: test fixes

* engine/rpcserver: fix and expand tests

* test: use `T.TempDir` to create temporary test directory (#934)

* test: use `T.TempDir` to create temporary test directory

This commit replaces `ioutil.TempDir` with `t.TempDir` in tests. The
directory created by `t.TempDir` is automatically removed when the test
and all its subtests complete.

Prior to this commit, temporary directory created using `ioutil.TempDir`
needs to be removed manually by calling `os.RemoveAll`, which is omitted
in some tests. The error handling boilerplate e.g.
	defer func() {
		if err := os.RemoveAll(dir); err != nil {
			t.Fatal(err)
		}
	}
is also tedious, but `t.TempDir` handles this for us nicely.

Reference: https://pkg.go.dev/testing#T.TempDir
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>

* test: fix TestEncryptTwiceReusesSaltButNewCipher on Windows

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>

* test: fix TestCheckConnection on Windows

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>

* test: fix TestRPCServer_GetTicker_LastUpdatedNanos on Windows

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>

* test: cleanup TestGenerateReport

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>

* account: storage, processing and method on balances update (#916)

* account: update account storage, retrieval and implement alert functionality when a currency change occurs.

* account: Add cancel channel

* account: remove old code

* account: don't embed mutex

* Update exchanges/account/account.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/account/account.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/account/account.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* account: addr nits

* account: Pull out test into indiv.

* account: Add test for update method

* account: add no change to test

* Update exchanges/account/account.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* account/portfolio: differentiate between asset type segregation and default to spot holdings.

* glorious: nits

* thrasher: nit

* ticker: fix spelling

* Update engine/portfolio_manager.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* thrasher: nits

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* build(deps): bump github.com/urfave/cli/v2 from 2.4.8 to 2.5.1 (#936)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.4.8 to 2.5.1.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.4.8...v2.5.1)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* orderbook/buffer: data integrity and resubscription pass (#910)

* orderbook/buffer: data integrity and resubscription pass

* btcmarkets: REMOVE THAT LIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIINE!!!!!!!!!!!!!!!!!

* buffer: reinstate publish, refaactor, invalidate more and comments

* buffer/orderbook: improve update and snapshot performance. Move Update type to orderbook package to util. pointer through entire function calls. (cleanup). Change action string to uint8 for easier comparison. Add parsing helper. Update current test benchmark comments.

* dispatch: change publish func to variadic id param

* dispatch: remove sender receiver wait time as this adds overhead and complexity. update tests.

* dispatch: don't create pointers for every job container

* rpcserver: fix assertion issues with data publishing change

* linter: fixes

* glorious: nits addr

* depth: change validation handling to incorporate and store err

* linter: fix more issues

* dispatch: fix race

* travis: update before fetching

* depth: wrap and return wrapped error in invalidate call and fix tests

* btcmarkets: fix commenting

* workflow: check

* workflow: check

* orderbook: check error

* buffer/depth: return invalidation error and fix tests

* gctcli: display errors on orderbook streams

* buffer: remove unused types

* orderbook/bitmex: shift function to bitmex

* orderbook: Add specific comments to unexported functions that don't have locking require locking.

* orderbook: restrict published data functionality to orderbook.Outbound interface

* common: add assertion failure helper for error

* dispatch: remove atomics, add mutex protection, remove add/remove worker, redo main tests

* dispatch: export function

* engine: revert and change sub logger to manager

* engine: remove old test

* dispatch: add common variable ;)

* btcmarket: don't overflow int in tests on 32bit systems

* ci: force 1.17.7 usage for go

* Revert "ci: force 1.17.7 usage for go"

This reverts commit af2f95563bf218cf2b9f36a9fcf3258e2c6a2d91.

* golangci: bump version add and remove linter items

* Revert "golangci: bump version add and remove linter items"

This reverts commit 3c98bffc9d030e39faca0387ea40c151df2ab06b.

* dispatch: remove unsused mutex from mux

* order: slight optimizations

* nits: glorious

* dispatch: fix regression on uuid generation and input inline with master

* linter: fix

* linter: fix

* glorious: nit - rm slice segration

* account: fix test after merge

* coinbasepro: revert change

* account: close channel instead of needing a receiver, push alert in routine to prepare for waiter.

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>

* exchange/wrapper: add GetServerTime() for exchange analytics (#938)

* exchange/wrapper: add GetServerTime() for exchange analytics

* binance: fix linter issue

* glorious: nits

* glorious: nits rides again

* thrasher: nits implement huobi

* thrasher: nits add to exchange_wrapper_issues cmd

* order: slight optimizations (#917)

* order: slight optimizations

* orders: add benchmarks, small optimize and change order side to uin8 for comparitive optimizations.

* orders: continue to convert string type -> uint

* orders/backtester: interim move type to orders package, later can expand or deprecate.

* orders: handle errors

* orders: optimize filters and remove error returns when its clearly not needed

* orders: remove log call

* backtester: zero value check

* orders/futures: zero value -> flag

* linter: fix

* linter: more fixes

* linters: rides again

* glorious: nits

* common: Add zero value unix check for time values; also addresses glorious nits

* glorious scott: nits

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>

* btcm: add order execution limit wrapper support (#941)

* btcm: add in order execution limit fetching

* btcm/test: add t.Parrrrrralllleeellllllllll

* btcm/wrapper: add update on startup

* glorious: nits

* thrasher: nit add status field

* build(deps): bump github.com/urfave/cli/v2 from 2.5.1 to 2.6.0 (#944)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.5.1 to 2.6.0.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.5.1...v2.6.0)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* proto/lint: Add protobuf GitHub action and linter (#943)

* Buf upgrades

* Buf format and basic endpoint fixes

* gRPC linter fixes

* Amend buf.yaml linter exceptions

* Update README

* Freshly generated gRPC code after depends update

* Nitterinos

* ordermanager: fix test error introduced in #917  (#942)

* ordermanager: fix residual test issue from #917 and reduce some racey action

* glorious: nits; also removed functions that weren't being used and were unexported

* rm: pew

* linter: fix issues

* glourious: nits

* credentials: fix test issue with racey racey horse basey

* engine: Add websocket data handler register function (#935)

* engine: Add websocket interceptor register function

* Update engine/engine.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update engine/websocketroutine_manager_types.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* engine/websock: switch to data handler function register and range over handlers to still include default gct handling

* engine/websocket: change name

* glorious: nits

* linter: fix

* glorious: nits

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* btcm: add modify order functionality, change return to pointer (#940)

* btcm: add modify order functionality, change return to pointer

* glorious: nits

* glorious: nits

* btcm: Adjust function name

* thrasher: nits

* thrasher: nits cont...

* request: adds WithVerbose function to package to add verbosity to request context (#950)

* request: adds WithVerbose function to package to add verbosity to request context

* request: add t.Parr....

* thrasher: nits

* build(deps): bump google.golang.org/grpc from 1.46.0 to 1.46.2 (#951)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.46.0 to 1.46.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.46.0...v1.46.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* exchange: expose GetCredentials() and split GetAuthenticatedAPISupport() (#954)

* exchange/wrapper: expose GetCredentials func to IBotInterface

* exchanges: split up GetAuthenticatedAPISupport into specific function calls, organize IBotExchange functionality getter functions

* interface: change name - RPCSercer: rm GetBase func call.

* glorious: nits (fix panic)

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>

* CI: merge fixes

* CI: fixing github generated lint issues

* orders: Add method for creating cancel struct from order details (#947)

* orders: Add method for creating cancel struct from order details

* orders: remove uneeded fields

* glorious: nit

* grpc: add shutdown call for external management (#957)

* grpc: add shutdown call for external management

* go mod: tidy

* glorious: suggestion

* Update engine/engine.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update engine/rpcserver.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update main.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update engine/rpcserver.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* build(deps): bump github.com/lib/pq from 1.10.5 to 1.10.6 (#960)

Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.10.5 to 1.10.6.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.10.5...v1.10.6)

---
updated-dependencies:
- dependency-name: github.com/lib/pq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/urfave/cli/v2 from 2.6.0 to 2.8.0 (#958)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.6.0 to 2.8.0.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.6.0...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 (#963)

Bumps [github.com/grpc-ecosystem/grpc-gateway/v2](https://github.com/grpc-ecosystem/grpc-gateway) from 2.10.0 to 2.10.2.
- [Release notes](https://github.com/grpc-ecosystem/grpc-gateway/releases)
- [Changelog](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/.goreleaser.yml)
- [Commits](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.10.0...v2.10.2)

---
updated-dependencies:
- dependency-name: github.com/grpc-ecosystem/grpc-gateway/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* orders: adds method to retrieve snapshot of order execution limit values (#946)

* orders: add method to Limit to retrieve order execution limit snapshots

* currency/btcmarkets: add error and update field name to standard

* linter: fix

* limts: don't return pointer

* limit: Add notes

* glorious: nits

* linter: fix

* limit: reinstate nil check

* exchanges: change field names to be more consistent (@thrasher-) suggestion

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>

* orders: Add derive modify struct method from order.Detail  (#948)

* orders: Add derive modify struct method to order.Detail and then subsequent method to derive and standardize response details

* exchanges: call modify method in wrappers

* linter: fixes

* engine/wsroutineman: remove print summary

* glorious: nits, removed modifyOrder functionality for Bithumb. There are not docs to support this.

* Update exchanges/order/orders.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* build(deps): bump github.com/spf13/viper from 1.11.0 to 1.12.0 (#965)

Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/urfave/cli/v2 from 2.8.0 to 2.8.1 (#964)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.8.0 to 2.8.1.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.8.0...v2.8.1)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* orders: Add methods to derive SubmitResponse and Detail types (#955)

* orders: deprecate SubmitResponse return and change to *order.Detail construct detail from order.Submit struct

* orders: add coverage, fix tests

* coinut: rm test for checking

* orders: revert change for return and change field ID to a more explicit name OrderID

* orders: Add method to see if the order was placed

* order: change field name in Cancel type to be more explicit

* orders: standardize field -> OrderID

* backtester: populate change

* orders: add test

* gctscript: fix field name

* linter: fix issues

* linter: more fixes

* linter: forever

* exchanges_tests: populate order.Submit field exchange name

* Update exchanges/order/order_types.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/order/orders.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* glorious: nits

* thrasher: nits

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* build(deps): bump bufbuild/buf-setup-action from 1.4.0 to 1.5.0 (#973)

Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/bufbuild/buf-setup-action/releases)
- [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: bufbuild/buf-setup-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump google.golang.org/grpc from 1.46.2 to 1.47.0 (#972)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.46.2 to 1.47.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.46.2...v1.47.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 (#971)

Bumps [github.com/grpc-ecosystem/grpc-gateway/v2](https://github.com/grpc-ecosystem/grpc-gateway) from 2.10.2 to 2.10.3.
- [Release notes](https://github.com/grpc-ecosystem/grpc-gateway/releases)
- [Changelog](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/.goreleaser.yml)
- [Commits](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.10.2...v2.10.3)

---
updated-dependencies:
- dependency-name: github.com/grpc-ecosystem/grpc-gateway/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/d5/tengo/v2 from 2.10.1 to 2.11.2 (#975)

Bumps [github.com/d5/tengo/v2](https://github.com/d5/tengo) from 2.10.1 to 2.11.2.
- [Release notes](https://github.com/d5/tengo/releases)
- [Changelog](https://github.com/d5/tengo/blob/master/.goreleaser.yml)
- [Commits](https://github.com/d5/tengo/compare/v2.10.1...v2.11.2)

---
updated-dependencies:
- dependency-name: github.com/d5/tengo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/urfave/cli/v2 from 2.8.1 to 2.10.1 (#979)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.8.1 to 2.10.1.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.8.1...v2.10.1)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/d5/tengo/v2 from 2.11.2 to 2.12.0 (#978)

Bumps [github.com/d5/tengo/v2](https://github.com/d5/tengo) from 2.11.2 to 2.12.0.
- [Release notes](https://github.com/d5/tengo/releases)
- [Changelog](https://github.com/d5/tengo/blob/master/.goreleaser.yml)
- [Commits](https://github.com/d5/tengo/compare/v2.11.2...v2.12.0)

---
updated-dependencies:
- dependency-name: github.com/d5/tengo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* CI: adding WS and REST tests, and minor fixes

* CI: fixes on available asset and related minor issues

* CI: fixes on endpoint function, test functions, and types

* CI: updating templates and slight fixes

* CI: updating slight fixes on tests and withdraws Request model

* build(deps): bump styfle/cancel-workflow-action from 0.9.1 to 0.10.0 (#985)

Bumps [styfle/cancel-workflow-action](https://github.com/styfle/cancel-workflow-action) from 0.9.1 to 0.10.0.
- [Release notes](https://github.com/styfle/cancel-workflow-action/releases)
- [Commits](https://github.com/styfle/cancel-workflow-action/compare/0.9.1...0.10.0)

---
updated-dependencies:
- dependency-name: styfle/cancel-workflow-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump bufbuild/buf-setup-action from 1.5.0 to 1.6.0 (#984)

Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/bufbuild/buf-setup-action/releases)
- [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: bufbuild/buf-setup-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* backtester: Futures handling & FTX Cash and Carry example strategy (#930)

* implements futures functions and GRPC functions on new branch

* lint and test fixes

* Fix uneven split pnl. Adds collateral weight test. docs. New clear func

* Test protection if someone has zero collateral

* Uses string instead of double for accuracy

* Fixes old code panic

* context, match, docs

* Addresses Shazniterinos, var names, expanded tests

* Returns subaccount name, provides USD values when offlinecalc

* Fixes oopsie

* Fixes cool bug which allowed made up subaccount results

* Subaccount override on FTX, subaccount results for collateral

* Strenghten collateral account info checks. Improve FTX test

* English is my first language

* Fixes oopsies

* Adds some conceptual futures order details to track PNL

* Initial design of future order processing in the backtester

* Introduces futures concept for collateral and spot/futures config diffs

* Fixes most tests

* Simple designs for collateral funding pair concept

* Expands interface use so much it hurts

* Implements more collateral interfaces

* Adds liquidation, adds strategy, struggles with Binance

* Attempts at getting FTX to work

* Adds calculatePNL as a wrapper function and adds an `IsFutures` asset check

* Successfully loads backtester with collateral currency

* Fails to really get much going for supporting futures

* Merges master changes

* Fleshes out how FTX processes collateral

* Further FTX collateral workings

* hooks up more ftx collateral and pnl calculations

* more funcs to flesh out handling

* Adds more links, just can't fit the pieces together :(

* Greatly expands futures order processing

* Fleshes out position tracker to also handle asset and exchange +testing

* RM linkedOrderID. rn positioncontroller, unexport

* Successfully tracks futures order positions

* Fails to calculate PNL

* Calculates pnl from orders accurately with exception to flipping orders

* Calculates PNL from orders

* Adds another controller layer to make it ez from orderstore

* Backtester now compiles. Adds test coverage

* labels things add scaling collateral test

* Calculates pnl in line with fees

* Mostly accurate PNL, with exception to appending with diff prices

* Adds locks, adds rpc function

* grpc implementations

* Gracefully handles rpc function

* beautiful tests!

* rejiggles tests to polish

* Finishes FTX testing, adds comments

* Exposes collateral calculations to rpc

* Adds commands and testing for rpcserver.go functions

* Increase testing and fix up backtester code

* Returns cool changes to original branch

* end of day fixes

* Fixing some tests

* Fixing tests 🎉

* Fixes all the tests

* Splits the backtester setup and running into different files

* Merge, minor fixes

* Messing with some strategy updates

* Failed understanding at collateral usage

* Begins the creation of cash and carry strategy

* Adds underlying pair, adds filldependentevent for futures

* Completes fill prerequsite event implementation. Can't short though

* Some bug fixes

* investigating funds

* CAN NOW CREATE A SHORT ORDER

* Minor change in short size

* Fixes for unrealised PNL & collateral rendering

* Fixes lint and tests

* Adds some verbosity

* Updates to pnl calc

* Tracks pnl for short orders, minor update to strategy

* Close and open event based on conditions

* Adds pnl data for currency statistics

* Working through PNL calculation automatically. Now panics

* Adds tracking, is blocked from design

* Work to flesh out closing a position

* vain attempts at tracking zeroing out bugs

* woww, super fun new subloggers 🎉

* Begins attempt at automatically handling contracts and collateral based on direction

* Merge master + fixes

* Investigating issues with pnl and holdings

* Minor pnl fixes

* Fixes future position sizing, needs contract sizing

* Can render pnl results, focussing on funding statistics

* tracking candles for futures, but why not btc

* Improves funding statistics

* Colours and stats

* Fixes collateral and snapshot bugs

* Completes test

* Fixes totals bug

* Fix double buy, expand stats, fixes usd totals, introduce interface

* Begins report formatting and calculations

* Appends pnl to receiving curr. Fixes map[time]. accurate USD

* Improves report output rendering

* PNL stats in report. New tests for futures

* Fixes existing tests before adding new coverage

* Test coverage

* Completes portfolio coverage

* Increase coverage exchange, portfolio. fix size bug. NEW CHART

* WHAT IS GOING ON WITH PNL

* Fixes PNL calculation. Adds ability to skip om futures tracking

* minor commit before merge

* Adds basic liquidation to backtester

* Changes liquidation to order based

* Liquidationnnnnn

* Further fleshes out liquidations

* Completes liquidations in a honorable manner. Adds AppendReasonf

* Beginnings of spot futures gap chart. Needs to link currencies to render difference

* Removes fake liquidation. Adds cool new chart

* Fixes somet tests,allows for zero fee value v nil distinction,New tests

* Some annoying test fixes that took too long

* portfolio coverage

* holding coverage, privatisation funding

* Testwork

* boring tests

* engine coverage

* More backtesting coverage

* Funding, strategy, report test coverage

* Completes coverage of report package

* Documentation, fixes some assumptions on asset errors

* Changes before master merge

* Lint and Tests

* defaults to non-coloured rendering

* Chart rendering

* Fixes surprise non-local-lints

* Niterinos to the extremeos

* Fixes merge problems

* The linter splintered across the glinting plinths

* Many nits addressed. Now sells spot position on final candle

* Adds forgotten coverage

* Adds ability to size futures contracts to match spot positions.

* fixes order sell sizing

* Adds tests to sizing. Fixes charting issue

* clint splintered the linters with flint

* Improves stats, stat rendering

* minifix

* Fixes tests and fee bug

* Merge fixeroos

* Microfixes

* Updates orderPNL on first Correctly utilises fees. Adds committed funds

* New base funcs. New order summary

* Fun test updates

* Fix logo colouring

* Fixes niteroonies

* Fix report

* BAD COMMIT

* Fixes funding issues.Updates default fee rates.Combines cashcarry case

* doc regen

* Now returns err

* Fixes sizing bug issue introduced in PR

* Fixes fun fee/total US value bug

* Fix chart bug. Show log charts with disclaimer

* sellside fee

* fixes fee and slippage view

* Fixed slippage price issue

* Fixes calculation and removes rendering

* Fixes stats and some rendering

* Merge fix

* Fixes merge issues

* go mod tidy, lint updates

* New linter attempt

* Version bump in appveyor and makefile

* Regex filename, config fixes, template h2 fixes

* Removes bad stats.

* neatens config builder. Moves filename generator

* Fixes issue where linter wants to fix my spelling

* Fixes pointers and starts

* build(deps): bump github.com/urfave/cli/v2 from 2.10.1 to 2.10.3 (#982)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.10.1 to 2.10.3.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.10.1...v2.10.3)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* alert: Add optimizations (#939)

* alert: Add optimizations

* alert: add basic benchmarks

* alert: fix linter issue

* documentation: change to text/template as html/template escapes to protect against code injection. Add readme.md for alert.

* README: Add package name

* alert: link up with engine settings

* request: isVerbose refactor

* Update exchanges/alert/alert_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/alert/alert.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* glorious: fun police

* documentation: regen

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* technical_analysis: TWAP & VWAP + TA methods to candles and link to existing RPC server for GCTCLI prototyping  (#970)

* kline: add weighted price helpers for candles

* twap/vwap: basic implementation and hook to rpc for protype

* ta: cont implementation. (WIP)

* kline: Add tests

* kline: add helpers

* ta: full impl.

* kline: remove support for macd and add in correlation-coefficient handling

* rpc: change naming convention

* linter: fix

* protolinter: fix

* linter: ++

* kline: reinstate macd handling after adding in check

* glorious: nits

* gctcl: linter

* Update exchanges/kline/weighted_price.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* glorious: nits v2.0

* kline: fix test

* huobi-tests: shift from next quarter to this weeks contracts as they were erroring out in tests.

* btcmarkets: update supported kline intervals

* zb: fix test

* rpcserver: fix bug and tests

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* build(deps): bump github.com/urfave/cli/v2 from 2.10.3 to 2.11.0 (#993)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.10.3 to 2.11.0.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.10.3...v2.11.0)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* FTX: Margin lending/borrow rate history (#981)

* Adds lending rates/borrows to FTX and the command

* Movements, renames, rpc test

* Fleshing out rpc response

* Allows rpcserver to calculate offline (but not gctcli). Expands tests

* rn structs. add exchange_wrapper_issues support

* Adds a nice yearly rate

* Surprise yearly borrow rate!

* Rn+Mv to margin package. Fixes some serious whoopsies

* Adds average lend/borrow rates instead of sum

* rm oopsie whoopsie

* This is what the linter was having an issue with

* re-gen

* lintl

* niteroos

* build(deps): bump google.golang.org/grpc from 1.47.0 to 1.48.0 (#995)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.47.0 to 1.48.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.47.0...v1.48.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* account: segregate holdings by credentials for future multi-key management (#956)

* exchanges/account: shift credentials to account package and segregate funds to keys

* merge: fixes

* linter: fix

* Update exchanges/account/account.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits + protection for string panic

* glorious_suggestion: add method for matching keys

* linter: fix tests

* account: add protected method for credentials minimizing access, display full account details to rpc.

* linter: spelling kweeeeeeen

* accounts/portfolio: clean/check portfolio code and quickly check balances from change. Add protected method for future matching.

* accounts: theres no point in pointerising everything

* linter: ok pointerise this then...

* exchanges: fix regression add in little notes.

* glorious: nits

* Update exchanges/account/credentials.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/account/credentials_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/account/credentials_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* gloriously: fix glorious glorious test gloriously

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* CI: fixing linter issue and conflicts

* CI: fixing ratelimit and other slight issues

* build(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 (#998)

Bumps [github.com/grpc-ecosystem/grpc-gateway/v2](https://github.com/grpc-ecosystem/grpc-gateway) from 2.10.3 to 2.11.0.
- [Release notes](https://github.com/grpc-ecosystem/grpc-gateway/releases)
- [Changelog](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/.goreleaser.yml)
- [Commits](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.10.3...v2.11.0)

---
updated-dependencies:
- dependency-name: github.com/grpc-ecosystem/grpc-gateway/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/d5/tengo/v2 from 2.12.0 to 2.12.1 (#997)

Bumps [github.com/d5/tengo/v2](https://github.com/d5/tengo) from 2.12.0 to 2.12.1.
- [Release notes](https://github.com/d5/tengo/releases)
- [Changelog](https://github.com/d5/tengo/blob/master/.goreleaser.yml)
- [Commits](https://github.com/d5/tengo/compare/v2.12.0...v2.12.1)

---
updated-dependencies:
- dependency-name: github.com/d5/tengo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/urfave/cli/v2 from 2.11.0 to 2.11.1 (#996)

Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.11.0 to 2.11.1.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v2.11.0...v2.11.1)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* CI: fixing type and other slight issues

* Cleanup after merge

* Endpoints rate limit update

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ryan O'Hara-Reid <oharareid.ryan@gmail.com>
Co-authored-by: Eng Zer Jun <engzerjun@gmail.com>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
samuael
2022-08-12 08:30:11 +03:00
committed by GitHub
parent 7c1951875c
commit ace5e64c10
24 changed files with 8427 additions and 2 deletions

View File

@@ -19,6 +19,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Exchange | REST API | Streaming API | FIX API |
|----------|------|-----------|-----|
| Alphapoint | Yes | Yes | NA |
| Binance.US| Yes | Yes | NA |
| Binance| Yes | Yes | NA |
| Bitfinex | Yes | Yes | NA |
| Bitflyer | Yes | No | NA |

View File

@@ -0,0 +1,106 @@
{{define "exchanges binanceus" -}}
{{template "header" .}}
## Binanceus Exchange
### Current Features
+ REST Support
+ Websocket Support
### How to enable
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### How to do REST public/private calls
+ If enabled via "configuration".json file the exchange will be added to the
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
the wrapper interface functions for accessing exchange data. View routines.go
for an example of integration usage with GoCryptoTrader. Rudimentary example
below:
main.go
```go
var b exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "Binanceus" {
b = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := b.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := b.FetchOrderbook()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := b.GetAccountInfo()
if err != nil {
// Handle error
}
```
+ If enabled via individually importing package, rudimentary example below:
```go
// Public calls
// Fetches current ticker information
ticker, err := b.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := b.GetOrderBook()
if err != nil {
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetUserInfo returns account info
accountInfo, err := b.GetUserInfo(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := b.Trade(...)
if err != nil {
// Handle error
}
```
### How to do Websocket public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations" .}}
{{end}}

View File

@@ -42,6 +42,7 @@ _b in this context is an `IBotExchange` implemented struct_
| Exchange | Recent Trades via REST | Live trade updates via Websocket | Trade history via REST |
|----------|------|-----------|-----|
| Alphapoint | No | No | No |
| Binance.US | Yes | Yes | NA |
| Binance| Yes | Yes | Yes |
| Bitfinex | Yes | Yes | Yes |
| Bitflyer | Yes | No | No |

View File

@@ -20,6 +20,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Exchange | REST API | Streaming API | FIX API |
|----------|------|-----------|-----|
| Alphapoint | Yes | Yes | NA |
| Binance.US| Yes | Yes | NA |
| Binance| Yes | Yes | NA |
| Bitfinex | Yes | Yes | NA |
| Bitflyer | Yes | No | NA |

View File

@@ -31,6 +31,11 @@ const (
defaultTimeout = time.Second * 15
)
var (
// emailRX represents email address matching pattern
emailRX = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
)
// Vars for common.go operations
var (
_HTTPClient *http.Client
@@ -69,6 +74,14 @@ var (
ErrTypeAssertFailure = errors.New("type assert failure")
)
// MatchesEmailPattern ensures that the string is an email address by regexp check
func MatchesEmailPattern(value string) bool {
if len(value) < 3 || len(value) > 254 {
return false
}
return emailRX.MatchString(value)
}
// SetHTTPClientWithTimeout sets a new *http.Client with different timeout
// settings
func SetHTTPClientWithTimeout(t time.Duration) error {

View File

@@ -702,3 +702,22 @@ func TestGetAssertError(t *testing.T) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrTypeAssertFailure)
}
}
func TestMatchesEmailPattern(t *testing.T) {
success := MatchesEmailPattern("someone semail")
if success {
t.Error("MatchesEmailPattern() unexpected test validation result")
}
success = MatchesEmailPattern("someon esemail@gmail")
if success {
t.Error("MatchesEmailPattern() unexpected test validation result")
}
success = MatchesEmailPattern("123@gmail")
if !success {
t.Error("MatchesEmailPattern() unexpected test validation result")
}
success = MatchesEmailPattern("someonesemail@email.com")
if !success {
t.Error("MatchesEmailPattern() unexpected test validation result")
}
}

View File

@@ -239,6 +239,90 @@
]
},
"exchanges": [
{
"name": "Binanceus",
"enabled": true,
"verbose": false,
"httpTimeout": 15000000000,
"websocketResponseCheckTimeout": 30000000,
"websocketResponseMaxLimit": 7000000000,
"websocketTrafficTimeout": 30000000000,
"baseCurrencies": "USD",
"currencyPairs": {
"bypassConfigFormatUpgrades": false,
"pairs": {
"spot": {
"assetEnabled": true,
"enabled": "BTC-USDT,ETH-USDT,LTC-USDT,ADA-USDT",
"available": "BTC-USD,BCH-USD,LTC-USD,USDT-USD,BTC-USDT,ETH-USDT,BCH-USDT,LTC-USDT,BNB-USD,BNB-USDT,ETH-BTC,BNB-BTC,LTC-BTC,BCH-BTC,ADA-USD,BAT-USD,ETC-USD,XLM-USD,ZRX-USD,ADA-USDT,BAT-USDT,ETC-USDT,XLM-USDT,ZRX-USDT,LINK-USD,RVN-USD,DASH-USD,ZEC-USD,ALGO-USD,IOTA-USD,BUSD-USD,BTCB-USD,DOGE-USDT,WAVES-USD,ATOM-USDT,ATOM-USD,NEO-USDT,NEO-USD,VET-USDT,QTUM-USDT,QTUM-USD,ICX-USD,ENJ-USD,ONT-USD,ONT-USDT,ZIL-USD,ZILB-USD,VET-USD,BNBB-USD,ETHB-USD,ALGO-BUSD,XTZ-USD,XTZ-BUSD,HBAR-USD,HBAR-BUSD,OMG-USD,OMG-BUSD,MATIC-USD,MATIC-BUSD,XTZ-BTC,ADA-BTC,REP-BUSD,REP-USD,EOS-BUSD,EOS-USD,DOGE-USD,KNC-USD,KNC-USDT,VTHO-USDT,VTHO-USD,USDC-USD,COMP-USDT,COMP-USD,MANA-USD,HNT-USD,HNT-USDT,MKR-USD,MKR-USDT,DAI-USD,ONE-USDT,ONE-USD,BAND-USDT,BAND-USD,STORJ-USDT,STORJ-USD,UNI-USD,UNI-USDT,SOL-USD,SOL-USDT,LINK-BTC,VET-BTC,UNI-BTC,EGLD-USDT,EGLD-USD,PAXG-USDT,PAXG-USD,OXT-USDT,OXT-USD,ZEN-USDT,ZEN-USD,BTC-USDC,ONEB-USD,FIL-USDT,FIL-USD,AAVE-USDT,AAVE-USD,GRT-USDT,GRT-USD,SUSHI-USD,ANKR-USD,AMP-USD,SHIB-USDT,SHIB-BUSD,CRV-USDT,CRV-USD,AXS-USDT,AXS-USD,SOL-BTC,AVAX-USDT,AVAX-USD,CTSI-USDT,CTSI-USD,DOT-USDT,DOT-USD,YFI-USDT,YFI-USD,1INCH-USDT,1INCH-USD,FTM-USDT,FTM-USD,USDC-USDT,ETH-USDC,USDC-BUSD,MATIC-USDT,MANA-USDT,MANA-BUSD,ALGO-USDT,ADA-BUSD,SOL-BUSD,EOS-USDT,ENJ-USDT,NEAR-USDT,NEAR-BUSD,NEAR-USD,OMG-USDT,SUSHI-USDT,LRC-USDT,LRC-USD,LRC-BTC,KSHI-BUSD,LPT-USDT,LPT-BUSD,LPT-USD,POLY-USDT,POLY-BUSD,POLY-USD,POLY-BTC,MATIC-BTC,DOT-BTC,NMR-USDT,NMR-USD,SLP-USDT,ANT-USD,XNO-USD,CHZ-USDT,CHZ-USD,OGN-USDT,OGN-USD,GALA-USDT,GALA-USD,TLM-USDT,TLM-USD,SNX-USDT,SNX-USD,AUDIO-USDT,AUDIO-USD,ENS-USDT,MANA-BTC,ATOM-BTC,AVAX-BTC,WBTC-BTC,REQ-USDT,REQ-USD,APE-USDT,APE-USD,FLUX-USDT,FLUX-USD,TRX-BTC,TRX-BUSD,TRX-USDT,TRX-USD,COTI-USDT,COTI-USD,VOXEL-USDT,VOXEL-USD,RLC-USDT,RLC-USD,UST-USDT,UST-USD,BICO-USDT,BICO-USD,API3-USDT,API3-USD,ENS-USD,BTC-UST,BNT-USDT,BNT-USD,IMX-USDT,IMX-USD,SPELL-USDT,SPELL-USD,JASMY-USDT,JASMY-USD,FLOW-USDT,FLOW-USD,GTC-USDT,GTC-USD,BTC-BUSD,ZIL-BUSD,BNB-BUSD,ETH-BUSD,BUSD-USDT,ONE-BUSD,LINK-USDT,ZEC-USDT,SLP-USD,ANT-USDT",
"requestFormat": {
"uppercase": true
},
"configFormat": {
"uppercase": true,
"delimiter": "-"
}
}
}
},
"api": {
"authenticatedSupport": false,
"authenticatedWebsocketApiSupport": false,
"credentials": {
"key": "",
"secret": ""
},
"credentialsValidator": {
"requiresKey": true,
"requiresSecret": true
},
"urlEndpoints": {
"RestSpotSupplementaryURL": "https://api.binance.us",
"RestSpotURL": "https://api.binance.us",
"WebsocketSpotSupplementaryURL": "wss://stream.binance.us:9443/stream",
"WebsocketSpotURL": "wss://stream.binance.us:9443/stream"
}
},
"features": {
"supports": {
"restAPI": true,
"restCapabilities": {
"tickerBatching": true,
"autoPairUpdates": true
},
"websocketAPI": true,
"websocketCapabilities": {}
},
"enabled": {
"autoPairUpdates": true,
"websocketAPI": true,
"saveTradeData": false,
"tradeFeed": false,
"fillsFeed": false
}
},
"bankAccounts": [
{
"enabled": false,
"bankName": "",
"bankAddress": "",
"bankPostalCode": "",
"bankPostalCity": "",
"bankCountry": "",
"accountName": "",
"accountNumber": "",
"swiftCode": "",
"iban": "",
"supportedCurrencies": ""
}
],
"orderbook": {
"verificationBypass": false,
"websocketBufferLimit": 5,
"websocketBufferEnabled": false,
"publishPeriod": 10000000000
}
},
{
"name": "Binance",
"enabled": true,

View File

@@ -43,6 +43,7 @@ $ ./gctcli withdrawcryptofunds --exchange=ftx --currency=USDT --address=TJU9piX2
| Exchange | Deposits | Withdrawals | Notes|
|----------|----------|-------------|------|
| Alphapoint | No | No | |
| Binance.US | Yes | Yes | |
| Binance | Yes | Yes | |
| Bitfinex | Yes | Yes | Only supports USDT |
| Bitflyer | No | No | |

View File

@@ -65,6 +65,7 @@ A helper tool [cmd/dbseed](../cmd/dbseed/README.md) has been created for assisti
## Exchange status
| Exchange | Supported |
|----------------|-------------|
| Binance.US | Y |
| Binance | Y |
| Bitfinex | Y |
| Bitflyer | |

View File

@@ -8,6 +8,7 @@ import (
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/binance"
"github.com/thrasher-corp/gocryptotrader/exchanges/binanceus"
"github.com/thrasher-corp/gocryptotrader/exchanges/bitfinex"
"github.com/thrasher-corp/gocryptotrader/exchanges/bitflyer"
"github.com/thrasher-corp/gocryptotrader/exchanges/bithumb"
@@ -146,6 +147,8 @@ func (m *ExchangeManager) NewExchangeByName(name string) (exchange.IBotExchange,
var exch exchange.IBotExchange
switch nameLower {
case "binanceus":
exch = new(binanceus.Binanceus)
case "binance":
exch = new(binance.Binance)
case "bitfinex":
@@ -206,6 +209,5 @@ func (m *ExchangeManager) NewExchangeByName(name string) (exchange.IBotExchange,
}
return nil, fmt.Errorf("%s, %w", nameLower, ErrExchangeNotFound)
}
return exch, nil
}

View File

@@ -82,7 +82,7 @@ func TestExchangeManagerRemoveExchange(t *testing.T) {
func TestNewExchangeByName(t *testing.T) {
m := SetupExchangeManager()
exchanges := []string{"binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "bybit", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"}
exchanges := []string{"binanceus", "binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "bybit", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"}
for i := range exchanges {
exch, err := m.NewExchangeByName(exchanges[i])
if err != nil && exchanges[i] != "fake" {

View File

@@ -0,0 +1,140 @@
# GoCryptoTrader package Binanceus
<img src="/common/gctlogo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/binanceus)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This binanceus package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
## Binanceus Exchange
### Current Features
+ REST Support
+ Websocket Support
### How to enable
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### How to do REST public/private calls
+ If enabled via "configuration".json file the exchange will be added to the
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
the wrapper interface functions for accessing exchange data. View routines.go
for an example of integration usage with GoCryptoTrader. Rudimentary example
below:
main.go
```go
var b exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "Binanceus" {
b = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := b.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := b.FetchOrderbook()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := b.GetAccountInfo()
if err != nil {
// Handle error
}
```
+ If enabled via individually importing package, rudimentary example below:
```go
// Public calls
// Fetches current ticker information
ticker, err := b.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := b.GetOrderBook()
if err != nil {
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetUserInfo returns account info
accountInfo, err := b.GetUserInfo(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := b.Trade(...)
if err != nil {
// Handle error
}
```
### How to do Websocket public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### Please click GoDocs chevron above to view current GoDoc information for this package
## Contribution
Please feel free to submit any pull requests or suggest any desired features to be added.
When submitting a PR, please abide by our coding guidelines:
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
+ Pull requests need to be based on and opened against the `master` branch.
## Donations
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,996 @@
package binanceus
import (
"context"
"errors"
"fmt"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"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/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
"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/stream"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)
// GetDefaultConfig returns a default exchange config
func (bi *Binanceus) GetDefaultConfig() (*config.Exchange, error) {
bi.SetDefaults()
exchCfg := new(config.Exchange)
exchCfg.Name = bi.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = bi.BaseCurrencies
err := bi.SetupDefaults(exchCfg)
if err != nil {
return nil, err
}
if bi.Features.Supports.RESTCapabilities.AutoPairUpdates {
err := bi.UpdateTradablePairs(context.TODO(), true)
if err != nil {
return nil, err
}
}
return exchCfg, nil
}
// SetDefaults sets the basic defaults for Binanceus
func (bi *Binanceus) SetDefaults() {
bi.Name = "Binanceus"
bi.Enabled = true
bi.Verbose = true
bi.API.CredentialsValidator.RequiresKey = true
bi.API.CredentialsValidator.RequiresSecret = true
bi.SetValues()
fmt1 := currency.PairStore{
RequestFormat: &currency.PairFormat{Uppercase: true},
ConfigFormat: &currency.PairFormat{
Delimiter: currency.DashDelimiter,
Uppercase: true,
},
}
err := bi.StoreAssetPairFormat(asset.Spot, fmt1)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
bi.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
Websocket: true,
RESTCapabilities: protocol.Features{
TickerBatching: true,
TickerFetching: true,
OrderbookFetching: true,
AutoPairUpdates: true,
AccountInfo: true,
CryptoDeposit: true,
CryptoWithdrawal: true,
GetOrder: true,
GetOrders: true,
CancelOrders: true,
CancelOrder: true,
SubmitOrder: true,
SubmitOrders: true,
DepositHistory: true,
WithdrawalHistory: true,
TradeFetching: true,
UserTradeHistory: true,
TradeFee: true,
CryptoDepositFee: true,
CryptoWithdrawalFee: true,
MultiChainDeposits: true,
MultiChainWithdrawals: true,
},
WebsocketCapabilities: protocol.Features{
TickerFetching: true,
OrderbookFetching: true,
Subscribe: true,
Unsubscribe: true,
AuthenticatedEndpoints: true,
AccountInfo: true,
GetOrders: true,
TradeFetching: true,
KlineFetching: true,
GetOrder: true,
},
WithdrawPermissions: exchange.AutoWithdrawCrypto |
exchange.AutoWithdrawFiat,
Kline: kline.ExchangeCapabilitiesSupported{
DateRanges: true,
Intervals: true,
},
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: true,
Kline: kline.ExchangeCapabilitiesEnabled{
Intervals: map[string]bool{
kline.OneMin.Word(): true,
kline.ThreeMin.Word(): true,
kline.FiveMin.Word(): true,
kline.FifteenMin.Word(): true,
kline.ThirtyMin.Word(): true,
kline.OneHour.Word(): true,
kline.TwoHour.Word(): true,
kline.FourHour.Word(): true,
kline.SixHour.Word(): true,
kline.EightHour.Word(): true,
kline.TwelveHour.Word(): true,
kline.OneDay.Word(): true,
kline.ThreeDay.Word(): true,
kline.OneWeek.Word(): true,
kline.OneMonth.Word(): true,
},
ResultLimit: 1000,
},
},
}
bi.Requester, err = request.New(bi.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
bi.API.Endpoints = bi.NewEndpoints()
if err := bi.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: binanceusAPIURL,
exchange.RestSpotSupplementary: binanceusAPIURL,
exchange.WebsocketSpot: binanceusDefaultWebsocketURL,
exchange.WebsocketSpotSupplementary: binanceusDefaultWebsocketURL,
}); err != nil {
log.Errorf(log.ExchangeSys,
"%s setting default endpoints error %v",
bi.Name, err)
}
bi.Websocket = stream.New()
bi.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
bi.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
bi.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
// Setup takes in the supplied exchange configuration details and sets params
func (bi *Binanceus) Setup(exch *config.Exchange) error {
err := exch.Validate()
if err != nil {
return err
}
if !exch.Enabled {
bi.SetEnabled(false)
return nil
}
err = bi.SetupDefaults(exch)
if err != nil {
return err
}
ePoint, err := bi.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = bi.Websocket.Setup(&stream.WebsocketSetup{
ExchangeConfig: exch,
DefaultURL: binanceusDefaultWebsocketURL,
RunningURL: ePoint,
Connector: bi.WsConnect,
Subscriber: bi.Subscribe,
Unsubscriber: bi.Unsubscribe,
GenerateSubscriptions: bi.GenerateSubscriptions,
Features: &bi.Features.Supports.WebsocketCapabilities,
OrderbookBufferConfig: buffer.Config{
SortBuffer: true,
SortBufferByUpdateIDs: true,
},
TradeFeed: bi.Features.Enabled.TradeFeed,
})
if err != nil {
return err
}
return bi.Websocket.SetupNewConnection(stream.ConnectionSetup{
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
RateLimit: wsRateLimitMilliseconds,
})
}
// Start starts the Binanceus go routine
func (bi *Binanceus) Start(wg *sync.WaitGroup) error {
if wg == nil {
return fmt.Errorf("%T %w", wg, common.ErrNilPointer)
}
wg.Add(1)
go func() {
bi.Run()
wg.Done()
}()
return nil
}
// Run implements the Binanceus wrapper
func (bi *Binanceus) Run() {
if bi.Verbose {
log.Debugf(log.ExchangeSys,
"%s Websocket: %s.",
bi.Name,
common.IsEnabled(bi.Websocket.IsEnabled()))
bi.PrintEnabledPairs()
}
if !bi.GetEnabledFeatures().AutoPairUpdates {
return
}
err := bi.UpdateTradablePairs(context.TODO(), false)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
bi.Name,
err)
}
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (bi *Binanceus) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string, error) {
if !bi.SupportsAsset(a) {
return nil, fmt.Errorf("asset type of %s is not supported by %s", a, bi.Name)
}
format, err := bi.GetPairFormat(a, false)
if err != nil {
return nil, err
}
tradingStatus := "TRADING"
var info ExchangeInfo
info, err = bi.GetExchangeInfo(ctx)
if err != nil {
return nil, err
}
pairs := make([]string, 0, len(info.Symbols))
for x := range info.Symbols {
if info.Symbols[x].Status != tradingStatus || !info.Symbols[x].IsSpotTradingAllowed {
continue
}
pair := info.Symbols[x].BaseAsset +
format.Delimiter +
info.Symbols[x].QuoteAsset
pairs = append(pairs, pair)
}
return pairs, nil
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (bi *Binanceus) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error {
pairs, err := bi.FetchTradablePairs(ctx, asset.Spot)
if err != nil {
return err
}
p, err := currency.NewPairsFromStrings(pairs)
if err != nil {
return err
}
return bi.UpdatePairs(p, asset.Spot, false, forceUpdate)
}
// UpdateTicker updates and returns the ticker for a currency pair
func (bi *Binanceus) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
if a != asset.Spot {
return nil, fmt.Errorf("%w '%v'", asset.ErrNotSupported, a)
}
tick, err := bi.GetPriceChangeStats(ctx, p)
if err != nil {
return nil, err
}
err = ticker.ProcessTicker(&ticker.Price{
Last: tick.LastPrice,
High: tick.HighPrice,
Low: tick.LowPrice,
Bid: tick.BidPrice,
Ask: tick.AskPrice,
Volume: tick.Volume,
QuoteVolume: tick.QuoteVolume,
Open: tick.OpenPrice,
Close: tick.PrevClosePrice,
Pair: p,
ExchangeName: bi.Name,
AssetType: a,
})
if err != nil {
return nil, err
}
return ticker.GetTicker(bi.Name, p, a)
}
// UpdateTickers updates all currency pairs of a given asset type
func (bi *Binanceus) UpdateTickers(ctx context.Context, a asset.Item) error {
if a != asset.Spot {
return fmt.Errorf("assetType not supported: %v", a)
}
tick, err := bi.GetTickers(ctx)
if err != nil {
return err
}
pairs, err := bi.GetEnabledPairs(a)
if err != nil {
return err
}
for i := range pairs {
for y := range tick {
pairFmt, err := bi.FormatExchangeCurrency(pairs[i], a)
if err != nil {
return err
}
if tick[y].Symbol != pairFmt.String() {
continue
}
err = ticker.ProcessTicker(&ticker.Price{
Last: tick[y].LastPrice,
High: tick[y].HighPrice,
Low: tick[y].LowPrice,
Bid: tick[y].BidPrice,
Ask: tick[y].AskPrice,
Volume: tick[y].Volume,
QuoteVolume: tick[y].QuoteVolume,
Open: tick[y].OpenPrice,
Close: tick[y].PrevClosePrice,
Pair: pairFmt,
ExchangeName: bi.Name,
AssetType: a,
})
if err != nil {
return err
}
}
}
return nil
}
// FetchTicker returns the ticker for a currency pair
func (bi *Binanceus) FetchTicker(ctx context.Context, p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
fPairs, er := bi.FormatExchangeCurrency(p, assetType)
if er != nil {
return nil, er
}
tickerNew, er := ticker.GetTicker(bi.Name, fPairs, assetType)
if er != nil {
return bi.UpdateTicker(ctx, p, assetType)
}
return tickerNew, nil
}
// FetchOrderbook returns orderbook base on the currency pair
func (bi *Binanceus) FetchOrderbook(ctx context.Context, pair currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
fPair, err := bi.FormatExchangeCurrency(pair, assetType)
if err != nil {
return nil, err
}
ob, err := orderbook.Get(bi.Name, fPair, assetType)
if err != nil {
return bi.UpdateOrderbook(ctx, pair, assetType)
}
return ob, nil
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (bi *Binanceus) UpdateOrderbook(ctx context.Context, pair currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
book := &orderbook.Base{
Exchange: bi.Name,
Pair: pair,
Asset: assetType,
VerifyOrderbook: bi.CanVerifyOrderbook,
}
orderbookNew, err := bi.GetOrderBookDepth(ctx, &OrderBookDataRequestParams{
Symbol: pair,
Limit: 1000})
if err != nil {
return book, err
}
book.Bids = make([]orderbook.Item, len(orderbookNew.Bids))
for x := range orderbookNew.Bids {
book.Bids[x] = orderbook.Item{
Amount: orderbookNew.Bids[x].Quantity,
Price: orderbookNew.Bids[x].Price,
}
}
book.Asks = make([]orderbook.Item, len(orderbookNew.Asks))
for x := range orderbookNew.Asks {
book.Asks[x] = orderbook.Item{
Amount: orderbookNew.Asks[x].Quantity,
Price: orderbookNew.Asks[x].Price,
}
}
err = book.Process()
if err != nil {
return book, err
}
return orderbook.Get(bi.Name, pair, assetType)
}
// UpdateAccountInfo retrieves balances for all enabled currencies
func (bi *Binanceus) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
var acc account.SubAccount
info.Exchange = bi.Name
if assetType != asset.Spot {
return info, fmt.Errorf("%v assetType is not supported", assetType)
}
theAccount, err := bi.GetAccount(ctx)
if err != nil {
return info, err
}
currencyBalance := make([]account.Balance, len(theAccount.Balances))
for i := range theAccount.Balances {
freeBalance := theAccount.Balances[i].Free.InexactFloat64()
locked := theAccount.Balances[i].Locked.InexactFloat64()
currencyBalance[i] = account.Balance{
CurrencyName: currency.NewCode(theAccount.Balances[i].Asset),
Total: freeBalance + locked,
Hold: locked,
Free: freeBalance,
}
}
acc.Currencies = currencyBalance
acc.AssetType = assetType
info.Accounts = append(info.Accounts, acc)
creds, err := bi.GetCredentials(ctx)
if err != nil {
return info, err
}
if err := account.Process(&info, creds); err != nil {
return account.Holdings{}, err
}
return info, nil
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (bi *Binanceus) FetchAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
creds, err := bi.GetCredentials(ctx)
if err != nil {
return account.Holdings{}, err
}
acc, err := account.GetHoldings(bi.Name, creds, assetType)
if err != nil {
return bi.UpdateAccountInfo(ctx, assetType)
}
return acc, nil
}
// GetFundingHistory returns funding history, deposits and withdrawals
func (bi *Binanceus) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
// GetWithdrawalsHistory returns previous withdrawals data
func (bi *Binanceus) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
w, err := bi.WithdrawalHistory(ctx, c, "", time.Time{}, time.Time{}, 0, 10000)
if err != nil {
return nil, err
}
for i := range w {
tm, err := time.Parse(binanceUSAPITimeLayout, w[i].ApplyTime)
if err != nil {
return nil, err
}
resp = append(resp, exchange.WithdrawalHistory{
Status: fmt.Sprint(w[i].Status),
TransferID: w[i].ID,
Currency: w[i].Coin,
Amount: w[i].Amount,
Fee: w[i].TransactionFee,
CryptoToAddress: w[i].Address,
CryptoTxID: w[i].ID,
CryptoChain: w[i].Network,
Timestamp: tm,
})
}
return resp, nil
}
// GetRecentTrades returns the most recent trades for a currency and asset
func (bi *Binanceus) GetRecentTrades(ctx context.Context, p currency.Pair, assetType asset.Item) ([]trade.Data, error) {
const limit = 1000
tradeData, err := bi.GetMostRecentTrades(ctx, RecentTradeRequestParams{p, limit})
if err != nil {
return nil, err
}
resp := make([]trade.Data, len(tradeData))
for i := range tradeData {
resp[i] = trade.Data{
TID: fmt.Sprint(tradeData[i].ID),
Exchange: bi.Name,
AssetType: assetType,
CurrencyPair: p,
Price: tradeData[i].Price,
Amount: tradeData[i].Quantity,
Timestamp: tradeData[i].Time,
}
}
if bi.IsSaveTradeDataEnabled() {
err := trade.AddTradesToBuffer(bi.Name, resp...)
if err != nil {
return nil, err
}
}
sort.Sort(trade.ByDate(resp))
return resp, nil
}
// GetHistoricTrades returns historic trade data within the timeframe provided
func (bi *Binanceus) GetHistoricTrades(ctx context.Context, p currency.Pair,
assetType asset.Item, timestampStart, timestampEnd time.Time) ([]trade.Data, error) {
req := AggregatedTradeRequestParams{
Symbol: p,
StartTime: uint64(timestampStart.UnixMilli()),
EndTime: uint64(timestampEnd.UnixMilli()),
}
trades, err := bi.GetAggregateTrades(ctx, &req)
if err != nil {
return nil, err
}
result := make([]trade.Data, len(trades))
exName := bi.Name
for i := range trades {
t := trades[i].toTradeData(p, exName, assetType)
result[i] = *t
}
return result, nil
}
// SubmitOrder submits a new order
func (bi *Binanceus) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
var timeInForce RequestParamsTimeForceType
var sideType string
err := s.Validate()
if err != nil {
return nil, err
}
if s.AssetType != asset.Spot {
return nil, fmt.Errorf("%s %w", s.AssetType, asset.ErrNotSupported)
}
if s.Side == order.Buy {
sideType = order.Buy.String()
} else {
sideType = order.Sell.String()
}
var requestParamOrderType RequestParamsOrderType
switch s.Type {
case order.Market:
requestParamOrderType = BinanceRequestParamsOrderMarket
case order.Limit:
timeInForce = BinanceRequestParamsTimeGTC
requestParamOrderType = BinanceRequestParamsOrderLimit
default:
return nil, errors.New(bi.Name + " unsupported order type")
}
var response NewOrderResponse
response, err = bi.NewOrder(ctx, &NewOrderRequest{
Symbol: s.Pair,
Side: sideType,
Price: s.Price,
Quantity: s.Amount,
TradeType: requestParamOrderType,
TimeInForce: timeInForce,
NewClientOrderID: s.ClientOrderID,
})
if err != nil {
return nil, err
}
if response.OrderID > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10)
}
if response.ExecutedQty == response.OrigQty {
submitOrderResponse.Status = order.Filled
}
for i := range response.Fills {
submitOrderResponse.Trades = append(submitOrderResponse.Trades, order.TradeHistory{
Price: response.Fills[i].Price,
Amount: response.Fills[i].Qty,
Fee: response.Fills[i].Commission,
FeeAsset: response.Fills[i].CommissionAsset,
Exchange: bi.Name,
})
}
return &submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (bi *Binanceus) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// CancelOrder cancels an order by its corresponding ID number
func (bi *Binanceus) CancelOrder(ctx context.Context, o *order.Cancel) error {
if err := o.Validate(o.StandardCancel()); err != nil {
return err
}
if o.AssetType != asset.Spot {
return fmt.Errorf("%w '%v'", asset.ErrNotSupported, o.AssetType)
}
_, err := bi.CancelExistingOrder(ctx,
&CancelOrderRequestParams{
Symbol: o.Pair,
OrderID: o.OrderID,
ClientSuppliedOrderID: o.ClientOrderID,
})
return err
}
// CancelBatchOrders cancels orders by their corresponding ID numbers
func (bi *Binanceus) CancelBatchOrders(ctx context.Context, orders []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrFunctionNotSupported
}
// CancelAllOrders cancels all orders associated with a currency pair
func (bi *Binanceus) CancelAllOrders(ctx context.Context, orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {
return order.CancelAllResponse{}, err
}
var cancelAllOrdersResponse order.CancelAllResponse
cancelAllOrdersResponse.Status = make(map[string]string)
if orderCancellation.AssetType == asset.Spot {
symbolValue, err := bi.FormatSymbol(orderCancellation.Pair, asset.Spot)
if err != nil {
return cancelAllOrdersResponse, err
}
openOrders, err := bi.GetAllOpenOrders(ctx, symbolValue)
if err != nil {
return cancelAllOrdersResponse, err
}
for ind := range openOrders {
pair, err := currency.NewPairFromString(openOrders[ind].Symbol)
if err != nil {
return cancelAllOrdersResponse, err
}
_, err = bi.CancelExistingOrder(ctx, &CancelOrderRequestParams{
Symbol: pair,
OrderID: strconv.FormatUint(openOrders[ind].OrderID, 10),
ClientSuppliedOrderID: openOrders[ind].ClientOrderID,
})
if err != nil {
return cancelAllOrdersResponse, err
}
}
} else {
return cancelAllOrdersResponse, fmt.Errorf("%w '%v'", asset.ErrNotSupported, orderCancellation.AssetType)
}
return cancelAllOrdersResponse, nil
}
// GetOrderInfo returns order information based on order ID
func (bi *Binanceus) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) {
var respData order.Detail
orderIDInt, err := strconv.ParseInt(orderID, 10, 64)
if err != nil {
return respData, fmt.Errorf("invalid orderID %w", err)
}
symbolValue, err := bi.FormatSymbol(pair, asset.Spot)
if err != nil {
return respData, err
}
if assetType != asset.Spot {
return respData, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported)
}
var orderType order.Type
resp, err := bi.GetOrder(ctx, &OrderRequestParams{
Symbol: symbolValue,
OrderID: uint64(orderIDInt),
})
if err != nil {
return respData, err
}
orderSide, err := order.StringToOrderSide(resp.Side)
if err != nil {
log.Errorf(log.ExchangeSys, "%s %v", bi.Name, err)
}
status, err := order.StringToOrderStatus(resp.Status)
if err != nil {
log.Errorf(log.ExchangeSys, "%s %v", bi.Name, err)
}
orderType, err = order.StringToOrderType(resp.Type)
if err != nil {
log.Errorf(log.ExchangeSys, "%s %v", bi.Name, err)
}
return order.Detail{
Amount: resp.OrigQty,
Exchange: bi.Name,
OrderID: strconv.FormatInt(int64(resp.OrderID), 10),
ClientOrderID: resp.ClientOrderID,
Side: orderSide,
Type: orderType,
Pair: pair,
Cost: resp.CummulativeQuoteQty,
AssetType: assetType,
Status: status,
Price: resp.Price,
ExecutedAmount: resp.ExecutedQty,
Date: resp.Time,
LastUpdated: resp.UpdateTime,
}, nil
}
// GetDepositAddress returns a deposit address for a specified currency
func (bi *Binanceus) GetDepositAddress(ctx context.Context, c currency.Code, _ /*accountID*/, chain string) (*deposit.Address, error) {
address, err := bi.GetDepositAddressForCurrency(ctx, c.String(), chain)
if err != nil {
return nil, err
}
return &deposit.Address{
Address: address.Address,
Tag: address.Tag,
}, nil
}
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is submitted
func (bi *Binanceus) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
if err := withdrawRequest.Validate(); err != nil {
return nil, err
}
withdrawID, err := bi.WithdrawCrypto(ctx, withdrawRequest)
if err != nil {
return nil, err
}
return &withdraw.ExchangeResponse{
ID: withdrawID,
}, nil
}
// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is submitted. But, GCT has no concept of withdrawal via SEN
// the fiat withdrawal end point of Binance.US is built to submit a USD withdraw request via Silvergate Exchange Network (SEN).
// So, this method is not implemented.
func (bi *Binanceus) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
return nil, common.ErrNotYetImplemented
}
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a withdrawal is submitted
// But, GCT has no concept of withdrawal via SEN the fiat withdrawal end point of Binance.US is built to submit a USD withdraw request via Silvergate Exchange Network (SEN).
func (bi *Binanceus) WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
return nil, common.ErrNotYetImplemented
}
// GetActiveOrders retrieves any orders that are active/open
func (bi *Binanceus) GetActiveOrders(ctx context.Context, getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
if err := getOrdersRequest.Validate(); err != nil {
return nil, err
}
var err error
var symbol string
var pair currency.Pair
var selectedOrders []Order
if getOrdersRequest.AssetType != asset.Spot {
return nil, fmt.Errorf("%s %w", getOrdersRequest.AssetType, asset.ErrNotSupported)
}
if len(getOrdersRequest.Pairs) != 1 {
symbol = ""
} else {
symbol, err = bi.FormatSymbol(getOrdersRequest.Pairs[0], asset.Spot)
if err != nil {
return nil, err
}
}
resp, err := bi.GetAllOpenOrders(ctx, symbol)
if err != nil {
return nil, err
}
for s := range resp {
ord := resp[s]
pair, err = currency.NewPairFromString(ord.Symbol)
if err != nil {
continue
}
for p := range getOrdersRequest.Pairs {
if getOrdersRequest.Pairs[p].Equal(pair) {
selectedOrders = append(selectedOrders, ord)
}
}
}
orders := make([]order.Detail, len(selectedOrders))
for x := range selectedOrders {
var orderSide order.Side
var orderType order.Type
var orderStatus order.Status
orderSide, err = order.StringToOrderSide(strings.ToUpper(resp[x].Side))
if err != nil {
log.Errorf(log.ExchangeSys, "%s %v", bi.Name, err)
}
orderType, err = order.StringToOrderType(strings.ToUpper(resp[x].Type))
if err != nil {
log.Errorf(log.ExchangeSys, "%s %v", bi.Name, err)
}
orderStatus, err = order.StringToOrderStatus(resp[x].Status)
if err != nil {
log.Errorf(log.ExchangeSys, "%s %v", bi.Name, err)
}
orders[x] = order.Detail{
Amount: resp[x].OrigQty,
Date: resp[x].Time,
Exchange: bi.Name,
OrderID: strconv.FormatInt(int64(resp[x].OrderID), 10),
ClientOrderID: resp[x].ClientOrderID,
Side: orderSide,
Type: orderType,
Price: resp[x].Price,
Status: orderStatus,
Pair: getOrdersRequest.Pairs[0],
AssetType: getOrdersRequest.AssetType,
LastUpdated: resp[x].UpdateTime,
}
}
order.FilterOrdersByPairs(&orders, getOrdersRequest.Pairs)
order.FilterOrdersByType(&orders, getOrdersRequest.Type)
order.FilterOrdersBySide(&orders, getOrdersRequest.Side)
err = order.FilterOrdersByTimeRange(&orders, getOrdersRequest.StartTime, getOrdersRequest.EndTime)
return orders, err
}
// GetOrderHistory retrieves account order information Can Limit response to specific order status
func (bi *Binanceus) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
// An endpoint like /api/v3/allOrders does not exist in the binance us
// so This end point is left unimplemented
return nil, common.ErrFunctionNotSupported
}
// GetFeeByType returns an estimate of fee based on the type of transaction
func (bi *Binanceus) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
if feeBuilder == nil {
return 0, fmt.Errorf("%T %w", feeBuilder, common.ErrNilPointer)
}
if (!bi.AreCredentialsValid(ctx) || bi.SkipAuthCheck) &&
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}
return bi.GetFee(ctx, feeBuilder)
}
// ValidateCredentials validates current credentials used for wrapper
func (bi *Binanceus) ValidateCredentials(ctx context.Context, assetType asset.Item) error {
_, err := bi.UpdateAccountInfo(ctx, assetType)
return bi.CheckTransientError(err)
}
// GetHistoricCandles returns candles between a time period for a set time interval
func (bi *Binanceus) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
if err := bi.ValidateKline(pair, a, interval); err != nil {
return kline.Item{}, err
}
if kline.TotalCandlesPerInterval(start, end, interval) > float64(bi.Features.Enabled.Kline.ResultLimit) {
return kline.Item{}, errors.New(kline.ErrRequestExceedsExchangeLimits)
}
req := KlinesRequestParams{
Interval: bi.GetIntervalEnum(interval),
Symbol: pair,
StartTime: start,
EndTime: end,
Limit: int64(bi.Features.Enabled.Kline.ResultLimit),
}
ret := kline.Item{
Exchange: bi.Name,
Pair: pair,
Asset: a,
Interval: interval,
}
candles, err := bi.GetSpotKline(ctx, &req)
if err != nil {
return kline.Item{}, err
}
for x := range candles {
ret.Candles = append(ret.Candles, kline.Candle{
Time: candles[x].OpenTime,
Open: candles[x].Open,
High: candles[x].High,
Low: candles[x].Low,
Close: candles[x].Close,
Volume: candles[x].Volume,
})
}
ret.SortCandlesByTimestamp(false)
return ret, nil
}
// GetHistoricCandlesExtended returns candles between a time period for a set time interval
func (bi *Binanceus) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
if err := bi.ValidateKline(pair, a, interval); err != nil {
return kline.Item{}, err
}
ret := kline.Item{
Exchange: bi.Name,
Pair: pair,
Asset: a,
Interval: interval,
}
dates, err := kline.CalculateCandleDateRanges(start, end, interval, bi.Features.Enabled.Kline.ResultLimit)
if err != nil {
return kline.Item{}, err
}
var candles []CandleStick
for x := range dates.Ranges {
req := KlinesRequestParams{
Interval: bi.GetIntervalEnum(interval),
Symbol: pair,
StartTime: dates.Ranges[x].Start.Time,
EndTime: dates.Ranges[x].End.Time,
Limit: int64(bi.Features.Enabled.Kline.ResultLimit),
}
candles, err = bi.GetSpotKline(ctx, &req)
if err != nil {
return kline.Item{}, err
}
for i := range candles {
for j := range ret.Candles {
if ret.Candles[j].Time.Equal(candles[i].OpenTime) {
continue
}
}
ret.Candles = append(ret.Candles, kline.Candle{
Time: candles[i].OpenTime,
Open: candles[i].Open,
High: candles[i].High,
Low: candles[i].Low,
Close: candles[i].Close,
Volume: candles[i].Volume,
})
}
}
dates.SetHasDataFromCandles(ret.Candles)
summary := dates.DataSummary(false)
if len(summary) > 0 {
log.Warnf(log.ExchangeSys, "%v - %v", bi.Name, summary)
}
ret.RemoveDuplicates()
ret.RemoveOutsideRange(start, end)
ret.SortCandlesByTimestamp(false)
return ret, nil
}
// GetAvailableTransferChains returns the available transfer blockchains for the specific
// cryptocurrency
func (bi *Binanceus) GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error) {
coinInfo, err := bi.GetAssetFeesAndWalletStatus(ctx)
if err != nil {
return nil, err
}
var availableChains []string
for x := range coinInfo {
if strings.EqualFold(coinInfo[x].Coin, cryptocurrency.String()) {
for y := range coinInfo[x].NetworkList {
if coinInfo[x].NetworkList[y].DepositEnable {
availableChains = append(availableChains, coinInfo[x].NetworkList[y].Network)
}
}
}
}
return availableChains, nil
}

View File

@@ -0,0 +1,130 @@
package binanceus
import (
"context"
"fmt"
"time"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"golang.org/x/time/rate"
)
const (
spotInterval = time.Minute
spotRequestRate = 1200
// Order related limits which are segregated from the global rate limits
// 100 requests per 10 seconds and max 100000 requests per day.
spotOrderInterval = 10 * time.Second
spotOrderRequestRate = 100
)
// Binance Spot rate limits
const (
spotDefaultRate request.EndpointLimit = iota
spotExchangeInfo
spotHistoricalTradesRate
spotOrderbookDepth500Rate
spotOrderbookDepth1000Rate
spotOrderbookDepth5000Rate
spotOrderbookTickerAllRate
spotPriceChangeAllRate
spotSymbolPriceAllRate
spotSingleOCOOrderRate
spotOpenOrdersAllRate
spotOpenOrdersSpecificRate
spotOrderRate
spotOrderQueryRate
spotTradesQueryRate
spotAllOrdersRate
spotAllOCOOrdersRate
spotOrderRateLimitRate
spotAccountInformationRate
)
// RateLimit implements the request.Limiter interface
type RateLimit struct {
SpotRate *rate.Limiter
SpotOrdersRate *rate.Limiter
}
// Limit executes rate limiting functionality for Binance
func (r *RateLimit) Limit(ctx context.Context, f request.EndpointLimit) error {
var limiter *rate.Limiter
var tokens int
switch f {
case spotDefaultRate:
limiter, tokens = r.SpotRate, 1
case spotOrderbookTickerAllRate,
spotSymbolPriceAllRate:
limiter, tokens = r.SpotRate, 2
case spotHistoricalTradesRate,
spotOrderbookDepth500Rate:
limiter, tokens = r.SpotRate, 5
case spotOrderbookDepth1000Rate,
spotAccountInformationRate,
spotExchangeInfo,
spotTradesQueryRate:
limiter, tokens = r.SpotRate, 10
case spotPriceChangeAllRate:
limiter, tokens = r.SpotRate, 40
case spotOrderbookDepth5000Rate:
limiter, tokens = r.SpotRate, 50
case spotOrderRate:
limiter, tokens = r.SpotOrdersRate, 1
case spotOrderQueryRate,
spotSingleOCOOrderRate:
limiter, tokens = r.SpotOrdersRate, 2
case spotOpenOrdersSpecificRate:
limiter, tokens = r.SpotOrdersRate, 3
case spotAllOrdersRate,
spotAllOCOOrdersRate:
limiter, tokens = r.SpotOrdersRate, 10
case spotOrderRateLimitRate:
limiter, tokens = r.SpotOrdersRate, 20
case spotOpenOrdersAllRate:
limiter, tokens = r.SpotOrdersRate, 40
default:
limiter, tokens = r.SpotRate, 1
}
var finalDelay time.Duration
var reserves = make([]*rate.Reservation, tokens)
for i := 0; i < tokens; i++ {
// Consume tokens 1 at a time as this avoids needing burst capacity in the limiter,
// which would otherwise allow the rate limit to be exceeded over short periods
reserves[i] = limiter.Reserve()
finalDelay = reserves[i].Delay()
}
if dl, ok := ctx.Deadline(); ok && dl.Before(time.Now().Add(finalDelay)) {
// Cancel all potential reservations to free up rate limiter if deadline
// is exceeded.
for x := range reserves {
reserves[x].Cancel()
}
return fmt.Errorf("rate limit delay of %s will exceed deadline: %w",
finalDelay,
context.DeadlineExceeded)
}
time.Sleep(finalDelay)
return nil
}
// SetRateLimit returns the rate limit for the exchange
func SetRateLimit() *RateLimit {
return &RateLimit{
SpotRate: request.NewRateLimit(spotInterval, spotRequestRate),
SpotOrdersRate: request.NewRateLimit(spotOrderInterval, spotOrderRequestRate),
}
}
// orderbookLimit returns the endpoint rate limit representing enum given order depth
func orderbookLimit(depth int64) request.EndpointLimit {
switch {
case depth <= 100:
return spotDefaultRate
case depth <= 500:
return spotOrderbookDepth500Rate
case depth <= 1000:
return spotOrderbookDepth1000Rate
}
return spotOrderbookDepth5000Rate
}

View File

@@ -0,0 +1,617 @@
package binanceus
import (
"encoding/json"
"time"
)
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *RecentTrade) UnmarshalJSON(data []byte) error {
type Alias RecentTrade
chil := &struct {
Time int64 `json:"time"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.Time > 0 {
a.Time = time.UnixMilli(chil.Time)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *HistoricalTrade) UnmarshalJSON(data []byte) error {
type Alias HistoricalTrade
chil := &struct {
Time int64 `json:"time"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.Time > 0 {
a.Time = time.UnixMilli(chil.Time)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *AggregatedTrade) UnmarshalJSON(data []byte) error {
type Alias AggregatedTrade
chil := &struct {
TimeStamp int64 `json:"T"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.TimeStamp > 0 {
a.TimeStamp = time.UnixMilli(chil.TimeStamp)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *PriceChangeStats) UnmarshalJSON(data []byte) error {
type Alias PriceChangeStats
chil := &struct {
OpenTime int64 `json:"openTime"`
CloseTime int64 `json:"closeTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.OpenTime > 0 {
a.OpenTime = time.UnixMilli(chil.OpenTime)
}
if chil.CloseTime > 0 {
a.CloseTime = time.UnixMilli(chil.CloseTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *TradeStatus) UnmarshalJSON(data []byte) error {
type Alias TradeStatus
chil := &struct {
UpdateTime int64 `json:"updateTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.UpdateTime > 0 {
a.UpdateTime = time.UnixMilli(chil.UpdateTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *SubAccount) UnmarshalJSON(data []byte) error {
type Alias SubAccount
chil := &struct {
CreateTime int64 `json:"createTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.CreateTime > 0 {
a.CreateTime = time.UnixMilli(chil.CreateTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *Account) UnmarshalJSON(data []byte) error {
type Alias Account
chil := &struct {
UpdateTime int64 `json:"updateTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.UpdateTime > 0 {
a.UpdateTime = time.UnixMilli(chil.UpdateTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *NewOrderResponse) UnmarshalJSON(data []byte) error {
type Alias NewOrderResponse
aux := &struct {
TransactionTime binanceusTime `json:"transactTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
if aux != nil {
a.TransactionTime = aux.TransactionTime.Time()
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *TransferHistory) UnmarshalJSON(data []byte) error {
type Alias TransferHistory
aux := &struct {
TimeStamp uint64 `json:"time"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if aux.TimeStamp == 0 {
a.TimeStamp = time.UnixMilli(int64(aux.TimeStamp))
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the server Time timestamp
func (a *ExchangeInfo) UnmarshalJSON(data []byte) error {
type Alias ExchangeInfo
chil := &struct {
Servertime uint64 `json:"serverTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.Servertime > 0 {
a.ServerTime = time.UnixMilli(int64(chil.Servertime))
}
return nil
}
// UnmarshalJSON deserialises the JSON infos, including the order time and update time timestamps
func (a *Order) UnmarshalJSON(data []byte) error {
type Alias Order
chil := &struct {
Time int64 `json:"time"`
UpdateTime int64 `json:"updateTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.Time > 0 {
a.Time = time.UnixMilli(chil.Time)
}
if chil.UpdateTime > 0 {
a.UpdateTime = time.UnixMilli(chil.UpdateTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *Trade) UnmarshalJSON(data []byte) error {
type Alie Trade
chil := &struct {
Time int64 `json:"time"`
*Alie
}{
Alie: (*Alie)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.Time > 0 {
a.Time = time.UnixMilli(chil.Time)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the ( TransactionTime )timestamp
func (a *OCOOrderReportItem) UnmarshalJSON(data []byte) error {
type Alias OCOOrderReportItem
chil := &struct {
*Alias
TransactionTime int64 `json:"transactionTime"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.TransactionTime > 0 {
a.TransactionTime = time.UnixMilli(chil.TransactionTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (TransactioTime) timestamp
func (a *OCOOrderResponse) UnmarshalJSON(data []byte) error {
type Alias OCOOrderResponse
chil := &struct {
*Alias
TransactionTime int64 `json:"transactionTime"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.TransactionTime > 0 {
a.TransactionTime = time.UnixMilli(chil.TransactionTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (Create Time) timestamp
func (a *OTCTradeOrderResponse) UnmarshalJSON(data []byte) error {
type Alias OTCTradeOrderResponse
chil := &struct {
CreateTime int64 `json:"createTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.CreateTime > 0 {
a.CreateTime = time.UnixMilli(chil.CreateTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (Create Time) timestamp
func (a *OTCTradeOrder) UnmarshalJSON(data []byte) error {
type Alias OTCTradeOrder
chil := &struct {
CreateTime int64 `json:"createTime"`
*Alias
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.CreateTime > 0 {
a.CreateTime = time.UnixMilli(chil.CreateTime)
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (EventTime , and TransactionTime) timestamp
func (a *WsListStatus) UnmarshalJSON(data []byte) error {
type Alias WsListStatus
aux := &struct {
Data struct {
EventTime binanceusTime `json:"E"`
TransactionTime binanceusTime `json:"T"`
*WsListStatusData
} `json:"data"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.Data = *aux.Data.WsListStatusData
a.Data.EventTime = aux.Data.EventTime.Time()
a.Data.TransactionTime = aux.Data.TransactionTime.Time()
return nil
}
// UnmarshalJSON deserialises the JSON info, including (EventTime , OpenTime, and TransactionTime) timestamp
func (a *TickerStream) UnmarshalJSON(data []byte) error {
type Alias TickerStream
aux := &struct {
EventTime binanceusTime `json:"E"`
OpenTime binanceusTime `json:"O"`
CloseTime binanceusTime `json:"C"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.EventTime = aux.EventTime.Time()
a.OpenTime = aux.OpenTime.Time()
a.CloseTime = aux.CloseTime.Time()
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *KlineStream) UnmarshalJSON(data []byte) error {
type Alias KlineStream
aux := &struct {
EventTime binanceusTime `json:"E"`
Kline struct {
StartTime binanceusTime `json:"t"`
CloseTime binanceusTime `json:"T"`
*KlineStreamData
} `json:"k"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.Kline = *aux.Kline.KlineStreamData
a.EventTime = aux.EventTime.Time()
a.Kline.StartTime = aux.Kline.StartTime.Time()
a.Kline.CloseTime = aux.Kline.CloseTime.Time()
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (Timesamp and EventTime) timestamp
func (a *TradeStream) UnmarshalJSON(data []byte) error {
type Alias TradeStream
aux := &struct {
TimeStamp binanceusTime `json:"T"`
EventTime binanceusTime `json:"E"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.TimeStamp = aux.TimeStamp.Time()
a.EventTime = aux.EventTime.Time()
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (EventTime, OrderCreationTime, and TransactionTime)timestamp
func (a *wsOrderUpdate) UnmarshalJSON(data []byte) error {
type Alias wsOrderUpdate
aux := &struct {
Data struct {
EventTime binanceusTime `json:"E"`
OrderCreationTime binanceusTime `json:"O"`
TransactionTime binanceusTime `json:"T"`
*WsOrderUpdateData
} `json:"data"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.Data = *aux.Data.WsOrderUpdateData
a.Data.EventTime = aux.Data.EventTime.Time()
a.Data.OrderCreationTime = aux.Data.OrderCreationTime.Time()
a.Data.TransactionTime = aux.Data.TransactionTime.Time()
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (EventTime and ClearTime) timestamp
func (a *wsBalanceUpdate) UnmarshalJSON(data []byte) error {
type Alias wsBalanceUpdate
aux := &struct {
Data struct {
EventTime binanceusTime `json:"E"`
ClearTime binanceusTime `json:"T"`
*WsBalanceUpdateData
} `json:"data"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.Data = *aux.Data.WsBalanceUpdateData
a.Data.EventTime = aux.Data.EventTime.Time()
a.Data.ClearTime = aux.Data.ClearTime.Time()
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (EventTime and LastUpdated) timestamp
func (a *wsAccountPosition) UnmarshalJSON(data []byte) error {
type Alias wsAccountPosition
aux := &struct {
Data struct {
EventTime binanceusTime `json:"E"`
LastUpdated binanceusTime `json:"u"`
*WsAccountPositionData
} `json:"data"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.Data = *aux.Data.WsAccountPositionData
a.Data.EventTime = aux.Data.EventTime.Time()
a.Data.LastUpdated = aux.Data.LastUpdated.Time()
return nil
}
// UnmarshalJSON deserialises the JSON info, including the (Timestamp)timestamp
func (a *WebsocketDepthStream) UnmarshalJSON(data []byte) error {
type Alias WebsocketDepthStream
aux := &struct {
Timestamp binanceusTime `json:"E"`
*Alias
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
a.Timestamp = aux.Timestamp.Time()
return nil
}
// UnmarshalJSON .. .
func (a *WebsocketAggregateTradeStream) UnmarshalJSON(data []byte) error {
type Alias WebsocketAggregateTradeStream
chil := &struct {
*Alias
TradeTime int64 `json:"T"`
EventTime int64 `json:"E"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
if chil.TradeTime > 0 {
a.TradeTime = time.UnixMilli(chil.TradeTime)
}
if chil.EventTime > 0 {
a.EventTime = time.UnixMilli(chil.EventTime)
}
return nil
}
// binanceTime provides an internal conversion helper
type binanceusTime time.Time
func (t *binanceusTime) UnmarshalJSON(data []byte) error {
var timestamp int64
if err := json.Unmarshal(data, &timestamp); err != nil {
return err
}
*t = binanceusTime(time.UnixMilli(timestamp))
return nil
}
// Time returns a time.Time object
func (t binanceusTime) Time() time.Time {
return time.Time(t)
}
// UnmarshalJSON deserialises createTime timestamp to built in time.
func (a *OCBSOrder) UnmarshalJSON(data []byte) error {
type Alias OCBSOrder
chil := &struct {
*Alias
CreateTime int64 `json:"createTime"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.CreateTime > 0 {
a.CreateTime = time.UnixMilli(chil.CreateTime)
}
return nil
}
// UnmarshalJSON deserialises createTime timestamp to built in time.
func (a *ServerTime) UnmarshalJSON(data []byte) error {
type Alias ServerTime
chil := &struct {
*Alias
Timestamp int64 `json:"serverTime"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.Timestamp > 0 {
a.Timestamp = time.UnixMilli(chil.Timestamp)
}
return nil
}
// UnmarshalJSON deserialises createTime timestamp to built in time.
func (a *SubAccountStatus) UnmarshalJSON(data []byte) error {
type Alias SubAccountStatus
chil := &struct {
*Alias
InsertTime int64 `json:"insertTime"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.InsertTime > 0 {
a.InsertTime = time.UnixMilli(chil.InsertTime)
}
return nil
}
// UnmarshalJSON deserialises ValidTimestamp timestamp to built in time.Time instance.
func (a *Quote) UnmarshalJSON(data []byte) error {
type Alias Quote
chil := &struct {
*Alias
ValidTimestamp int64 `json:"validTimestamp"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.ValidTimestamp > 0 {
a.ValidTimestamp = time.UnixMilli(chil.ValidTimestamp)
}
return nil
}
// UnmarshalJSON deserialises createTime timestamp to built in time.
func (a *SubAccountDepositItem) UnmarshalJSON(data []byte) error {
type Alias SubAccountDepositItem
chil := &struct {
*Alias
InsertTime int64 `json:"insertTime"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.InsertTime > 0 {
a.InsertTime = time.UnixMilli(chil.InsertTime)
}
return nil
}
// UnmarshalJSON deserialises createTime timestamp to built in time.
func (a *ReferralWithdrawalItem) UnmarshalJSON(data []byte) error {
type Alias ReferralWithdrawalItem
chil := &struct {
*Alias
ReceiveDateTime int64 `json:"receiveDateTime"`
}{
Alias: (*Alias)(a),
}
if er := json.Unmarshal(data, chil); er != nil {
return er
}
if chil.ReceiveDateTime > 0 {
a.ReceiveDateTime = time.UnixMilli(chil.ReceiveDateTime)
}
return nil
}

View File

@@ -14,6 +14,7 @@ func IsSupported(exchangeName string) bool {
// Exchanges stores a list of supported exchanges
var Exchanges = []string{
"binanceus",
"binance",
"bitfinex",
"bitflyer",

View File

@@ -60,6 +60,7 @@ _b in this context is an `IBotExchange` implemented struct_
| Exchange | Recent Trades via REST | Live trade updates via Websocket | Trade history via REST |
|----------|------|-----------|-----|
| Alphapoint | No | No | No |
| Binance.US | Yes | Yes | NA |
| Binance| Yes | Yes | Yes |
| Bitfinex | Yes | Yes | Yes |
| Bitflyer | Yes | No | No |

View File

@@ -91,6 +91,9 @@ type Request struct {
Amount float64 `json:"amount"`
Type RequestType `json:"type"`
// Used exclusively in Binance.US
ClientOrderID string `json:"clientID"`
TradePassword string
OneTimePassword int64
PIN int64

View File

@@ -238,6 +238,90 @@
]
},
"exchanges": [
{
"name": "Binanceus",
"enabled": true,
"verbose": false,
"httpTimeout": 15000000000,
"websocketResponseCheckTimeout": 30000000,
"websocketResponseMaxLimit": 7000000000,
"websocketTrafficTimeout": 30000000000,
"baseCurrencies": "USD",
"currencyPairs": {
"bypassConfigFormatUpgrades": false,
"pairs": {
"spot": {
"assetEnabled": true,
"enabled": "BTC-USDT,ETH-USDT,LTC-USDT,ADA-USDT",
"available": "BTC-USD,BCH-USD,LTC-USD,USDT-USD,BTC-USDT,ETH-USDT,BCH-USDT,LTC-USDT,BNB-USD,BNB-USDT,ETH-BTC,BNB-BTC,LTC-BTC,BCH-BTC,ADA-USD,BAT-USD,ETC-USD,XLM-USD,ZRX-USD,ADA-USDT,BAT-USDT,ETC-USDT,XLM-USDT,ZRX-USDT,LINK-USD,RVN-USD,DASH-USD,ZEC-USD,ALGO-USD,IOTA-USD,BUSD-USD,BTCB-USD,DOGE-USDT,WAVES-USD,ATOM-USDT,ATOM-USD,NEO-USDT,NEO-USD,VET-USDT,QTUM-USDT,QTUM-USD,ICX-USD,ENJ-USD,ONT-USD,ONT-USDT,ZIL-USD,ZILB-USD,VET-USD,BNBB-USD,ETHB-USD,ALGO-BUSD,XTZ-USD,XTZ-BUSD,HBAR-USD,HBAR-BUSD,OMG-USD,OMG-BUSD,MATIC-USD,MATIC-BUSD,XTZ-BTC,ADA-BTC,REP-BUSD,REP-USD,EOS-BUSD,EOS-USD,DOGE-USD,KNC-USD,KNC-USDT,VTHO-USDT,VTHO-USD,USDC-USD,COMP-USDT,COMP-USD,MANA-USD,HNT-USD,HNT-USDT,MKR-USD,MKR-USDT,DAI-USD,ONE-USDT,ONE-USD,BAND-USDT,BAND-USD,STORJ-USDT,STORJ-USD,UNI-USD,UNI-USDT,SOL-USD,SOL-USDT,LINK-BTC,VET-BTC,UNI-BTC,EGLD-USDT,EGLD-USD,PAXG-USDT,PAXG-USD,OXT-USDT,OXT-USD,ZEN-USDT,ZEN-USD,BTC-USDC,ONEB-USD,FIL-USDT,FIL-USD,AAVE-USDT,AAVE-USD,GRT-USDT,GRT-USD,SUSHI-USD,ANKR-USD,AMP-USD,SHIB-USDT,SHIB-BUSD,CRV-USDT,CRV-USD,AXS-USDT,AXS-USD,SOL-BTC,AVAX-USDT,AVAX-USD,CTSI-USDT,CTSI-USD,DOT-USDT,DOT-USD,YFI-USDT,YFI-USD,1INCH-USDT,1INCH-USD,FTM-USDT,FTM-USD,USDC-USDT,ETH-USDC,USDC-BUSD,MATIC-USDT,MANA-USDT,MANA-BUSD,ALGO-USDT,ADA-BUSD,SOL-BUSD,EOS-USDT,ENJ-USDT,NEAR-USDT,NEAR-BUSD,NEAR-USD,OMG-USDT,SUSHI-USDT,LRC-USDT,LRC-USD,LRC-BTC,KSHI-BUSD,LPT-USDT,LPT-BUSD,LPT-USD,POLY-USDT,POLY-BUSD,POLY-USD,POLY-BTC,MATIC-BTC,DOT-BTC,NMR-USDT,NMR-USD,SLP-USDT,ANT-USD,XNO-USD,CHZ-USDT,CHZ-USD,OGN-USDT,OGN-USD,GALA-USDT,GALA-USD,TLM-USDT,TLM-USD,SNX-USDT,SNX-USD,AUDIO-USDT,AUDIO-USD,ENS-USDT,MANA-BTC,ATOM-BTC,AVAX-BTC,WBTC-BTC,REQ-USDT,REQ-USD,APE-USDT,APE-USD,FLUX-USDT,FLUX-USD,TRX-BTC,TRX-BUSD,TRX-USDT,TRX-USD,COTI-USDT,COTI-USD,VOXEL-USDT,VOXEL-USD,RLC-USDT,RLC-USD,UST-USDT,UST-USD,BICO-USDT,BICO-USD,API3-USDT,API3-USD,ENS-USD,BTC-UST,BNT-USDT,BNT-USD,IMX-USDT,IMX-USD,SPELL-USDT,SPELL-USD,JASMY-USDT,JASMY-USD,FLOW-USDT,FLOW-USD,GTC-USDT,GTC-USD,BTC-BUSD,ZIL-BUSD,BNB-BUSD,ETH-BUSD,BUSD-USDT,ONE-BUSD,LINK-USDT,ZEC-USDT,SLP-USD,ANT-USDT",
"requestFormat": {
"uppercase": true
},
"configFormat": {
"uppercase": true,
"delimiter": "-"
}
}
}
},
"api": {
"authenticatedSupport": false,
"authenticatedWebsocketApiSupport": false,
"credentials": {
"key": "",
"secret": ""
},
"credentialsValidator": {
"requiresKey": true,
"requiresSecret": true
},
"urlEndpoints": {
"RestSpotSupplementaryURL": "https://api.binance.us",
"RestSpotURL": "https://api.binance.us",
"WebsocketSpotSupplementaryURL": "wss://stream.binance.us:9443/stream",
"WebsocketSpotURL": "wss://stream.binance.us:9443/stream"
}
},
"features": {
"supports": {
"restAPI": true,
"restCapabilities": {
"tickerBatching": true,
"autoPairUpdates": true
},
"websocketAPI": true,
"websocketCapabilities": {}
},
"enabled": {
"autoPairUpdates": true,
"websocketAPI": true,
"saveTradeData": false,
"tradeFeed": false,
"fillsFeed": false
}
},
"bankAccounts": [
{
"enabled": false,
"bankName": "",
"bankAddress": "",
"bankPostalCode": "",
"bankPostalCity": "",
"bankCountry": "",
"accountName": "",
"accountNumber": "",
"swiftCode": "",
"iban": "",
"supportedCurrencies": ""
}
],
"orderbook": {
"verificationBypass": false,
"websocketBufferLimit": 5,
"websocketBufferEnabled": false,
"publishPeriod": 10000000000
}
},
{
"name": "Binance",
"enabled": true,

View File

@@ -1,3 +1,4 @@
binanceus,
binance,
bitfinex,
bitflyer,
1 binance binanceus
1 binanceus
2 binance binance
3 bitfinex bitfinex
4 bitflyer bitflyer