Merge pull request #382 from thrasher-corp/engine

Merge engine branch into master
This commit is contained in:
Adrian Gallagher
2019-12-09 16:35:58 +11:00
committed by GitHub
560 changed files with 73075 additions and 28674 deletions

View File

@@ -13,15 +13,35 @@ environment:
GO111MODULE: on
NODEJS_VER: 10.15.3
APPVEYOR_SAVE_CACHE_ON_ERROR: true
POSTGRES_PATH: C:\Program Files\PostgreSQL\9.6
PGUSER: postgres
PGPASSWORD: Password12!
POSTGRES_ENV_POSTGRES_USER: postgres
POSTGRES_ENV_POSTGRES_PASSWORD: Password12!
POSTGRES_ENV_POSTGRES_DB: gct_dev_ci
PSQL_USER: postgres
PSQL_HOST: localhost
PSQL_PASS: Password12!
PSQL_DBNAME: gct_dev_ci
PSQL_SSLMODE: disable
stack: go 1.13.x
services:
- postgresql96
init:
- SET PATH=%POSTGRES_PATH%\bin;%PATH%
install:
- set Path=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%Path%
- ps: Install-Product node $env:NODEJS_VER
- cd c:\gopath\src\github.com\thrasher-corp\gocryptotrader\web
- npm install
build_script:
- createdb gct_dev_ci
before_test:
- cd c:\gopath\src\github.com\thrasher-corp\gocryptotrader
- go get

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
go.mod text eol=lf
go.sum text eol=lf

View File

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

View File

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

View File

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

View File

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

3
.gitignore vendored
View File

@@ -5,6 +5,7 @@ lib
.vscode
testdata/dump
testdata/preengine_config.json
testdata/writefiletest
# InteliJ
@@ -25,3 +26,5 @@ gocryptotrader
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
sqlboiler.toml
sqlboiler.json

View File

@@ -16,21 +16,54 @@ matrix:
- language: go
dist: xenial
name: 'GoCryptoTrader [back-end] [linux]'
name: 'GoCryptoTrader [back-end] [linux] [64-bit]'
go:
- 1.13.x
env:
- GO111MODULE=on
- PSQL_USER=postgres
- PSQL_HOST=localhost
- PSQL_DBNAME=gct_dev_ci
install: true
cache:
directories:
- $GOPATH/pkg/mod
services:
- postgresql
before_script:
- psql -c 'create database gct_dev_ci;' -U postgres
script:
- make check
after_success:
- bash <(curl -s https://codecov.io/bash)
- language: go
dist: xenial
name: 'GoCryptoTrader [back-end] [linux] [32-bit]'
go:
- 1.13.x
env:
- GO111MODULE=on
- NO_RACE_TEST=1
- PSQL_USER=postgres
- PSQL_HOST=localhost
- PSQL_DBNAME=gct_dev_ci
install: true
cache:
directories:
- $GOPATH/pkg/mod
services:
- postgresql
before_script:
- psql -c 'create database gct_dev_ci;' -U postgres
script:
- export GOARCH=386
- export CGO_ENABLED=1
- sudo apt-get install gcc-multilib
- make test
after_success:
- bash <(curl -s https://codecov.io/bash)
- language: go
os: osx
name: 'GoCryptoTrader [back-end] [darwin]'
@@ -38,11 +71,22 @@ matrix:
- 1.13.x
env:
- GO111MODULE=on
- PSQL_USER=postgres
- PSQL_HOST=localhost
- PSQL_DBNAME=gct_dev_ci
- PSQL_SSLMODE=disable
- PSQL_SKIPSQLCMD=true
- PSQL_TESTDBNAME=gct_dev_ci
install: true
cache:
directories:
- $GOPATH/pkg/mod
before_install:
- rm -rf /usr/local/var/postgres
- initdb /usr/local/var/postgres
- pg_ctl start --pgdata /usr/local/var/postgres
- createuser -s postgres
- psql -c 'create database gct_dev_ci;' -U postgres
script:
- make check
after_success:

View File

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

View File

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

View File

@@ -5,6 +5,8 @@ LINTBIN = $(GOPATH)/bin/golangci-lint
GCTLISTENPORT=9050
GCTPROFILERLISTENPORT=8085
CRON = $(TRAVIS_EVENT_TYPE)
DRIVER ?= psql
RACE_FLAG := $(if $(NO_RACE_TEST),,-race)
get:
GO111MODULE=on go get $(GCTPKG)
@@ -18,9 +20,9 @@ check: linter test
test:
ifeq ($(CRON), cron)
go test -race -tags=mock_test_off -coverprofile=coverage.txt -covermode=atomic ./...
go test $(RACE_FLAG) -tags=mock_test_off -coverprofile=coverage.txt -covermode=atomic ./...
else
go test -race -coverprofile=coverage.txt -covermode=atomic ./...
go test $(RACE_FLAG) -coverprofile=coverage.txt -covermode=atomic ./...
endif
build:
@@ -41,7 +43,14 @@ update_deps:
.PHONY: profile_heap
profile_heap:
go tool pprof -http "localhost:$(GCTPROFILERLISTENPORT)" 'http://localhost:$(GCTLISTENPORT)/debug/pprof/heap'
.PHONY: profile_cpu
profile_cpu:
go tool pprof -http "localhost:$(GCTPROFILERLISTENPORT)" 'http://localhost:$(GCTLISTENPORT)/debug/pprof/profile'
go tool pprof -http "localhost:$(GCTPROFILERLISTENPORT)" 'http://localhost:$(GCTLISTENPORT)/debug/pprof/profile'
gen_db_models:
ifeq ($(DRIVER), psql)
sqlboiler -o database/models/postgres -p postgres --no-auto-timestamps --wipe $(DRIVER)
else
sqlboiler -o database/models/sqlite3 -p sqlite3 --no-auto-timestamps --wipe $(DRIVER)
endif

View File

@@ -32,6 +32,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| COINUT | Yes | Yes | NA |
| Exmo | Yes | NA | NA |
| CoinbasePro | Yes | Yes | No|
| Coinbene | Yes | No | No |
| GateIO | Yes | Yes | NA |
| Gemini | Yes | Yes | No |
| HitBTC | Yes | Yes | No |
@@ -53,19 +54,26 @@ We are aiming to support the top 20 highest volume exchanges based off the [Coin
## Current Features
+ Support for all Exchange fiat and digital currencies, with the ability to individually toggle them on/off.
+ Support for all exchange fiat and digital currencies, with the ability to individually toggle them on/off.
+ AES256 encrypted config file.
+ REST API support for all exchanges.
+ Websocket support for applicable exchanges.
+ Ability to turn off/on certain exchanges.
+ Ability to adjust manual polling timer for exchanges.
+ Communication packages (Slack, SMS via SMSGlobal, Telegram and SMTP)
+ HTTP rate limiter package.
+ Unified API for exchange usage.
+ Customisation of HTTP client features including setting a proxy, user agent and adjusting transport settings.
+ NTP client package.
+ Database support (Postgres and SQLite3). See [database](/database/README.md).
+ OTP generation tool. See [gen otp](/cmd/gen_otp).
+ Connection monitor package.
+ gRPC service and JSON RPC proxy. See [gRPC service](/gctrpc/README.md).
+ gRPC client. See [gctcli](/cmd/gctcli/README.md).
+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Fixer.io, OpenExchangeRates)
+ Packages for handling currency pairs, tickers and orderbooks.
+ Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking.
+ Basic event trigger system.
+ WebGUI.
+ WebGUI (discontinued).
## Planned Features
@@ -128,42 +136,41 @@ Binaries will be published once the codebase reaches a stable condition.
### A very special thank you to all who have contributed to this program:
|User|Github|Contribution Amount|
|--|--|--|
| thrasher- | https://github.com/thrasher- | 548 |
| shazbert | https://github.com/shazbert | 176 |
| gloriousCode | https://github.com/gloriousCode | 155 |
| xtda | https://github.com/xtda | 18 |
| ermalguni | https://github.com/ermalguni | 14 |
| vadimzhukck | https://github.com/vadimzhukck | 10 |
| 140am | https://github.com/140am | 8 |
| marcofranssen | https://github.com/marcofranssen | 8 |
| cranktakular | https://github.com/cranktakular | 5 |
| MadCozBadd | https://github.com/MadCozBadd | 3 |
| leilaes | https://github.com/leilaes | 3 |
| crackcomm | https://github.com/crackcomm | 3 |
| andreygrehov | https://github.com/andreygrehov | 2 |
| bretep | https://github.com/bretep | 2 |
| woshidama323 | https://github.com/woshidama323 | 2 |
| gam-phon | https://github.com/gam-phon | 2 |
| cornelk | https://github.com/cornelk | 2 |
| if1live | https://github.com/if1live | 2 |
| soxipy | https://github.com/soxipy | 2 |
| herenow | https://github.com/herenow | 2 |
| blombard | https://github.com/blombard | 1 |
| CodeLingoBot | https://github.com/CodeLingoBot | 1 |
| CodeLingoTeam | https://github.com/CodeLingoTeam | 1 |
| Daanikus | https://github.com/Daanikus | 1 |
| daniel-cohen | https://github.com/daniel-cohen | 1 |
| DirectX | https://github.com/DirectX | 1 |
| frankzougc | https://github.com/frankzougc | 1 |
| starit | https://github.com/starit | 1 |
| Jimexist | https://github.com/Jimexist | 1 |
| lookfirst | https://github.com/lookfirst | 1 |
| mattkanwisher | https://github.com/mattkanwisher | 1 |
| mKurrels | https://github.com/mKurrels | 1 |
| m1kola | https://github.com/m1kola | 1 |
| cavapoo2 | https://github.com/cavapoo2 | 1 |
| zeldrinn | https://github.com/zeldrinn | 1 |
|User|Contribution Amount|
|--|--|
| [thrasher-](https://github.com/thrasher-) | 551 |
| [shazbert](https://github.com/shazbert) | 176 |
| [gloriousCode](https://github.com/gloriousCode) | 155 |
| [xtda](https://github.com/xtda) | 18 |
| [ermalguni](https://github.com/ermalguni) | 14 |
| [vadimzhukck](https://github.com/vadimzhukck) | 10 |
| [140am](https://github.com/140am) | 8 |
| [marcofranssen](https://github.com/marcofranssen) | 8 |
| [MadCozBadd](https://github.com/MadCozBadd) | 6 |
| [cranktakular](https://github.com/cranktakular) | 5 |
| [leilaes](https://github.com/leilaes) | 3 |
| [crackcomm](https://github.com/crackcomm) | 3 |
| [andreygrehov](https://github.com/andreygrehov) | 2 |
| [bretep](https://github.com/bretep) | 2 |
| [woshidama323](https://github.com/woshidama323) | 2 |
| [gam-phon](https://github.com/gam-phon) | 2 |
| [cornelk](https://github.com/cornelk) | 2 |
| [if1live](https://github.com/if1live) | 2 |
| [soxipy](https://github.com/soxipy) | 2 |
| [herenow](https://github.com/herenow) | 2 |
| [blombard](https://github.com/blombard) | 1 |
| [CodeLingoBot](https://github.com/CodeLingoBot) | 1 |
| [CodeLingoTeam](https://github.com/CodeLingoTeam) | 1 |
| [Daanikus](https://github.com/Daanikus) | 1 |
| [daniel-cohen](https://github.com/daniel-cohen) | 1 |
| [DirectX](https://github.com/DirectX) | 1 |
| [frankzougc](https://github.com/frankzougc) | 1 |
| [starit](https://github.com/starit) | 1 |
| [Jimexist](https://github.com/Jimexist) | 1 |
| [lookfirst](https://github.com/lookfirst) | 1 |
| [idoall](https://github.com/idoall) | 1 |
| [mattkanwisher](https://github.com/mattkanwisher) | 1 |
| [mKurrels](https://github.com/mKurrels) | 1 |
| [m1kola](https://github.com/m1kola) | 1 |
| [cavapoo2](https://github.com/cavapoo2) | 1 |
| [zeldrinn](https://github.com/zeldrinn) | 1 |

View File

@@ -2,9 +2,10 @@ package main
import (
"flag"
"io/ioutil"
"log"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/file"
"github.com/thrasher-corp/gocryptotrader/config"
)
@@ -42,19 +43,19 @@ func main() {
key = string(result)
}
file, err := common.ReadFile(inFile)
fileData, err := ioutil.ReadFile(inFile)
if err != nil {
log.Fatalf("Unable to read input file %s. Error: %s.", inFile, err)
}
if config.ConfirmECS(file) && encrypt {
if config.ConfirmECS(fileData) && encrypt {
log.Println("File is already encrypted. Decrypting..")
encrypt = false
}
if !config.ConfirmECS(file) && !encrypt {
if !config.ConfirmECS(fileData) && !encrypt {
var result interface{}
errf := config.ConfirmConfigJSON(file, result)
errf := config.ConfirmConfigJSON(fileData, result)
if errf != nil {
log.Fatal("File isn't in JSON format")
}
@@ -64,18 +65,18 @@ func main() {
var data []byte
if encrypt {
data, err = config.EncryptConfigFile(file, []byte(key))
data, err = config.EncryptConfigFile(fileData, []byte(key))
if err != nil {
log.Fatalf("Unable to encrypt config data. Error: %s.", err)
}
} else {
data, err = config.DecryptConfigFile(file, []byte(key))
data, err = config.DecryptConfigFile(fileData, []byte(key))
if err != nil {
log.Fatalf("Unable to decrypt config data. Error: %s.", err)
}
}
err = common.WriteFile(outFile, data)
err = file.Write(outFile, data)
if err != nil {
log.Fatalf("Unable to write output file %s. Error: %s", outFile, err)
}

View File

@@ -6,13 +6,13 @@ func TestEncryptOrDecrypt(t *testing.T) {
reValue := EncryptOrDecrypt(true)
if reValue != "encrypted" {
t.Error(
"Test failed - Tools/Config/Config_test.go - EncryptOrDecrypt Error",
"Tools/Config/Config_test.go - EncryptOrDecrypt Error",
)
}
reValue = EncryptOrDecrypt(false)
if reValue != "decrypted" {
t.Error(
"Test failed - Tools/Config/Config_test.go - EncryptOrDecrypt Error",
"Tools/Config/Config_test.go - EncryptOrDecrypt Error",
)
}
}

View File

@@ -0,0 +1,51 @@
package main
import (
"encoding/json"
"log"
"sync"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/engine"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
func main() {
var err error
engine.Bot, err = engine.New()
if err != nil {
log.Fatalf("Failed to initialise engine. Err: %s", err)
}
log.Printf("Loading exchanges..")
var wg sync.WaitGroup
for x := range exchange.Exchanges {
name := exchange.Exchanges[x]
err = engine.LoadExchange(name, true, &wg)
if err != nil {
log.Printf("Failed to load exchange %s. Err: %s", name, err)
continue
}
}
wg.Wait()
log.Println("Done.")
var cfgs []config.ExchangeConfig
for x := range engine.Bot.Exchanges {
var cfg *config.ExchangeConfig
cfg, err = engine.Bot.Exchanges[x].GetDefaultConfig()
if err != nil {
log.Printf("Failed to get exchanges default config. Err: %s", err)
continue
}
log.Printf("Adding %s", engine.Bot.Exchanges[x].GetName())
cfgs = append(cfgs, *cfg)
}
data, err := json.MarshalIndent(cfgs, "", " ")
if err != nil {
log.Fatalf("Unable to marshal cfgs. Err: %s", err)
}
log.Println(string(data))
}

102
cmd/dbmigrate/main.go Normal file
View File

@@ -0,0 +1,102 @@
package main
import (
"errors"
"flag"
"fmt"
"os"
"runtime"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/core"
"github.com/thrasher-corp/gocryptotrader/database"
dbPSQL "github.com/thrasher-corp/gocryptotrader/database/drivers/postgres"
dbsqlite3 "github.com/thrasher-corp/gocryptotrader/database/drivers/sqlite3"
"github.com/thrasher-corp/gocryptotrader/database/repository"
"github.com/thrasher-corp/goose"
)
var (
dbConn *database.Db
configFile string
defaultDataDir string
migrationDir string
command string
args string
)
func openDbConnection(driver string) (err error) {
if driver == database.DBPostgreSQL {
dbConn, err = dbPSQL.Connect()
if err != nil {
return fmt.Errorf("database failed to connect: %v Some features that utilise a database will be unavailable", err)
}
return nil
} else if driver == database.DBSQLite || driver == database.DBSQLite3 {
dbConn, err = dbsqlite3.Connect()
if err != nil {
return fmt.Errorf("database failed to connect: %v Some features that utilise a database will be unavailable", err)
}
return nil
}
return errors.New("no connection established")
}
func main() {
fmt.Println("GoCryptoTrader database migration tool")
fmt.Println(core.Copyright)
fmt.Println()
defaultPath, err := config.GetFilePath("")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
flag.StringVar(&command, "command", "", "command to run status|up|up-by-one|up-to|down|create")
flag.StringVar(&args, "args", "", "arguments to pass to goose")
flag.StringVar(&configFile, "config", defaultPath, "config file to load")
flag.StringVar(&defaultDataDir, "datadir", common.GetDefaultDataDir(runtime.GOOS), "default data directory for GoCryptoTrader files")
flag.StringVar(&migrationDir, "migrationdir", database.MigrationDir, "override migration folder")
flag.Parse()
conf := config.GetConfig()
err = conf.LoadConfig(configFile, true)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if !conf.Database.Enabled {
fmt.Println("Database support is disabled")
os.Exit(1)
}
err = openDbConnection(conf.Database.Driver)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
drv := repository.GetSQLDialect()
if drv == database.DBSQLite || drv == database.DBSQLite3 {
fmt.Printf("Database file: %s\n", conf.Database.Database)
} else {
fmt.Printf("Connected to: %s\n", conf.Database.Host)
}
if command == "" {
_ = goose.Run("status", dbConn.SQL, drv, migrationDir, "")
fmt.Println()
flag.Usage()
return
}
if err = goose.Run(command, dbConn.SQL, drv, migrationDir, args); err != nil {
fmt.Println(err)
}
}

View File

@@ -0,0 +1,97 @@
# GoCryptoTrader package Documentation
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://travis-ci.org/thrasher-corp/gocryptotrader.svg?branch=master)](https://travis-ci.org/thrasher-corp/gocryptotrader)
[![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/cmd/documentation)
[![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 documentation package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progresss 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)
## Current Features for documentation
#### This tool allows for the generation of new documentation through templating
From the `gocryptotrader/cmd/documentation/` folder, using the go command: **go run documentation.go** this will auto-generate and regenerate documentation across the **GoCryptoTrader** code base.
>Using the -v command will, ie **go run documentation.go -v** put the tool into verbose mode allowing you to see what is happening with a little more depth.
Be aware, this tool will:
- Works off a configuration JSON file located at ``gocryptotrader/cmd/documentation/`` for future use with multiple repositories.
- Automatically find the directory and file tree for the GoCryptoTrader source code and alert you of undocumented file systems which **need** to be updated.
- Automatically find the template folder tree.
- Fetch an updated contributor list and rank on pull request commit amount
- Sets up core folder docs for the root directory tree including **LICENSE** and **CONTRIBUTORS**
### config.json example
```json
{
"githubRepo": "https://api.github.com/repos/thrasher-corp/gocryptotrader", This is your current repo
"exclusionList": { This allows for excluded directories and files
"Files": null,
"Directories": [
"_templates",
".git",
"web"
]
},
"rootReadmeActive": true, allows a root directory README.md
"licenseFileActive": true, allows for a license file to be generated
"contributorFileActive": true, fetches a new contributor list
"referencePathToRepo": "../../"
}
```
### Template example
>place a new template **example_file.tmpl** located in the current gocryptotrader/cmd/documentation/ folder; when the documentation tool finishes it will give you the define template associated name e.g. ``Template not found for path ../../cmd/documentation create new template with \{\{define "cmd documentation" -\}\} TEMPLATE HERE \{\{end}}`` so you can replace the below example with ``\{\{define "cmd documentation" -}}``
```
\{\{\define "example_definition_created_by_documentation_tool" -}}
\{\{\template "header" .}}
## Current Features for documentation
#### A concise blurb about the package or tool system
+ Coding examples
import "github.com/thrasher-corp/gocryptotrader/something"
testString := "aAaAa"
upper := strings.ToUpper(testString)
// upper == "AAAAA"
{\{\template "contributions"}}
{\{\template "donations"}}
{\{\end}}
```
### ALL NEW UPDATES AND FILE SYSTEM ADDITIONS NEED A DOCUMENTATION UPDATE USING THIS TOOL OR PR MERGE REQUEST MAY BE POSTPONED.
### 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:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

View File

@@ -0,0 +1,63 @@
{{define "cmd documentation" -}}
{{template "header" .}}
## Current Features for {{.Name}}
#### This tool allows for the generation of new documentation through templating
From the `gocryptotrader/cmd/documentation/` folder, using the go command: **go run documentation.go** this will auto-generate and regenerate documentation across the **GoCryptoTrader** code base.
>Using the -v command will, ie **go run documentation.go -v** put the tool into verbose mode allowing you to see what is happening with a little more depth.
Be aware, this tool will:
- Works off a configuration JSON file located at ``gocryptotrader/cmd/documentation/`` for future use with multiple repositories.
- Automatically find the directory and file tree for the GoCryptoTrader source code and alert you of undocumented file systems which **need** to be updated.
- Automatically find the template folder tree.
- Fetch an updated contributor list and rank on pull request commit amount
- Sets up core folder docs for the root directory tree including **LICENSE** and **CONTRIBUTORS**
### config.json example
```json
{
"githubRepo": "https://api.github.com/repos/thrasher-corp/gocryptotrader", This is your current repo
"exclusionList": { This allows for excluded directories and files
"Files": null,
"Directories": [
"_templates",
".git",
"web"
]
},
"rootReadmeActive": true, allows a root directory README.md
"licenseFileActive": true, allows for a license file to be generated
"contributorFileActive": true, fetches a new contributor list
"referencePathToRepo": "../../"
}
```
### Template example
>place a new template **example_file.tmpl** located in the current gocryptotrader/cmd/documentation/ folder; when the documentation tool finishes it will give you the define template associated name e.g. ``Template not found for path ../../cmd/documentation create new template with \{\{define "cmd documentation" -\}\} TEMPLATE HERE \{\{end}}`` so you can replace the below example with ``\{\{define "cmd documentation" -}}``
```
\{\{\define "example_definition_created_by_documentation_tool" -}}
\{\{\template "header" .}}
## Current Features for {{.Name}}
#### A concise blurb about the package or tool system
+ Coding examples
import "github.com/thrasher-corp/gocryptotrader/something"
testString := "aAaAa"
upper := strings.ToUpper(testString)
// upper == "AAAAA"
{\{\template "contributions"}}
{\{\template "donations"}}
{\{\end}}
```
### ALL NEW UPDATES AND FILE SYSTEM ADDITIONS NEED A DOCUMENTATION UPDATE USING THIS TOOL OR PR MERGE REQUEST MAY BE POSTPONED.
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations"}}
{{end}}

View File

@@ -11,7 +11,7 @@ import "github.com/thrasher-corp/gocryptotrader/common"
testString := "aAaAa"
upper := common.StringToUpper(testString)
upper := strings.ToUpper(testString)
// upper == "AAAAA"
```

View File

@@ -1,4 +1,4 @@
{{define "currency forexprovider currencyconverter" -}}
{{define "currency forexprovider currencyconverterapi" -}}
{{template "header" .}}
## Current Features for {{.Name}}
@@ -11,15 +11,15 @@
+ Individual package example below:
```go
import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencyconverter"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencyconverter"
)
c := currencyconverter.CurrencyConverter{}
// Define configuration
newSettings := base.Settings{
Name: "CurrencyConverter",
Name: "CurrencyConverter",
Enabled: true,
Verbose: false,
RESTPollingDelay: time.Duration,

View File

@@ -11,15 +11,15 @@
+ Individual package example below:
```go
import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencylayer"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencylayer"
)
c := currencylayer.CurrencyLayer{}
// Define configuration
newSettings := base.Settings{
Name: "CurrencyLayer",
Name: "CurrencyLayer",
Enabled: true,
Verbose: false,
RESTPollingDelay: time.Duration,

View File

@@ -0,0 +1,40 @@
{{define "currency forexprovider exchangeratesapi.io" -}}
{{template "header" .}}
## Current Features for {{.Name}}
+ Fetches up to date curency data from [Exchange rates API]("http://exchangeratesapi.io")
### How to enable
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-currency-via-config-example)
+ Individual package example below:
```go
import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/exchangerates"
)
c := exchangerates.ExchangeRates{}
// Define configuration
newSettings := base.Settings{
Name: "ExchangeRates",
Enabled: true,
Verbose: false,
RESTPollingDelay: time.Duration,
APIKey: "key",
APIKeyLvl: "keylvl",
PrimaryProvider: true,
}
c.Setup(newSettings)
mapstringfloat, err := c.GetRates("USD", "EUR,CHY")
// Handle error
```
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations"}}
{{- end}}

View File

@@ -1,4 +1,4 @@
{{define "currency forexprovider fixer" -}}
{{define "currency forexprovider fixer.io" -}}
{{template "header" .}}
## Current Features for {{.Name}}
@@ -11,15 +11,15 @@
+ Individual package example below:
```go
import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/fixer.io"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/fixer.io"
)
c := fixer.Fixer{}
// Define configuration
newSettings := base.Settings{
Name: "Fixer",
Name: "Fixer",
Enabled: true,
Verbose: false,
RESTPollingDelay: time.Duration,

View File

@@ -11,15 +11,15 @@
+ Individual package example below:
```go
import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/openexchangerates"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/openexchangerates"
)
c := openexchangerates.OXR{}
// Define configuration
newSettings := base.Settings{
Name: "openexchangerates",
Name: "openexchangerates",
Enabled: true,
Verbose: false,
RESTPollingDelay: time.Duration,

View File

@@ -0,0 +1,487 @@
package main
import (
"encoding/json"
"errors"
"flag"
"fmt"
"html/template"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/core"
)
const (
// DefaultRepo is the main example repository
DefaultRepo = "https://api.github.com/repos/thrasher-corp/gocryptotrader"
// GithubAPIEndpoint allows the program to query your repository
// contributor list
GithubAPIEndpoint = "/contributors"
// LicenseFile defines a license file
LicenseFile = "LICENSE"
// ContributorFile defines contributor file
ContributorFile = "CONTRIBUTORS"
)
var (
// DefaultExcludedDirectories defines the basic directory exclusion list for GCT
DefaultExcludedDirectories = []string{".github",
".git",
"node_modules",
".vscode",
".idea",
"cmd_templates",
"common_templates",
"communications_templates",
"config_templates",
"currency_templates",
"events_templates",
"exchanges_templates",
"portfolio_templates",
"root_templates",
"sub_templates",
"testdata_templates",
"tools_templates",
"web_templates",
}
// global flag for verbosity
verbose bool
// current tool directory to specify working templates
toolDir string
// exposes root directory if outside of document tool directory
repoDir string
// is a broken down version of the documentation tool dir for cross platform
// checking
ref = []string{"gocryptotrader", "cmd", "documentation"}
)
// Contributor defines an account associated with this code base by doing
// contributions
type Contributor struct {
Login string `json:"login"`
URL string `json:"html_url"`
Contributions int `json:"contributions"`
}
// Config defines the running config to deploy documentation across a github
// repository including exclusion lists for files and directories
type Config struct {
GithubRepo string `json:"githubRepo"`
Exclusions Exclusions `json:"exclusionList"`
RootReadme bool `json:"rootReadmeActive"`
LicenseFile bool `json:"licenseFileActive"`
ContributorFile bool `json:"contributorFileActive"`
}
// Exclusions defines the exclusion list so documents are not generated
type Exclusions struct {
Files []string `json:"Files"`
Directories []string `json:"Directories"`
}
// DocumentationDetails defines parameters to update documentation
type DocumentationDetails struct {
Directories []string
Tmpl *template.Template
Contributors []Contributor
Config *Config
}
// Attributes defines specific documentation attributes when a template is
// executed
type Attributes struct {
Name string
Contributors []Contributor
NameURL string
Year int
CapitalName string
}
func main() {
flag.BoolVar(&verbose, "v", false, "Verbose output")
flag.StringVar(&toolDir, "tooldir", "", "Pass in the documentation tool directory if outside tool folder")
flag.Parse()
wd, err := os.Getwd()
if err != nil {
fmt.Println("Documentation tool error cannot get working dir:", err)
os.Exit(1)
}
if strings.Contains(wd, filepath.Join(ref...)) {
rootdir := filepath.Dir(filepath.Dir(wd))
repoDir = rootdir
toolDir = wd
} else {
if toolDir == "" {
fmt.Println("Please set documentation tool directory via the tooldir flag if working outside of tool directory")
os.Exit(1)
}
repoDir = wd
}
fmt.Println(core.Banner)
fmt.Println("This will update and regenerate documentation for the different packages in your repo.")
fmt.Println()
if verbose {
fmt.Println("Fetching configuration...")
}
config, err := GetConfiguration()
if err != nil {
log.Fatalf("Documentation Generation Tool - GetConfiguration error %s",
err)
}
if verbose {
fmt.Println("Fetching project directory tree...")
}
dirList, err := GetProjectDirectoryTree(&config)
if err != nil {
log.Fatalf("Documentation Generation Tool - GetProjectDirectoryTree error %s",
err)
}
var contributors []Contributor
if config.ContributorFile {
if verbose {
fmt.Println("Fetching repository contributor list...")
}
contributors, err = GetContributorList(config.GithubRepo)
if err != nil {
log.Fatalf("Documentation Generation Tool - GetContributorList error %s",
err)
}
// Github API missing contributors
contributors = append(contributors, []Contributor{
// idoall's contributors were forked and merged, so his contributions
// aren't automatically retrievable
{
Login: "idoall",
URL: "https://github.com/idoall",
Contributions: 1,
},
{
Login: "mattkanwisher",
URL: "https://github.com/mattkanwisher",
Contributions: 1,
},
{
Login: "mKurrels",
URL: "https://github.com/mKurrels",
Contributions: 1,
},
{
Login: "m1kola",
URL: "https://github.com/m1kola",
Contributions: 1,
},
{
Login: "cavapoo2",
URL: "https://github.com/cavapoo2",
Contributions: 1,
},
{
Login: "zeldrinn",
URL: "https://github.com/zeldrinn",
Contributions: 1,
},
}...)
if verbose {
fmt.Println("Contributor List Fetched")
for i := range contributors {
fmt.Println(contributors[i].Login)
}
}
} else {
fmt.Println("Contributor list file disabled skipping fetching details")
}
if verbose {
fmt.Println("Fetching template files...")
}
tmpl, err := GetTemplateFiles()
if err != nil {
log.Fatalf("Documentation Generation Tool - GetTemplateFiles error %s",
err)
}
if verbose {
fmt.Println("All core systems fetched, updating documentation...")
}
err = UpdateDocumentation(DocumentationDetails{
dirList,
tmpl,
contributors,
&config})
if err != nil {
log.Fatalf("Documentation Generation Tool - UpdateDocumentation error %s",
err)
}
fmt.Println("\nDocumentation Generation Tool - Finished")
}
// GetConfiguration retrieves the documentation configuration
func GetConfiguration() (Config, error) {
var c Config
configFilePath := filepath.Join([]string{toolDir, "config.json"}...)
file, err := os.OpenFile(configFilePath, os.O_RDWR, os.ModePerm)
if err != nil {
fmt.Println("Creating configuration file, please check to add a different github repository path and change preferences")
file, err = os.Create(configFilePath)
if err != nil {
return c, err
}
// Set default params for configuration
c.GithubRepo = DefaultRepo
c.ContributorFile = true
c.LicenseFile = true
c.RootReadme = true
c.Exclusions.Directories = DefaultExcludedDirectories
data, mErr := json.MarshalIndent(c, "", " ")
if mErr != nil {
return c, mErr
}
_, err = file.WriteAt(data, 0)
if err != nil {
return c, err
}
}
defer file.Close()
config, err := ioutil.ReadAll(file)
if err != nil {
return c, err
}
err = json.Unmarshal(config, &c)
if err != nil {
return c, err
}
if c.GithubRepo == "" {
return c, errors.New("repository not set in config.json file, please change")
}
return c, nil
}
// IsExcluded returns if the file path is included in the exclusion list
func IsExcluded(path string, exclusion []string) bool {
for i := range exclusion {
if path == exclusion[i] {
return true
}
}
return false
}
// GetProjectDirectoryTree uses filepath walk functions to get each individual
// directory name and path to match templates with
func GetProjectDirectoryTree(c *Config) ([]string, error) {
var directoryData []string
if c.RootReadme { // Projects root README.md
directoryData = append(directoryData, repoDir)
}
if c.LicenseFile { // Standard license file
directoryData = append(directoryData, filepath.Join(repoDir, LicenseFile))
}
if c.ContributorFile { // Standard contributor file
directoryData = append(directoryData, filepath.Join(repoDir, ContributorFile))
}
walkfn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
// Bypass what is contained in config.json directory exclusion
if IsExcluded(info.Name(), c.Exclusions.Directories) {
if verbose {
fmt.Println("Excluding Directory:", info.Name())
}
return filepath.SkipDir
}
// Don't append parent directory
if strings.EqualFold(info.Name(), "..") {
return nil
}
directoryData = append(directoryData, path)
}
return nil
}
return directoryData, filepath.Walk(repoDir, walkfn)
}
// GetTemplateFiles parses and returns all template files in the documentation
// tree
func GetTemplateFiles() (*template.Template, error) {
tmpl := template.New("")
walkfn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
if path == "." || path == ".." {
return nil
}
var parseError error
tmpl, parseError = tmpl.ParseGlob(filepath.Join(path, "*.tmpl"))
if parseError != nil {
if strings.Contains(parseError.Error(), "pattern matches no files") {
return nil
}
return parseError
}
return filepath.SkipDir
}
return nil
}
return tmpl, filepath.Walk(toolDir, walkfn)
}
// GetContributorList fetches a list of contributors from the github api
// endpoint
func GetContributorList(repo string) ([]Contributor, error) {
var resp []Contributor
return resp, common.SendHTTPGetRequest(repo+GithubAPIEndpoint, true, false, &resp)
}
// GetDocumentationAttributes returns specific attributes for a file template
func GetDocumentationAttributes(packageName string, contributors []Contributor) Attributes {
return Attributes{
Name: GetPackageName(packageName, false),
Contributors: contributors,
NameURL: GetGoDocURL(packageName),
Year: time.Now().Year(),
CapitalName: GetPackageName(packageName, true),
}
}
// GetPackageName returns the package name after cleaning path as a string
func GetPackageName(name string, capital bool) string {
newStrings := strings.Split(name, " ")
var i int
if len(newStrings) > 1 {
i = 1
}
if capital {
return strings.Title(newStrings[i])
}
return newStrings[i]
}
// GetGoDocURL returns a string for godoc package names
func GetGoDocURL(name string) string {
if strings.Contains(name, " ") {
return strings.Join(strings.Split(name, " "), "/")
}
if name == "testdata" ||
name == "tools" ||
name == ContributorFile ||
name == LicenseFile {
return ""
}
return name
}
// UpdateDocumentation generates or updates readme/documentation files across
// the codebase
func UpdateDocumentation(details DocumentationDetails) error {
for i := range details.Directories {
cutset := details.Directories[i][len(repoDir):]
if cutset != "" && cutset[0] == os.PathSeparator {
cutset = cutset[1:]
}
data := strings.Split(cutset, string(os.PathSeparator))
var temp []string
for x := range data {
if data[x] == ".." {
continue
}
if data[x] == "" {
break
}
temp = append(temp, data[x])
}
var name string
if len(temp) == 0 {
name = "root"
} else {
name = strings.Join(temp, " ")
}
if IsExcluded(name, details.Config.Exclusions.Files) {
if verbose {
fmt.Println("Excluding file:", name)
}
continue
}
if details.Tmpl.Lookup(name) == nil {
fmt.Printf("Template not found for path %s create new template with {{define \"%s\" -}} TEMPLATE HERE {{end}}\n",
details.Directories[i],
name)
continue
}
var mainPath string
if name == LicenseFile || name == ContributorFile {
mainPath = details.Directories[i]
} else {
mainPath = filepath.Join(details.Directories[i], "README.md")
}
err := os.Remove(mainPath)
if err != nil && !(strings.Contains(err.Error(), "no such file or directory") ||
strings.Contains(err.Error(), "The system cannot find the file specified.")) {
return err
}
file, err := os.Create(mainPath)
if err != nil {
return err
}
attr := GetDocumentationAttributes(name, details.Contributors)
err = details.Tmpl.ExecuteTemplate(file, name, attr)
if err != nil {
file.Close()
return err
}
file.Close()
}
return nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@
### Current Features
+ REST Support
+ Websocket Support
### How to enable
@@ -29,22 +30,22 @@ main.go
```go
var b exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "BTCMarkets" {
b = bot.exchanges[i]
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "BTCMarkets" {
b = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := b.GetTickerPrice()
tick, err := b.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := b.GetOrderbookEx()
ob, err := b.FetchOrderbook()
if err != nil {
// Handle error
}

View File

@@ -0,0 +1,99 @@
{{define "exchanges btse" -}}
{{template "header" .}}
## BTCMarkets 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() == "BTSE" {
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
}
```
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations"}}
{{end}}

View File

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

View File

@@ -30,22 +30,22 @@ main.go
```go
var c exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "Coinbene" {
c = bot.exchanges[i]
for i := range Bot.Exchanges {
if Bot.Exchanges[i].GetName() == "Coinbene" {
c = Bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := c.GetTickerPrice()
tick, err := c.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := c.GetOrderbookEx()
ob, err := c.FetchOrderbook()
if err != nil {
// Handle error
}
@@ -72,7 +72,7 @@ if err != nil {
}
// Fetches current orderbook information
ob, err := c.GetOrderBook()
ob, err := c.GetOrderbook()
if err != nil {
// Handle error
}
@@ -87,7 +87,7 @@ if err != nil {
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := c.Trade(...)
resp, err := c.SubmitOrder(...)
if err != nil {
// Handle error
}

View File

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

View File

@@ -16,7 +16,7 @@ exchange interface system set by exchange wrapper orderbook functions in
Examples below:
```go
ob, err := yobitExchange.GetOrderbookEx()
ob, err := yobitExchange.FetchOrderbook()
if err != nil {
// Handle error
}

View File

@@ -17,7 +17,7 @@ exchange interface system set by exchange wrapper orderbook functions in
Examples below:
```go
tick, err := yobitExchange.GetTickerPrice()
tick, err := yobitExchange.FetchTicker()
if err != nil {
// Handle error
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@
### Current Features
+ REST Support
+ Websocket Support
### How to enable
@@ -29,22 +30,22 @@ main.go
```go
var l exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "LakeBTC" {
l = bot.exchanges[i]
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "LakeBTC" {
l = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := l.GetTickerPrice()
tick, err := l.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := l.GetOrderbookEx()
ob, err := l.FetchOrderbook()
if err != nil {
// Handle error
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{{define "CONTRIBUTORS"}}
Thanks to the following contributors:
{{ range $contributor := .Contributors -}}
{{range $contributor := .Contributors -}}
{{$contributor.Login}} | {{$contributor.URL}}
{{ end }}
{{end}}
{{- end}}

View File

@@ -33,6 +33,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| COINUT | Yes | Yes | NA |
| Exmo | Yes | NA | NA |
| CoinbasePro | Yes | Yes | No|
| Coinbene | Yes | No | No |
| GateIO | Yes | Yes | NA |
| Gemini | Yes | Yes | No |
| HitBTC | Yes | Yes | No |
@@ -54,19 +55,26 @@ We are aiming to support the top 20 highest volume exchanges based off the [Coin
## Current Features
+ Support for all Exchange fiat and digital currencies, with the ability to individually toggle them on/off.
+ Support for all exchange fiat and digital currencies, with the ability to individually toggle them on/off.
+ AES256 encrypted config file.
+ REST API support for all exchanges.
+ Websocket support for applicable exchanges.
+ Ability to turn off/on certain exchanges.
+ Ability to adjust manual polling timer for exchanges.
+ Communication packages (Slack, SMS via SMSGlobal, Telegram and SMTP)
+ HTTP rate limiter package.
+ Unified API for exchange usage.
+ Customisation of HTTP client features including setting a proxy, user agent and adjusting transport settings.
+ NTP client package.
+ Database support (Postgres and SQLite3). See [database](/database/README.md).
+ OTP generation tool. See [gen otp](/cmd/gen_otp).
+ Connection monitor package.
+ gRPC service and JSON RPC proxy. See [gRPC service](/gctrpc/README.md).
+ gRPC client. See [gctcli](/cmd/gctcli/README.md).
+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Fixer.io, OpenExchangeRates)
+ Packages for handling currency pairs, tickers and orderbooks.
+ Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking.
+ Basic event trigger system.
+ WebGUI.
+ WebGUI (discontinued).
## Planned Features
@@ -125,4 +133,4 @@ If this framework helped you in any way, or you would like to support the develo
Binaries will be published once the codebase reaches a stable condition.
{{template "contributors" .}}
{{end}}
{{- end}}

View File

@@ -0,0 +1,11 @@
{{define "contributors"}}
## Contributor List
### A very special thank you to all who have contributed to this program:
|User|Contribution Amount|
|--|--|
{{range $contributor := .Contributors -}}
| [{{$contributor.Login}}]({{$contributor.URL}}) | {{$contributor.Contributions}} |
{{end}}
{{- end}}

View File

@@ -6,4 +6,4 @@
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***
{{end}}
{{- end}}

View File

@@ -6,5 +6,5 @@ This folder contains a configuration test file for non-deployement test params.
It also has the code coverage test files that allow us to monitor our entire
codebase, click this link for more information [https://codecov.io/](https://codecov.io/).
{{template "contributions"}}
{{template "donations"}}
{{template "donations" -}}
{{end}}

View File

@@ -7,10 +7,13 @@ import (
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
const (
@@ -20,9 +23,8 @@ const (
packageMain = "%s.go"
packageReadme = "README.md"
exchangePackageLocation = "..%s..%sexchanges%s"
exchangeLocation = "..%s..%sexchange.go"
exchangeConfigPath = "..%s..%stestdata%sconfigtest.json"
exchangePackageLocation = "../../exchanges"
exchangeConfigPath = "../../testdata/configtest.json"
)
var (
@@ -32,7 +34,6 @@ var (
exchangeWrapper string
exchangeMain string
exchangeReadme string
exchangeJSON string
)
type exchange struct {
@@ -82,9 +83,9 @@ func main() {
log.Fatal("GoCryptoTrader: Exchange templating tool stopped...")
}
newExchangeName = common.StringToLower(newExchangeName)
newExchangeName = strings.ToLower(newExchangeName)
v := newExchangeName[:1]
capName := common.StringToUpper(v) + newExchangeName[1:]
capName := strings.ToUpper(v) + newExchangeName[1:]
exch := exchange{
Name: newExchangeName,
@@ -95,18 +96,14 @@ func main() {
FIX: fixSupport,
}
osPathSlash := common.GetOSPathSlash()
exchangeJSON := fmt.Sprintf(exchangeConfigPath, osPathSlash, osPathSlash, osPathSlash)
configTestFile := config.GetConfig()
err = configTestFile.LoadConfig(exchangeJSON)
err = configTestFile.LoadConfig(exchangeConfigPath, true)
if err != nil {
log.Fatal("GoCryptoTrader: Exchange templating configuration retrieval error ", err)
}
// NOTE need to nullify encrypt configuration
var configTestExchanges []string
for x := range configTestFile.Exchanges {
configTestExchanges = append(configTestExchanges, configTestFile.Exchanges[x].Name)
}
@@ -118,31 +115,34 @@ func main() {
newExchConfig := config.ExchangeConfig{}
newExchConfig.Name = capName
newExchConfig.Enabled = true
newExchConfig.RESTPollingDelay = 10
newExchConfig.APIKey = "Key"
newExchConfig.APISecret = "Secret"
newExchConfig.AssetTypes = orderbook.Spot
newExchConfig.API.Credentials.Key = "Key"
newExchConfig.API.Credentials.Secret = "Secret"
newExchConfig.CurrencyPairs = &currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
RequestFormat: &currency.PairFormat{
Uppercase: true,
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
},
}
configTestFile.Exchanges = append(configTestFile.Exchanges, newExchConfig)
// TODO sorting function so exchanges are in alphabetical order - low priority
err = configTestFile.SaveConfig(exchangeJSON)
err = configTestFile.SaveConfig(exchangeConfigPath, false)
if err != nil {
log.Fatal("GoCryptoTrader: Exchange templating configuration error - cannot save")
}
exchangeDirectory = fmt.Sprintf(
exchangePackageLocation+newExchangeName+"%s",
osPathSlash,
osPathSlash,
osPathSlash,
osPathSlash)
exchangeTest = fmt.Sprintf(exchangeDirectory+packageTests, newExchangeName)
exchangeTypes = fmt.Sprintf(exchangeDirectory+packageTypes, newExchangeName)
exchangeWrapper = fmt.Sprintf(exchangeDirectory+packageWrapper, newExchangeName)
exchangeMain = fmt.Sprintf(exchangeDirectory+packageMain, newExchangeName)
exchangeReadme = exchangeDirectory + packageReadme
exchangeDirectory = filepath.Join(exchangePackageLocation, newExchangeName)
exchangeTest = filepath.Join(exchangeDirectory, fmt.Sprintf(packageTests, newExchangeName))
exchangeTypes = filepath.Join(exchangeDirectory, fmt.Sprintf(packageTypes, newExchangeName))
exchangeWrapper = filepath.Join(exchangeDirectory, fmt.Sprintf(packageWrapper, newExchangeName))
exchangeMain = filepath.Join(exchangeDirectory, fmt.Sprintf(packageMain, newExchangeName))
exchangeReadme = filepath.Join(exchangeDirectory, packageReadme)
err = os.Mkdir(exchangeDirectory, 0700)
if err != nil {
@@ -220,12 +220,13 @@ func main() {
}
fmt.Println("GoCryptoTrader: Exchange templating tool service complete")
fmt.Println("When wrapper is finished add exchange to exchange.go")
fmt.Println("Test exchange.go")
fmt.Println("Update the config_test.go file")
fmt.Println("Test config.go")
fmt.Println("When the exchange code implementation has been completed (REST/Websocket/wrappers and tests), please add the exchange to engine/exchange.go")
fmt.Println("Add the exchange config settings to config_example.json (it will automatically be added to testdata/configtest.json)")
fmt.Println("Increment the available exchanges counter in config/config_test.go")
fmt.Println("Add the exchange name to exchanges/support.go")
fmt.Println("Ensure go test ./... -race passes")
fmt.Println("Open a pull request")
fmt.Println("If help is needed please post a message on Slack.")
fmt.Println("If help is needed, please post a message in Slack.")
}
func newFile(path string) {

View File

@@ -0,0 +1,24 @@
{{define "main"}}
package {{.Name}}
import (
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
// {{.CapitalName}} is the overarching type across this package
type {{.CapitalName}} struct {
exchange.Base
}
const (
{{.Name}}APIURL = ""
{{.Name}}APIVersion = ""
// Public endpoints
// Authenticated endpoints
)
// Start implementing public and private exchange API funcs below
{{end}}

View File

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

View File

@@ -0,0 +1,397 @@
{{define "wrapper"}}
package {{.Name}}
import (
"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/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// GetDefaultConfig returns a default exchange config
func ({{.Variable}} *{{.CapitalName}}) GetDefaultConfig() (*config.ExchangeConfig, error) {
{{.Variable}}.SetDefaults()
exchCfg := new(config.ExchangeConfig)
exchCfg.Name = {{.Variable}}.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = {{.Variable}}.BaseCurrencies
err := {{.Variable}}.SetupDefaults(exchCfg)
if err != nil {
return nil, err
}
if {{.Variable}}.Features.Supports.RESTCapabilities.AutoPairUpdates {
err = {{.Variable}}.UpdateTradablePairs(true)
if err != nil {
return nil, err
}
}
return exchCfg, nil
}
// SetDefaults sets the basic defaults for {{.CapitalName}}
func ({{.Variable}} *{{.CapitalName}}) SetDefaults() {
{{.Variable}}.Name = "{{.CapitalName}}"
{{.Variable}}.Enabled = true
{{.Variable}}.Verbose = true
{{.Variable}}.API.CredentialsValidator.RequiresKey = true
{{.Variable}}.API.CredentialsValidator.RequiresSecret = true
{{.Variable}}.CurrencyPairs = currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
RequestFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: "-",
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: "-",
},
}
// Fill out the capabilities/features that the exchange supports
{{.Variable}}.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
{{ if .REST }} REST: true, {{ end }}
{{ if .WS }} Websocket: true, {{ end }}
RESTCapabilities: protocol.Features{
TickerFetching: true,
OrderbookFetching: true,
},
WebsocketCapabilities: protocol.Features{
TickerFetching: true,
OrderbookFetching: true,
},
WithdrawPermissions: exchange.AutoWithdrawCrypto |
exchange.AutoWithdrawFiat,
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: true,
},
}
{{.Variable}}.Requester = request.New({{.Variable}}.Name,
request.NewRateLimit(time.Second, 0),
request.NewRateLimit(time.Second, 0),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
{{.Variable}}.API.Endpoints.URLDefault = {{.Name}}APIURL
{{.Variable}}.API.Endpoints.URL = {{.Variable}}.API.Endpoints.URLDefault
{{.Variable}}.Websocket = wshandler.New()
{{.Variable}}.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
{{.Variable}}.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
{{.Variable}}.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
// Setup takes in the supplied exchange configuration details and sets params
func ({{.Variable}} *{{.CapitalName}}) Setup(exch *config.ExchangeConfig) error {
if !exch.Enabled {
{{.Variable}}.SetEnabled(false)
return nil
}
err := {{.Variable}}.SetupDefaults(exch)
if err != nil {
return err
}
// If websocket is supported, please fill out the following
/*
err = {{.Variable}}.Websocket.Setup(
&wshandler.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport,
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: {{.Name}}WSURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
Connector: {{.Variable}}.WsConnect,
Subscriber: {{.Variable}}.Subscribe,
UnSubscriber: {{.Variable}}.Unsubscribe,
Features: &{{.Variable}}.Features.Supports.WebsocketCapabilities,
})
if err != nil {
return err
}
{{.Variable}}.WebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: {{.Variable}}.Name,
URL: {{.Variable}}.Websocket.GetWebsocketURL(),
ProxyURL: {{.Variable}}.Websocket.GetProxyAddress(),
Verbose: {{.Variable}}.Verbose,
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
// NOTE: PLEASE ENSURE YOU SET THE ORDERBOOK BUFFER SETTINGS CORRECTLY
{{.Variable}}.Websocket.Orderbook.Setup(
exch.WebsocketOrderbookBufferLimit,
true,
true,
false,
false,
exch.Name)
*/
return nil
}
// Start starts the {{.CapitalName}} go routine
func ({{.Variable}} *{{.CapitalName}}) Start(wg *sync.WaitGroup) {
wg.Add(1)
go func() {
{{.Variable}}.Run()
wg.Done()
}()
}
// Run implements the {{.CapitalName}} wrapper
func ({{.Variable}} *{{.CapitalName}}) Run() {
if {{.Variable}}.Verbose {
{{ if .WS }} log.Debugf(log.ExchangeSys,
"%s Websocket: %s.",
{{.Variable}}.Name,
common.IsEnabled({{.Variable}}.Websocket.IsEnabled())) {{ end }}
{{.Variable}}.PrintEnabledPairs()
}
if !{{.Variable}}.GetEnabledFeatures().AutoPairUpdates {
return
}
err := {{.Variable}}.UpdateTradablePairs(false)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
{{.Variable}}.Name,
err)
}
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func ({{.Variable}} *{{.CapitalName}}) FetchTradablePairs(asset asset.Item) ([]string, error) {
// Implement fetching the exchange available pairs if supported
return nil, nil
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func ({{.Variable}} *{{.CapitalName}}) UpdateTradablePairs(forceUpdate bool) error {
pairs, err := {{.Variable}}.FetchTradablePairs(asset.Spot)
if err != nil {
return err
}
return {{.Variable}}.UpdatePairs(currency.NewPairsFromStrings(pairs),
asset.Spot, false, forceUpdate)
}
// UpdateTicker updates and returns the ticker for a currency pair
func ({{.Variable}} *{{.CapitalName}}) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
// NOTE: EXAMPLE FOR GETTING TICKER PRICE
/*
var tickerPrice ticker.Price
tick, err := {{.Variable}}.GetTicker(p.String())
if err != nil {
return tickerPrice, err
}
tickerPrice = ticker.Price{
High: tick.High,
Low: tick.Low,
Bid: tick.Bid,
Ask: tick.Ask,
Open: tick.Open,
Close: tick.Close,
Pair: p,
}
err = ticker.ProcessTicker({{.Variable}}.Name, &tickerPrice, assetType)
if err != nil {
return tickerPrice, err
}
*/
return ticker.GetTicker({{.Variable}}.Name, p, assetType)
}
// FetchTicker returns the ticker for a currency pair
func ({{.Variable}} *{{.CapitalName}}) FetchTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
tickerNew, err := ticker.GetTicker({{.Variable}}.Name, p, assetType)
if err != nil {
return {{.Variable}}.UpdateTicker(p, assetType)
}
return tickerNew, nil
}
// FetchOrderbook returns orderbook base on the currency pair
func ({{.Variable}} *{{.CapitalName}}) FetchOrderbook(currency currency.Pair, assetType asset.Item) (orderbook.Base, error) {
ob, err := orderbook.Get({{.Variable}}.Name, currency, assetType)
if err != nil {
return {{.Variable}}.UpdateOrderbook(currency, assetType)
}
return ob, nil
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func ({{.Variable}} *{{.CapitalName}}) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
// NOTE: UPDATE ORDERBOOK EXAMPLE
/*
orderbookNew, err := {{.Variable}}.GetOrderBook(exchange.FormatExchangeCurrency({{.Variable}}.Name, p).String(), 1000)
if err != nil {
return orderBook, err
}
for x := range orderbookNew.Bids {
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Amount: orderbookNew.Bids[x].Quantity,
Price: orderbookNew.Bids[x].Price,
})
}
for x := range orderbookNew.Asks {
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Amount: orderBook.Asks[x].Quantity,
Price: orderBook.Asks[x].Price,
})
}
*/
orderBook.Pair = p
orderBook.ExchangeName = {{.Variable}}.Name
orderBook.AssetType = assetType
err := orderBook.Process()
if err != nil {
return orderBook, err
}
return orderbook.Get({{.Variable}}.Name, p, assetType)
}
// GetAccountInfo retrieves balances for all enabled currencies for the
// {{.CapitalName}} exchange
func ({{.Variable}} *{{.CapitalName}}) GetAccountInfo() (exchange.AccountInfo, error) {
return exchange.AccountInfo{}, common.ErrNotYetImplemented
}
// GetFundingHistory returns funding history, deposits and
// withdrawals
func ({{.Variable}} *{{.CapitalName}}) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrNotYetImplemented
}
// GetExchangeHistory returns historic trade data since exchange opening.
func ({{.Variable}} *{{.CapitalName}}) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
// SubmitOrder submits a new order
func ({{.Variable}} *{{.CapitalName}}) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
return submitOrderResponse, err
}
return submitOrderResponse, common.ErrNotYetImplemented
}
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func ({{.Variable}} *{{.CapitalName}}) ModifyOrder(action *order.Modify) (string, error) {
return "", common.ErrNotYetImplemented
}
// CancelOrder cancels an order by its corresponding ID number
func ({{.Variable}} *{{.CapitalName}}) CancelOrder(order *order.Cancel) error {
return common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func ({{.Variable}} *{{.CapitalName}}) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
return order.CancelAllResponse{}, common.ErrNotYetImplemented
}
// GetOrderInfo returns information on a current open order
func ({{.Variable}} *{{.CapitalName}}) GetOrderInfo(orderID string) (order.Detail, error) {
return order.Detail{}, common.ErrNotYetImplemented
}
// GetDepositAddress returns a deposit address for a specified currency
func ({{.Variable}} *{{.CapitalName}}) GetDepositAddress(cryptocurrency currency.Code, accountID string) (string, error) {
return "", common.ErrNotYetImplemented
}
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func ({{.Variable}} *{{.CapitalName}}) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) {
return "", common.ErrNotYetImplemented
}
// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is
// submitted
func ({{.Variable}} *{{.CapitalName}}) WithdrawFiatFunds(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrNotYetImplemented
}
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a withdrawal is
// submitted
func ({{.Variable}} *{{.CapitalName}}) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrNotYetImplemented
}
// GetWebsocket returns a pointer to the exchange websocket
func ({{.Variable}} *{{.CapitalName}}) GetWebsocket() (*wshandler.Websocket, error) {
return nil, common.ErrNotYetImplemented
}
// GetActiveOrders retrieves any orders that are active/open
func ({{.Variable}} *{{.CapitalName}}) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
return nil, common.ErrNotYetImplemented
}
// GetOrderHistory retrieves account order information
// Can Limit response to specific order status
func ({{.Variable}} *{{.CapitalName}}) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
return nil, common.ErrNotYetImplemented
}
// GetFeeByType returns an estimate of fee based on the type of transaction
func ({{.Variable}} *{{.CapitalName}}) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
return 0, common.ErrNotYetImplemented
}
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle subscribing
func ({{.Variable}} *{{.CapitalName}}) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
{{.Variable}}.Websocket.SubscribeToChannels(channels)
return nil
}
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle unsubscribing
func ({{.Variable}} *{{.CapitalName}}) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
{{.Variable}}.Websocket.RemoveSubscribedChannels(channels)
return nil
}
// GetSubscriptions returns a copied list of subscriptions
func ({{.Variable}} *{{.CapitalName}}) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
return nil, common.ErrNotYetImplemented
}
// AuthenticateWebsocket sends an authentication message to the websocket
func ({{.Variable}} *{{.CapitalName}}) AuthenticateWebsocket() error {
return common.ErrNotYetImplemented
}
{{end}}

View File

@@ -0,0 +1,191 @@
package main
import (
"log"
"math/rand"
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/engine"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
const (
totalWrappers = 20
)
func main() {
var err error
engine.Bot, err = engine.New()
if err != nil {
log.Fatalf("Failed to initialise engine. Err: %s", err)
}
engine.Bot.Settings = engine.Settings{
DisableExchangeAutoPairUpdates: true,
}
log.Printf("Loading exchanges..")
var wg sync.WaitGroup
for x := range exchange.Exchanges {
name := exchange.Exchanges[x]
err := engine.LoadExchange(name, true, &wg)
if err != nil {
log.Printf("Failed to load exchange %s. Err: %s", name, err)
continue
}
}
wg.Wait()
log.Println("Done.")
log.Printf("Testing exchange wrappers..")
results := make(map[string][]string)
wg = sync.WaitGroup{}
for x := range engine.Bot.Exchanges {
wg.Add(1)
go func(num int) {
name := engine.Bot.Exchanges[num].GetName()
results[name] = testWrappers(engine.Bot.Exchanges[num])
wg.Done()
}(x)
}
wg.Wait()
log.Println("Done.")
log.Println()
for name, funcs := range results {
pct := float64(totalWrappers-len(funcs)) / float64(totalWrappers) * 100
log.Printf("Exchange %s wrapper coverage [%d/%d - %.2f%%] | Total missing: %d", name, totalWrappers-len(funcs), totalWrappers, pct, len(funcs))
log.Printf("\t Wrappers not implemented:")
for x := range funcs {
log.Printf("\t - %s", funcs[x])
}
log.Println()
}
}
func testWrappers(e exchange.IBotExchange) []string {
p := currency.NewPair(currency.BTC, currency.USD)
assetType := asset.Spot
if !e.SupportsAsset(assetType) {
assets := e.GetAssetTypes()
rand.Seed(time.Now().Unix())
assetType = assets[rand.Intn(len(assets))]
}
var funcs []string
_, err := e.FetchTicker(p, assetType)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "FetchTicker")
}
_, err = e.UpdateTicker(p, assetType)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "UpdateTicker")
}
_, err = e.FetchOrderbook(p, assetType)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "FetchOrderbook")
}
_, err = e.UpdateOrderbook(p, assetType)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "UpdateOrderbook")
}
_, err = e.FetchTradablePairs(asset.Spot)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "FetchTradablePairs")
}
err = e.UpdateTradablePairs(false)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "UpdateTradablePairs")
}
_, err = e.GetAccountInfo()
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetAccountInfo")
}
_, err = e.GetExchangeHistory(p, assetType)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetExchangeHistory")
}
_, err = e.GetFundingHistory()
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetFundingHistory")
}
s := &order.Submit{
Pair: p,
OrderSide: order.Buy,
OrderType: order.Limit,
Amount: 1000000,
Price: 10000000000,
ClientID: "meow",
}
_, err = e.SubmitOrder(s)
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "SubmitOrder")
}
_, err = e.ModifyOrder(&order.Modify{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "ModifyOrder")
}
err = e.CancelOrder(&order.Cancel{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "CancelOrder")
}
_, err = e.CancelAllOrders(&order.Cancel{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "CancelAllOrders")
}
_, err = e.GetOrderInfo("1")
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetOrderInfo")
}
_, err = e.GetOrderHistory(&order.GetOrdersRequest{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetOrderHistory")
}
_, err = e.GetActiveOrders(&order.GetOrdersRequest{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetActiveOrders")
}
_, err = e.GetDepositAddress(currency.BTC, "")
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetDepositAddress")
}
_, err = e.WithdrawCryptocurrencyFunds(&exchange.CryptoWithdrawRequest{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "WithdrawCryptocurrencyFunds")
}
_, err = e.WithdrawFiatFunds(&exchange.FiatWithdrawRequest{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "WithdrawFiatFunds")
}
_, err = e.WithdrawFiatFundsToInternationalBank(&exchange.FiatWithdrawRequest{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "WithdrawFiatFundsToInternationalBank")
}
return funcs
}

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