mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
CI: Bump go version, linters and fix minor issues (#1010)
* Bump golang, golangci-lint versions and fix issues * Add -fno-stack-protector * Fix AppVeyor golangci-lint ver * Nitters * Nitters round 2
This commit is contained in:
@@ -36,7 +36,7 @@ services:
|
||||
|
||||
init:
|
||||
- set PATH=%POSTGRES_PATH%\bin;%PATH%
|
||||
- set PATH=C:\go118\bin;%PATH%
|
||||
- set PATH=C:\go119\bin;%PATH%
|
||||
|
||||
install:
|
||||
- set Path=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%Path%
|
||||
@@ -55,7 +55,7 @@ before_test:
|
||||
|
||||
test_script:
|
||||
# test back-end
|
||||
- go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2
|
||||
- go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0
|
||||
- '%GOPATH%\bin\golangci-lint.exe run --verbose'
|
||||
- ps: >-
|
||||
if($env:APPVEYOR_SCHEDULED_BUILD -eq 'true') {
|
||||
|
||||
4
.github/workflows/linter.yml
vendored
4
.github/workflows/linter.yml
vendored
@@ -8,8 +8,8 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18.x'
|
||||
go-version: '1.19.x'
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.46.2
|
||||
version: v1.48.0
|
||||
|
||||
2
.github/workflows/proto-lint.yml
vendored
2
.github/workflows/proto-lint.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
go-version: 1.19.x
|
||||
|
||||
- name: Setup build depends
|
||||
run: |
|
||||
|
||||
3
.github/workflows/tests.yml
vendored
3
.github/workflows/tests.yml
vendored
@@ -1,7 +1,7 @@
|
||||
on: [push, pull_request]
|
||||
name: CI
|
||||
env:
|
||||
GO_VERSION: 1.18.x
|
||||
GO_VERSION: 1.19.x
|
||||
jobs:
|
||||
backend-psql:
|
||||
name: GoCryptoTrader back-end with PSQL
|
||||
@@ -115,6 +115,7 @@ jobs:
|
||||
PSQL_SSLMODE: disable
|
||||
GOARCH: 386
|
||||
CGO_ENABLED: 1
|
||||
CGO_CFLAGS: -fno-stack-protector
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
@@ -18,12 +18,12 @@ linters:
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
# - unused
|
||||
# - varcheck
|
||||
|
||||
# disabled by default linters
|
||||
# - asasalint
|
||||
- asciicheck
|
||||
- bidichk
|
||||
- bodyclose
|
||||
@@ -38,8 +38,10 @@ linters:
|
||||
- errchkjson
|
||||
- errname
|
||||
# - errorlint
|
||||
- execinquery
|
||||
# - exhaustive
|
||||
# - exhaustivestruct
|
||||
# - exhaustivestruct // abandoned by its owner, replaced with exhaustruct
|
||||
# - exhaustruct
|
||||
- exportloopref
|
||||
# - forbidigo
|
||||
- forcetypeassert
|
||||
@@ -65,7 +67,7 @@ linters:
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- grouper
|
||||
- ifshort
|
||||
# - ifshort // deprecated by its owner
|
||||
# - importas
|
||||
# - interfacer // deprecated by its owner
|
||||
# - ireturn
|
||||
@@ -77,9 +79,13 @@ linters:
|
||||
- nakedret
|
||||
# - nestif
|
||||
- nilerr
|
||||
# - nilnil
|
||||
# - nlreturn
|
||||
- noctx
|
||||
- nolintlint
|
||||
# - nonamedreturns
|
||||
# - nosnakecase
|
||||
- nosprintfhostport
|
||||
# - paralleltest
|
||||
- prealloc
|
||||
- predeclared
|
||||
@@ -87,14 +93,18 @@ linters:
|
||||
- revive
|
||||
- rowserrcheck
|
||||
# - scopelint // deprecated since v1.39.0, replaced by exportloopref
|
||||
# - sqlclosecheck
|
||||
- sqlclosecheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
# - tagliatelle
|
||||
- tenv
|
||||
# - testpackage
|
||||
- thelper
|
||||
- tparallel
|
||||
- unconvert
|
||||
- unparam
|
||||
- usestdlibvars
|
||||
# - varnamelen
|
||||
- wastedassign
|
||||
- whitespace
|
||||
# - wrapcheck
|
||||
@@ -103,8 +113,6 @@ linters:
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
goconst:
|
||||
min-occurrences: 6
|
||||
depguard:
|
||||
@@ -139,3 +147,6 @@ issues:
|
||||
# The following silences false positives in table tests
|
||||
# https://github.com/kyoh86/scopelint/issues/4
|
||||
- Using the variable on range scope `ti` in function literal
|
||||
include:
|
||||
- EXC0012 # revive: Comment exported (.+) should have comment( \(or a comment on this block\))? or be unexported
|
||||
- EXC0014 # revive: Comment on exported (.+) should be of the form "(.+)..."
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.18 as build
|
||||
FROM golang:1.19 as build
|
||||
WORKDIR /go/src/github.com/thrasher-corp/gocryptotrader
|
||||
COPY . .
|
||||
RUN GO111MODULE=on go mod vendor
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
LDFLAGS = -ldflags "-w -s"
|
||||
GCTPKG = github.com/thrasher-corp/gocryptotrader
|
||||
LINTPKG = github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2
|
||||
LINTPKG = github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0
|
||||
LINTBIN = $(GOPATH)/bin/golangci-lint
|
||||
GCTLISTENPORT=9050
|
||||
GCTPROFILERLISTENPORT=8085
|
||||
|
||||
@@ -20,7 +20,8 @@ const (
|
||||
TradeStr = "trade"
|
||||
|
||||
// DataCandle is an int64 representation of a candle data type
|
||||
DataCandle = iota
|
||||
DataCandle int64 = iota
|
||||
// DataTrade is an int64 representation of a trade data type
|
||||
DataTrade
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -458,7 +457,7 @@ func TestValidate(t *testing.T) {
|
||||
|
||||
func TestReadConfigFromFile(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
passFile, err := ioutil.TempFile(tempDir, "*.start")
|
||||
passFile, err := os.CreateTemp(tempDir, "*.start")
|
||||
if err != nil {
|
||||
t.Fatalf("Problem creating temp file at %v: %s\n", passFile, err)
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func main() {
|
||||
yn := quickParse(reader)
|
||||
if yn == y || yn == yes {
|
||||
var fp, wd string
|
||||
extension := "strat" // nolint:misspell // its shorthand for strategy
|
||||
extension := "strat" //nolint:misspell // its shorthand for strategy
|
||||
for {
|
||||
wd, err = os.Getwd()
|
||||
if err != nil {
|
||||
@@ -427,9 +427,9 @@ func parseDatabase(reader *bufio.Reader, cfg *config.Config) error {
|
||||
}
|
||||
fmt.Println("What is the database Port? eg 1337")
|
||||
input = quickParse(reader)
|
||||
var port float64
|
||||
var port uint64
|
||||
if input != "" {
|
||||
port, err = strconv.ParseFloat(input, 64)
|
||||
port, err = strconv.ParseUint(input, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package database
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -39,7 +38,7 @@ func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
@@ -85,16 +84,7 @@ func TestLoadDataCandles(t *testing.T) {
|
||||
}
|
||||
database.MigrationDir = filepath.Join("..", "..", "..", "..", "database", "migrations")
|
||||
testhelpers.MigrationDir = filepath.Join("..", "..", "..", "..", "database", "migrations")
|
||||
_, err = testhelpers.ConnectToDatabase(&dbConfg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
bot.DatabaseManager, err = engine.SetupDatabaseConnectionManager(&bot.Config.Database)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = bot.DatabaseManager.Start(&bot.ServicesWG)
|
||||
conn, err := testhelpers.ConnectToDatabase(&dbConfg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -133,6 +123,10 @@ func TestLoadDataCandles(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err = conn.SQL.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadDataTrades(t *testing.T) {
|
||||
@@ -165,16 +159,7 @@ func TestLoadDataTrades(t *testing.T) {
|
||||
}
|
||||
database.MigrationDir = filepath.Join("..", "..", "..", "..", "database", "migrations")
|
||||
testhelpers.MigrationDir = filepath.Join("..", "..", "..", "..", "database", "migrations")
|
||||
_, err = testhelpers.ConnectToDatabase(&dbConfg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
bot.DatabaseManager, err = engine.SetupDatabaseConnectionManager(&bot.Config.Database)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = bot.DatabaseManager.Start(&bot.ServicesWG)
|
||||
conn, err := testhelpers.ConnectToDatabase(&dbConfg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -206,6 +191,10 @@ func TestLoadDataTrades(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err = conn.SQL.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadDataInvalid(t *testing.T) {
|
||||
|
||||
@@ -22,7 +22,7 @@ func EstimateSlippagePercentage(maximumSlippageRate, minimumSlippageRate decimal
|
||||
// eg 80 means for every dollar, keep 80%
|
||||
randSeed := int(minimumSlippageRate.IntPart()) - int(maximumSlippageRate.IntPart())
|
||||
if randSeed > 0 {
|
||||
result := int64(rand.Intn(randSeed)) // nolint:gosec // basic number generation required, no need for crypto/rand
|
||||
result := int64(rand.Intn(randSeed)) //nolint:gosec // basic number generation required, no need for crypto/rand
|
||||
|
||||
return maximumSlippageRate.Add(decimal.NewFromInt(result)).Div(decimal.NewFromInt(100))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ var (
|
||||
// ErrSimultaneousProcessingOnly is raised when a strategy is improperly configured
|
||||
ErrSimultaneousProcessingOnly = errors.New("this strategy only supports simultaneous processing")
|
||||
// ErrStrategyNotFound used when strategy specified in strategy config does not exist
|
||||
ErrStrategyNotFound = errors.New("not found. Please ensure the strategy-settings field 'name' is spelled properly in your .strat config") // nolint:misspell // its shorthand for strategy
|
||||
ErrStrategyNotFound = errors.New("not found. Please ensure the strategy-settings field 'name' is spelled properly in your .strat config") //nolint:misspell // its shorthand for strategy
|
||||
// ErrInvalidCustomSettings used when bad custom settings are found in the strategy config
|
||||
ErrInvalidCustomSettings = errors.New("invalid custom settings in config")
|
||||
// ErrTooMuchBadData used when there is too much missing data
|
||||
|
||||
@@ -77,6 +77,7 @@ func (b *Base) GetReasons() []string {
|
||||
return b.Reasons
|
||||
}
|
||||
|
||||
// GetBase returns an event base
|
||||
func (b *Base) GetBase() *Base {
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ func (d *Data) enhanceCandles() error {
|
||||
Asset: lookup.Asset,
|
||||
Pair: lookup.Pair,
|
||||
Interval: lookup.Interval,
|
||||
Watermark: fmt.Sprintf("%s - %s - %s", strings.Title(lookup.Exchange), lookup.Asset.String(), lookup.Pair.Upper()), // nolint // Title usage
|
||||
Watermark: fmt.Sprintf("%s - %s - %s", strings.Title(lookup.Exchange), lookup.Asset.String(), lookup.Pair.Upper()), //nolint // Title usage
|
||||
}
|
||||
|
||||
statsForCandles :=
|
||||
|
||||
@@ -1717,7 +1717,7 @@ func htmlScrapeBitfinex(htmlData *HTMLScrapingData) ([]string, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// htmlScrapeBinance gets checkstring for binance exchange
|
||||
// htmlScrapeBinance gets checkstring for binance exchange
|
||||
func htmlScrapeBinance(htmlData *HTMLScrapingData) ([]string, error) {
|
||||
temp, err := sendHTTPGetRequest(htmlData.Path, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -486,7 +486,7 @@ func GetPackageName(name string, capital bool) string {
|
||||
i = len(newStrings) - 1
|
||||
}
|
||||
if capital {
|
||||
return strings.Replace(strings.Title(newStrings[i]), "_", " ", -1) // nolint // ignore staticcheck strings.Title warning
|
||||
return strings.Replace(strings.Title(newStrings[i]), "_", " ", -1) //nolint // ignore staticcheck strings.Title warning
|
||||
}
|
||||
return newStrings[i]
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func makeExchange(exchangeDirectory string, configTestFile *config.Config, exch
|
||||
|
||||
fmt.Printf("Output directory: %s\n", exchangeDirectory)
|
||||
|
||||
exch.CapitalName = strings.Title(exch.Name) // nolint:staticcheck // Ignore Title usage warning
|
||||
exch.CapitalName = strings.Title(exch.Name) //nolint:staticcheck // Ignore Title usage warning
|
||||
exch.Variable = exch.Name[0:2]
|
||||
newExchConfig := &config.Exchange{}
|
||||
newExchConfig.Name = exch.CapitalName
|
||||
|
||||
@@ -878,7 +878,7 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
|
||||
}
|
||||
|
||||
func jsonifyInterface(params []interface{}) json.RawMessage {
|
||||
response, _ := json.MarshalIndent(params, "", " ") // nolint:errchkjson // TODO: ignore this for now
|
||||
response, _ := json.MarshalIndent(params, "", " ") //nolint:errchkjson // TODO: ignore this for now
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
@@ -83,8 +85,8 @@ func main() {
|
||||
}
|
||||
|
||||
listenAddr := cfg.RemoteControl.WebsocketRPC.ListenAddress
|
||||
wsHost := fmt.Sprintf("ws://%s:%d/ws", common.ExtractHost(listenAddr),
|
||||
common.ExtractPort(listenAddr))
|
||||
wsHost := fmt.Sprintf("ws://%s/ws", net.JoinHostPort(common.ExtractHost(listenAddr),
|
||||
strconv.Itoa(common.ExtractPort(listenAddr))))
|
||||
log.Printf("Connecting to websocket host: %s", wsHost)
|
||||
|
||||
var dialer websocket.Dialer
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -305,7 +306,7 @@ func EncodeURLValues(urlPath string, values url.Values) string {
|
||||
|
||||
// ExtractHost returns the hostname out of a string
|
||||
func ExtractHost(address string) string {
|
||||
host := strings.Split(address, ":")[0]
|
||||
host, _, _ := net.SplitHostPort(address)
|
||||
if host == "" {
|
||||
return "localhost"
|
||||
}
|
||||
@@ -314,12 +315,12 @@ func ExtractHost(address string) string {
|
||||
|
||||
// ExtractPort returns the port name out of a string
|
||||
func ExtractPort(host string) int {
|
||||
portStrs := strings.Split(host, ":")
|
||||
if len(portStrs) == 1 {
|
||||
_, port, _ := net.SplitHostPort(host)
|
||||
if port == "" {
|
||||
return 80
|
||||
}
|
||||
port, _ := strconv.Atoi(portStrs[1])
|
||||
return port
|
||||
portInt, _ := strconv.Atoi(port)
|
||||
return portInt
|
||||
}
|
||||
|
||||
// GetURIPath returns the path of a URL given a URI
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package crypto
|
||||
|
||||
// nolint:gosec // md5/sha1 hash functions used by some exchanges
|
||||
//nolint:gosec // md5/sha1 hash functions used by some exchanges
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
@@ -63,7 +63,7 @@ func GetRandomSalt(input []byte, saltLen int) ([]byte, error) {
|
||||
|
||||
// GetMD5 returns a MD5 hash of a byte array
|
||||
func GetMD5(input []byte) ([]byte, error) {
|
||||
m := md5.New() // nolint:gosec // hash function used by some exchanges
|
||||
m := md5.New() //nolint:gosec // hash function used by some exchanges
|
||||
_, err := m.Write(input)
|
||||
return m.Sum(nil), err
|
||||
}
|
||||
@@ -108,7 +108,7 @@ func GetHMAC(hashType int, input, key []byte) ([]byte, error) {
|
||||
// Sha1ToHex takes a string, sha1 hashes it and return a hex string of the
|
||||
// result
|
||||
func Sha1ToHex(data string) (string, error) {
|
||||
h := sha1.New() // nolint:gosec // hash function used by some exchanges
|
||||
h := sha1.New() //nolint:gosec // hash function used by some exchanges
|
||||
_, err := h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum(nil)), err
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func UnZip(src, dest string) (fileList []string, err error) {
|
||||
}
|
||||
|
||||
for x := range z.File {
|
||||
fPath := filepath.Join(dest, z.File[x].Name) // nolint // We ignore
|
||||
fPath := filepath.Join(dest, z.File[x].Name) //nolint // We ignore
|
||||
// gosec linter above because the code below files the file traversal
|
||||
// bug when extracting archives
|
||||
if !strings.HasPrefix(fPath, filepath.Clean(dest)+string(os.PathSeparator)) {
|
||||
|
||||
@@ -2,7 +2,6 @@ package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -162,7 +161,7 @@ func TestWriteAsCSV(t *testing.T) {
|
||||
{"Sup", "bra"},
|
||||
}
|
||||
|
||||
testFile, err := ioutil.TempFile(os.TempDir(), "gct-csv-test.*.csv")
|
||||
testFile, err := os.CreateTemp(os.TempDir(), "gct-csv-test.*.csv")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1771,7 +1771,7 @@ func (c *Config) AssetTypeEnabled(a asset.Item, exch string) (bool, error) {
|
||||
|
||||
err = cfg.CurrencyPairs.IsAssetEnabled(a)
|
||||
if err != nil {
|
||||
return false, nil // nolint:nilerr // non-fatal error
|
||||
return false, nil //nolint:nilerr // non-fatal error
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -142,7 +141,7 @@ func TestEncryptTwiceReusesSaltButNewCipher(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// Prepare input
|
||||
passFile, err := ioutil.TempFile(tempDir, "*.pw")
|
||||
passFile, err := os.CreateTemp(tempDir, "*.pw")
|
||||
if err != nil {
|
||||
t.Fatalf("Problem creating temp file at %s: %s\n", tempDir, err)
|
||||
}
|
||||
@@ -318,7 +317,7 @@ func TestSaveConfigToFileWithErrorInPasswordPrompt(t *testing.T) {
|
||||
EncryptConfig: fileEncryptionEnabled,
|
||||
}
|
||||
testData := []byte("testdata")
|
||||
f, err := ioutil.TempFile("", "")
|
||||
f, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -355,7 +354,7 @@ func TestSaveConfigToFileWithErrorInPasswordPrompt(t *testing.T) {
|
||||
func withInteractiveResponse(t *testing.T, response string, body func() error) error {
|
||||
t.Helper()
|
||||
// Answers to the prompt
|
||||
responseFile, err := ioutil.TempFile("", "*.in")
|
||||
responseFile, err := os.CreateTemp("", "*.in")
|
||||
if err != nil {
|
||||
return fmt.Errorf("problem creating temp file: %w", err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -1576,7 +1575,7 @@ func TestCheckExchangeConfigValues(t *testing.T) {
|
||||
}
|
||||
|
||||
// Make a sneaky copy for bank account testing
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
cpy := append(cfg.Exchanges[:0:0], cfg.Exchanges...)
|
||||
|
||||
// Test empty exchange name for an enabled exchange
|
||||
@@ -1729,7 +1728,7 @@ func TestSaveConfigToFile(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("TestSaveConfig.LoadConfig: %s", err.Error())
|
||||
}
|
||||
f, err := ioutil.TempFile("", "")
|
||||
f, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
t.Errorf("TestSaveConfig create file: %s", err)
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ var (
|
||||
BAT = NewCode("BAT")
|
||||
ETP = NewCode("ETP")
|
||||
HOT = NewCode("HOT")
|
||||
STRAT = NewCode("STRAT") // nolint // Cryptocurrency code
|
||||
STRAT = NewCode("STRAT") //nolint // Cryptocurrency code
|
||||
GNT = NewCode("GNT")
|
||||
REP = NewCode("REP")
|
||||
SNT = NewCode("SNT")
|
||||
@@ -372,9 +372,9 @@ var (
|
||||
STQ = NewCode("STQ")
|
||||
INK = NewCode("INK")
|
||||
HBZ = NewCode("HBZ")
|
||||
USDT_ETH = NewCode("USDT_ETH") // nolint // Cryptocurrency code
|
||||
QTUM_ETH = NewCode("QTUM_ETH") // nolint // Cryptocurrency code
|
||||
BTM_ETH = NewCode("BTM_ETH") // nolint // Cryptocurrency code
|
||||
USDT_ETH = NewCode("USDT_ETH") //nolint // Cryptocurrency code
|
||||
QTUM_ETH = NewCode("QTUM_ETH") //nolint // Cryptocurrency code
|
||||
BTM_ETH = NewCode("BTM_ETH") //nolint // Cryptocurrency code
|
||||
FIL = NewCode("FIL")
|
||||
STX = NewCode("STX")
|
||||
BOT = NewCode("BOT")
|
||||
@@ -387,7 +387,7 @@ var (
|
||||
GOD = NewCode("GOD")
|
||||
SMT = NewCode("SMT")
|
||||
BTF = NewCode("BTF")
|
||||
NAS_ETH = NewCode("NAS_ETH") // nolint // Cryptocurrency code
|
||||
NAS_ETH = NewCode("NAS_ETH") //nolint // Cryptocurrency code
|
||||
TSL = NewCode("TSL")
|
||||
BIFI = NewCode("BIFI")
|
||||
BNTY = NewCode("BNTY")
|
||||
@@ -413,7 +413,7 @@ var (
|
||||
MOBI = NewCode("MOBI")
|
||||
LEDU = NewCode("LEDU")
|
||||
DBC = NewCode("DBC")
|
||||
MKR_OLD = NewCode("MKR_OLD") // nolint // Cryptocurrency code
|
||||
MKR_OLD = NewCode("MKR_OLD") //nolint // Cryptocurrency code
|
||||
DPY = NewCode("DPY")
|
||||
BCDN = NewCode("BCDN")
|
||||
EOSDAC = NewCode("EOSDAC")
|
||||
@@ -422,7 +422,7 @@ var (
|
||||
PPS = NewCode("PPS")
|
||||
BOE = NewCode("BOE")
|
||||
MEDX = NewCode("MEDX")
|
||||
SMT_ETH = NewCode("SMT_ETH") // nolint // Cryptocurrency code
|
||||
SMT_ETH = NewCode("SMT_ETH") //nolint // Cryptocurrency code
|
||||
CS = NewCode("CS")
|
||||
MAN = NewCode("MAN")
|
||||
REM = NewCode("REM")
|
||||
@@ -442,7 +442,7 @@ var (
|
||||
SWTH = NewCode("SWTH")
|
||||
NKN = NewCode("NKN")
|
||||
SOUL = NewCode("SOUL")
|
||||
GALA_NEO = NewCode("GALA_NEO") // nolint // Cryptocurrency code
|
||||
GALA_NEO = NewCode("GALA_NEO") //nolint // Cryptocurrency code
|
||||
LRN = NewCode("LRN")
|
||||
GSE = NewCode("GSE")
|
||||
RATING = NewCode("RATING")
|
||||
@@ -1669,7 +1669,7 @@ var (
|
||||
YER = NewCode("YER")
|
||||
ZWD = NewCode("ZWD")
|
||||
XETH = NewCode("XETH")
|
||||
FX_BTC = NewCode("FX_BTC") // nolint // Cryptocurrency code
|
||||
FX_BTC = NewCode("FX_BTC") //nolint // Cryptocurrency code
|
||||
AAVE = NewCode("AAVE")
|
||||
YFI = NewCode("YFI")
|
||||
BAL = NewCode("BAL")
|
||||
@@ -2512,7 +2512,7 @@ var (
|
||||
NAFT = NewCode("NAFT")
|
||||
SHIBLITE = NewCode("SHIBLITE")
|
||||
BHD = NewCode("BHD")
|
||||
THN = NewCode("THN") // nolint:misspell // false positive
|
||||
THN = NewCode("THN") //nolint:misspell // false positive
|
||||
DOGEDASH = NewCode("DOGEDASH")
|
||||
FARA = NewCode("FARA")
|
||||
FIL120 = NewCode("FIL120")
|
||||
@@ -2811,7 +2811,7 @@ var (
|
||||
SBR = NewCode("SBR")
|
||||
BMARS = NewCode("BMARS")
|
||||
GOMI = NewCode("GOMI")
|
||||
ONOT = NewCode("ONOT") // nolint:misspell // false positive
|
||||
ONOT = NewCode("ONOT") //nolint:misspell // false positive
|
||||
GOKU = NewCode("GOKU")
|
||||
MINTYS = NewCode("MINTYS")
|
||||
PONYO = NewCode("PONYO")
|
||||
|
||||
@@ -130,24 +130,24 @@ func (c *Coinmarketcap) GetCryptocurrencyHistoricalListings() ([]CryptocurrencyH
|
||||
// Status Status `json:"status"`
|
||||
// }{}
|
||||
|
||||
// nolint:gocritic // unused code, used as example
|
||||
//nolint:gocritic // unused code, used as example
|
||||
// err := c.CheckAccountPlan(0)
|
||||
// if err != nil {
|
||||
// return resp.Data, err
|
||||
// }
|
||||
|
||||
// nolint:gocritic // unused code, used as example
|
||||
//nolint:gocritic // unused code, used as example
|
||||
// err = c.SendHTTPRequest(http.MethodGet, endpointCryptocurrencyHistoricalListings, nil, &resp)
|
||||
// if err != nil {
|
||||
// return resp.Data, err
|
||||
// }
|
||||
|
||||
// nolint:gocritic // unused code, used as example
|
||||
//nolint:gocritic // unused code, used as example
|
||||
// if resp.Status.ErrorCode != 0 {
|
||||
// return resp.Data, errors.New(resp.Status.ErrorMessage)
|
||||
// }
|
||||
|
||||
// nolint:gocritic // unused code, used as example
|
||||
//nolint:gocritic // unused code, used as example
|
||||
// return resp.Data, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ func (p Pairs) FindDifferences(pairs Pairs) (newPairs, removedPairs Pairs) {
|
||||
// GetRandomPair returns a random pair from a list of pairs
|
||||
func (p Pairs) GetRandomPair() Pair {
|
||||
if pairsLen := len(p); pairsLen != 0 {
|
||||
return p[rand.Intn(pairsLen)] // nolint:gosec // basic number generation required, no need for crypo/rand
|
||||
return p[rand.Intn(pairsLen)] //nolint:gosec // basic number generation required, no need for crypo/rand
|
||||
}
|
||||
return EMPTYPAIR
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package currency
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -12,7 +11,7 @@ import (
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -3,6 +3,8 @@ package postgres
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
// import go libpq driver package
|
||||
_ "github.com/lib/pq"
|
||||
@@ -21,11 +23,11 @@ func Connect(cfg *database.Config) (*database.Instance, error) {
|
||||
cfg.SSLMode = "disable"
|
||||
}
|
||||
|
||||
configDSN := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
|
||||
host := net.JoinHostPort(cfg.Host, strconv.FormatUint(uint64(cfg.Port), 10))
|
||||
configDSN := fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
|
||||
cfg.Username,
|
||||
cfg.Password,
|
||||
cfg.Host,
|
||||
cfg.Port,
|
||||
host,
|
||||
cfg.Database,
|
||||
cfg.SSLMode)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package audit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -16,7 +15,7 @@ import (
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -3,7 +3,6 @@ package candle
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -39,7 +38,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -3,7 +3,6 @@ package datahistoryjob
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -41,7 +40,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package datahistoryjobresult
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -38,7 +37,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package exchange
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -39,7 +38,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -2,7 +2,6 @@ package script
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -21,7 +20,7 @@ var (
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -2,7 +2,6 @@ package trade
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -41,11 +40,16 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
|
||||
exitCode := m.Run()
|
||||
if err = os.RemoveAll(testhelpers.TempDir); err != nil {
|
||||
fmt.Printf("failed to remove temp dir: %s", err)
|
||||
}
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func TestTrades(t *testing.T) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package withdraw
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -38,7 +37,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
var err error
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
@@ -139,7 +138,7 @@ func seedWithdrawData() {
|
||||
},
|
||||
},
|
||||
}
|
||||
rnd := rand.Intn(2) // nolint:gosec // used for generating test data, no need to import crypo/rand
|
||||
rnd := rand.Intn(2) //nolint:gosec // used for generating test data, no need to import crypo/rand
|
||||
if rnd == 0 {
|
||||
resp.RequestDetails.Currency = currency.AUD
|
||||
resp.RequestDetails.Type = 1
|
||||
|
||||
@@ -2,7 +2,6 @@ package testhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -14,7 +13,7 @@ import (
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
PostgresTestDatabase = GetConnectionDetails()
|
||||
TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -169,8 +169,9 @@ func (m *apiServerManager) StartRESTServer() error {
|
||||
m.restRouter = m.newRouter(true)
|
||||
if m.restHTTPServer == nil {
|
||||
m.restHTTPServer = &http.Server{
|
||||
Addr: m.restListenAddress,
|
||||
Handler: m.restRouter,
|
||||
Addr: m.restListenAddress,
|
||||
Handler: m.restRouter,
|
||||
ReadHeaderTimeout: time.Minute,
|
||||
}
|
||||
}
|
||||
m.wgRest.Add(1)
|
||||
@@ -429,8 +430,9 @@ func (m *apiServerManager) StartWebsocketServer() error {
|
||||
m.websocketRouter = m.newRouter(false)
|
||||
if m.websocketHTTPServer == nil {
|
||||
m.websocketHTTPServer = &http.Server{
|
||||
Addr: m.websocketListenAddress,
|
||||
Handler: m.websocketRouter,
|
||||
Addr: m.websocketListenAddress,
|
||||
Handler: m.websocketRouter,
|
||||
ReadHeaderTimeout: time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -710,7 +710,7 @@ func (m *DataHistoryManager) processCandleData(job *DataHistoryJob, exch exchang
|
||||
if err != nil {
|
||||
r.Result += "could not get candles: " + err.Error() + ". "
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
job.rangeHolder.SetHasDataFromCandles(candles.Candles)
|
||||
for i := range job.rangeHolder.Ranges[intervalIndex].Intervals {
|
||||
@@ -759,13 +759,13 @@ func (m *DataHistoryManager) processTradeData(job *DataHistoryJob, exch exchange
|
||||
if err != nil {
|
||||
r.Result += "could not get trades: " + err.Error() + ". "
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
candles, err := trade.ConvertTradesToCandles(job.Interval, trades...)
|
||||
if err != nil {
|
||||
r.Result += "could not convert candles to trades: " + err.Error() + ". "
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
job.rangeHolder.SetHasDataFromCandles(candles.Candles)
|
||||
for i := range job.rangeHolder.Ranges[intervalIndex].Intervals {
|
||||
@@ -826,13 +826,13 @@ func (m *DataHistoryManager) convertTradesToCandles(job *DataHistoryJob, startRa
|
||||
if err != nil {
|
||||
r.Result = "could not get trades in range: " + err.Error()
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
candles, err := trade.ConvertTradesToCandles(job.Interval, trades...)
|
||||
if err != nil {
|
||||
r.Result = "could not convert trades in range: " + err.Error()
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
candles.SourceJobID = job.ID
|
||||
err = m.saveCandlesInBatches(job, &candles, r)
|
||||
@@ -865,13 +865,13 @@ func (m *DataHistoryManager) convertCandleData(job *DataHistoryJob, startRange,
|
||||
if err != nil {
|
||||
r.Result = "could not get candles in range: " + err.Error()
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
newCandles, err := kline.ConvertToNewInterval(&candles, job.ConversionInterval)
|
||||
if err != nil {
|
||||
r.Result = "could not convert candles in range: " + err.Error()
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
newCandles.SourceJobID = job.ID
|
||||
err = m.saveCandlesInBatches(job, &candles, r)
|
||||
@@ -913,14 +913,14 @@ func (m *DataHistoryManager) validateCandles(job *DataHistoryJob, exch exchange.
|
||||
if err != nil {
|
||||
r.Result = "could not get API candles: " + err.Error()
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
apiCandles.ValidationJobID = job.ID
|
||||
dbCandles, err := m.candleLoader(job.Exchange, job.Pair, job.Asset, job.Interval, startRange, endRange)
|
||||
if err != nil {
|
||||
r.Result = "could not get database candles: " + err.Error()
|
||||
r.Status = dataHistoryStatusFailed
|
||||
return r, nil // nolint:nilerr // error is returned in the job result
|
||||
return r, nil //nolint:nilerr // error is returned in the job result
|
||||
}
|
||||
if len(dbCandles.Candles) == 0 {
|
||||
r.Result = fmt.Sprintf("missing database candles for period %v-%v", startRange, endRange)
|
||||
|
||||
@@ -654,7 +654,7 @@ func TestCompareJobsToData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunJob(t *testing.T) { // nolint // TO-DO: Fix race t.Parallel() usage
|
||||
func TestRunJob(t *testing.T) { //nolint // TO-DO: Fix race t.Parallel() usage
|
||||
testCases := []*DataHistoryJob{
|
||||
{
|
||||
Nickname: "TestRunJobDataHistoryCandleDataType",
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestGetRPCEndpoints(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetSubsystem(t *testing.T) { // nolint // TO-DO: Fix race t.Parallel() usage
|
||||
func TestSetSubsystem(t *testing.T) { //nolint // TO-DO: Fix race t.Parallel() usage
|
||||
testCases := []struct {
|
||||
Subsystem string
|
||||
Engine *Engine
|
||||
|
||||
@@ -2579,7 +2579,7 @@ func (s *RPCServer) GCTScriptQuery(_ context.Context, r *gctrpc.GCTScriptQueryRe
|
||||
|
||||
UUID, err := uuid.FromString(r.Script.Uuid)
|
||||
if err != nil {
|
||||
// nolint:nilerr // error is returned in the GCTScriptQueryResponse
|
||||
//nolint:nilerr // error is returned in the GCTScriptQueryResponse
|
||||
return &gctrpc.GCTScriptQueryResponse{Status: MsgStatusError, Data: err.Error()}, nil
|
||||
}
|
||||
|
||||
@@ -2626,7 +2626,7 @@ func (s *RPCServer) GCTScriptExecute(_ context.Context, r *gctrpc.GCTScriptExecu
|
||||
|
||||
script := filepath.Join(r.Script.Path, r.Script.Name)
|
||||
if err := gctVM.Load(script); err != nil {
|
||||
return &gctrpc.GenericResponse{ // nolint:nilerr // error is returned in the generic response
|
||||
return &gctrpc.GenericResponse{ //nolint:nilerr // error is returned in the generic response
|
||||
Status: MsgStatusError,
|
||||
Data: err.Error(),
|
||||
}, nil
|
||||
@@ -2648,7 +2648,7 @@ func (s *RPCServer) GCTScriptStop(_ context.Context, r *gctrpc.GCTScriptStopRequ
|
||||
|
||||
UUID, err := uuid.FromString(r.Script.Uuid)
|
||||
if err != nil {
|
||||
return &gctrpc.GenericResponse{Status: MsgStatusError, Data: err.Error()}, nil // nolint:nilerr // error is returned in the generic response
|
||||
return &gctrpc.GenericResponse{Status: MsgStatusError, Data: err.Error()}, nil //nolint:nilerr // error is returned in the generic response
|
||||
}
|
||||
|
||||
v, f := gctscript.AllVMSync.Load(UUID)
|
||||
@@ -2818,7 +2818,7 @@ func (s *RPCServer) GCTScriptStopAll(context.Context, *gctrpc.GCTScriptStopAllRe
|
||||
|
||||
err := s.gctScriptManager.ShutdownAll()
|
||||
if err != nil {
|
||||
return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil // nolint:nilerr // error is returned in the generic response
|
||||
return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil //nolint:nilerr // error is returned in the generic response
|
||||
}
|
||||
|
||||
return &gctrpc.GenericResponse{
|
||||
@@ -2836,7 +2836,7 @@ func (s *RPCServer) GCTScriptAutoLoadToggle(_ context.Context, r *gctrpc.GCTScri
|
||||
if r.Status {
|
||||
err := s.gctScriptManager.Autoload(r.Script, true)
|
||||
if err != nil {
|
||||
// nolint:nilerr // error is returned in the generic response
|
||||
//nolint:nilerr // error is returned in the generic response
|
||||
return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil
|
||||
}
|
||||
return &gctrpc.GenericResponse{Status: "success", Data: "script " + r.Script + " removed from autoload list"}, nil
|
||||
@@ -2844,7 +2844,7 @@ func (s *RPCServer) GCTScriptAutoLoadToggle(_ context.Context, r *gctrpc.GCTScri
|
||||
|
||||
err := s.gctScriptManager.Autoload(r.Script, false)
|
||||
if err != nil {
|
||||
return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil // nolint:nilerr // error is returned in the generic response
|
||||
return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil //nolint:nilerr // error is returned in the generic response
|
||||
}
|
||||
return &gctrpc.GenericResponse{Status: "success", Data: "script " + r.Script + " added to autoload list"}, nil
|
||||
}
|
||||
|
||||
@@ -22,14 +22,17 @@ const (
|
||||
// changed while the same keys can be used.
|
||||
ContextSubAccountFlag contextCredential = "subaccountoverride"
|
||||
|
||||
apiKeyDisplaySize = 16
|
||||
)
|
||||
|
||||
// Default credential values
|
||||
const (
|
||||
Key = "key"
|
||||
Secret = "secret"
|
||||
SubAccountSTR = "subaccount"
|
||||
ClientID = "clientid"
|
||||
OneTimePassword = "otp"
|
||||
PEMKey = "pemkey"
|
||||
|
||||
apiKeyDisplaySize = 16
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -13,7 +13,9 @@ const (
|
||||
alerting
|
||||
|
||||
dataToActuatorDefaultBuffer = 1
|
||||
PreAllocCommsDefaultBuffer = 5
|
||||
|
||||
// PreAllocCommsDefaultBuffer is the default buffer size for comms
|
||||
PreAllocCommsDefaultBuffer = 5
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -101,8 +101,10 @@ func BenchmarkAlert(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// 150352 9916 ns/op 681 B/op 4 allocs/op // PREV
|
||||
// 87436 14724 ns/op 682 B/op 4 allocs/op // CURRENT
|
||||
// BenchmarkWait benchmark
|
||||
//
|
||||
// 150352 9916 ns/op 681 B/op 4 allocs/op // PREV
|
||||
// 87436 14724 ns/op 682 B/op 4 allocs/op // CURRENT
|
||||
func BenchmarkWait(b *testing.B) {
|
||||
n := Notice{}
|
||||
for x := 0; x < b.N; x++ {
|
||||
|
||||
@@ -62,7 +62,7 @@ func Supported() Items {
|
||||
return supportedList
|
||||
}
|
||||
|
||||
// returns an Item to string
|
||||
// String converts an Item to its string representation
|
||||
func (a Item) String() string {
|
||||
switch a {
|
||||
case Spot:
|
||||
|
||||
@@ -775,8 +775,9 @@ func (bi *Binanceus) GetTradeFee(ctx context.Context, recvWindow uint, symbol st
|
||||
|
||||
// GetAssetDistributionHistory this endpoint to query
|
||||
// asset distribution records, including for staking, referrals and airdrops etc.
|
||||
//
|
||||
// INPUTS:
|
||||
// asset: string , startTime & endTime unix time in Milli seconds, recvWindow(duration in milli seconds > 2000 to < 6000)
|
||||
// asset: string , startTime & endTime unix time in Milli seconds, recvWindow(duration in milli seconds > 2000 to < 6000)
|
||||
func (bi *Binanceus) GetAssetDistributionHistory(ctx context.Context, asset string, startTime, endTime uint64, recvWindow uint) (*AssetDistributionHistories, error) {
|
||||
params := url.Values{}
|
||||
timestamp := time.Now().UnixMilli()
|
||||
|
||||
@@ -309,7 +309,7 @@ func (b *Bitflyer) SendHTTPRequest(ctx context.Context, ep exchange.URL, path st
|
||||
// if you have access and update the authenticated requests
|
||||
// TODO: Fill out this function once API access is obtained
|
||||
func (b *Bitflyer) SendAuthHTTPRequest() {
|
||||
// nolint:gocritic // code example
|
||||
//nolint:gocritic // code example
|
||||
// headers := make(map[string]string)
|
||||
// headers["ACCESS-KEY"] = b.API.Credentials.Key
|
||||
// headers["ACCESS-TIMESTAMP"] = strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
|
||||
@@ -254,14 +254,7 @@ func TestMarketSellOrder(t *testing.T) {
|
||||
func TestUpdateTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
cp := currency.NewPair(currency.QTUM, currency.KRW)
|
||||
_, err := b.UpdateTicker(context.Background(), cp, asset.Spot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cp = currency.NewPair(currency.BTC, currency.KRW)
|
||||
_, err = b.UpdateTicker(context.Background(), cp, asset.Spot)
|
||||
if err != nil {
|
||||
if _, err := b.UpdateTicker(context.Background(), cp, asset.Spot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ const (
|
||||
|
||||
// Authenticated endpoints
|
||||
bitmexEndpointAPIkeys = "/apiKey"
|
||||
bitmexEndpointDisableAPIkey = "/apiKey/disable" // nolint:gosec // false positive
|
||||
bitmexEndpointEnableAPIkey = "/apiKey/enable" // nolint:gosec // false positive
|
||||
bitmexEndpointDisableAPIkey = "/apiKey/disable" //nolint:gosec // false positive
|
||||
bitmexEndpointEnableAPIkey = "/apiKey/enable" //nolint:gosec // false positive
|
||||
bitmexEndpointTrollboxSend = "/chat"
|
||||
bitmexEndpointExecution = "/execution"
|
||||
bitmexEndpointExecutionTradeHistory = "/execution/tradeHistory"
|
||||
|
||||
@@ -783,7 +783,7 @@ func TestWithdraw(t *testing.T) {
|
||||
Amount: -1,
|
||||
Currency: currency.BTC,
|
||||
Description: "WITHDRAW IT ALL",
|
||||
OneTimePassword: 000000, // nolint // gocritic false positive
|
||||
OneTimePassword: 000000, //nolint // gocritic false positive
|
||||
}
|
||||
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
|
||||
@@ -17,7 +17,9 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -654,3 +656,12 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange
|
||||
func parseTime(dateTime string) (time.Time, error) {
|
||||
return time.Parse(bitstampTimeLayout, dateTime)
|
||||
}
|
||||
|
||||
func filterOrderbookZeroBidPrice(ob *orderbook.Base) {
|
||||
if len(ob.Bids) == 0 || ob.Bids[len(ob.Bids)-1].Price != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
log.Warnf(log.ExchangeSys, "%s %s %s orderbook has zero bid price, filtering.", ob.Exchange, ob.Pair, ob.Asset)
|
||||
ob.Bids = ob.Bids[0 : len(ob.Bids)-1]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/banking"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
@@ -757,3 +758,37 @@ func TestGetHistoricTrades(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderbookZeroBidPrice(t *testing.T) {
|
||||
t.Parallel()
|
||||
ob := &orderbook.Base{
|
||||
Exchange: "Bitstamp",
|
||||
Pair: currency.NewPair(currency.BTC, currency.USD),
|
||||
Asset: asset.Spot,
|
||||
}
|
||||
|
||||
filterOrderbookZeroBidPrice(ob)
|
||||
|
||||
ob.Bids = orderbook.Items{
|
||||
{Price: 69, Amount: 1337},
|
||||
{Price: 0, Amount: 69},
|
||||
}
|
||||
|
||||
filterOrderbookZeroBidPrice(ob)
|
||||
|
||||
if ob.Bids[0].Price != 69 || ob.Bids[0].Amount != 1337 || len(ob.Bids) != 1 {
|
||||
t.Error("invalid orderbook bid values")
|
||||
}
|
||||
|
||||
ob.Bids = orderbook.Items{
|
||||
{Price: 59, Amount: 1337},
|
||||
{Price: 42, Amount: 8595},
|
||||
}
|
||||
|
||||
filterOrderbookZeroBidPrice(ob)
|
||||
|
||||
if ob.Bids[0].Price != 59 || ob.Bids[0].Amount != 1337 ||
|
||||
ob.Bids[1].Price != 42 || ob.Bids[1].Amount != 8595 || len(ob.Bids) != 2 {
|
||||
t.Error("invalid orderbook bid values")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
bitstampWSURL = "wss://ws.bitstamp.net" // nolint // gosec false positive
|
||||
bitstampWSURL = "wss://ws.bitstamp.net" //nolint // gosec false positive
|
||||
)
|
||||
|
||||
// WsConnect connects to a websocket feed
|
||||
@@ -118,7 +118,7 @@ func (b *Bitstamp) wsHandleData(respRaw []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.wsUpdateOrderbook(wsOrderBookTemp.Data, p, asset.Spot)
|
||||
err = b.wsUpdateOrderbook(&wsOrderBookTemp.Data, p, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -251,12 +251,21 @@ func (b *Bitstamp) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscriptio
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bitstamp) wsUpdateOrderbook(update websocketOrderBook, p currency.Pair, assetType asset.Item) error {
|
||||
func (b *Bitstamp) wsUpdateOrderbook(update *websocketOrderBook, p currency.Pair, assetType asset.Item) error {
|
||||
if len(update.Asks) == 0 && len(update.Bids) == 0 {
|
||||
return errors.New("no orderbook data")
|
||||
}
|
||||
asks := make([]orderbook.Item, len(update.Asks))
|
||||
bids := make([]orderbook.Item, len(update.Bids))
|
||||
|
||||
obUpdate := &orderbook.Base{
|
||||
Bids: make(orderbook.Items, len(update.Bids)),
|
||||
Asks: make(orderbook.Items, len(update.Asks)),
|
||||
Pair: p,
|
||||
LastUpdated: time.Unix(update.Timestamp, 0),
|
||||
Asset: assetType,
|
||||
Exchange: b.Name,
|
||||
VerifyOrderbook: b.CanVerifyOrderbook,
|
||||
}
|
||||
|
||||
for i := range update.Asks {
|
||||
target, err := strconv.ParseFloat(update.Asks[i][0], 64)
|
||||
if err != nil {
|
||||
@@ -266,7 +275,7 @@ func (b *Bitstamp) wsUpdateOrderbook(update websocketOrderBook, p currency.Pair,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
asks[i] = orderbook.Item{Price: target, Amount: amount}
|
||||
obUpdate.Asks[i] = orderbook.Item{Price: target, Amount: amount}
|
||||
}
|
||||
for i := range update.Bids {
|
||||
target, err := strconv.ParseFloat(update.Bids[i][0], 64)
|
||||
@@ -277,17 +286,10 @@ func (b *Bitstamp) wsUpdateOrderbook(update websocketOrderBook, p currency.Pair,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bids[i] = orderbook.Item{Price: target, Amount: amount}
|
||||
obUpdate.Bids[i] = orderbook.Item{Price: target, Amount: amount}
|
||||
}
|
||||
return b.Websocket.Orderbook.LoadSnapshot(&orderbook.Base{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: p,
|
||||
LastUpdated: time.Unix(update.Timestamp, 0),
|
||||
Asset: assetType,
|
||||
Exchange: b.Name,
|
||||
VerifyOrderbook: b.CanVerifyOrderbook,
|
||||
})
|
||||
filterOrderbookZeroBidPrice(obUpdate)
|
||||
return b.Websocket.Orderbook.LoadSnapshot(obUpdate)
|
||||
}
|
||||
|
||||
func (b *Bitstamp) seedOrderBook(ctx context.Context) error {
|
||||
@@ -306,27 +308,31 @@ func (b *Bitstamp) seedOrderBook(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var newOrderBook orderbook.Base
|
||||
newOrderBook.Asks = make(orderbook.Items, len(orderbookSeed.Asks))
|
||||
newOrderBook := &orderbook.Base{
|
||||
Pair: p[x],
|
||||
Asset: asset.Spot,
|
||||
Exchange: b.Name,
|
||||
VerifyOrderbook: b.CanVerifyOrderbook,
|
||||
Bids: make(orderbook.Items, len(orderbookSeed.Bids)),
|
||||
Asks: make(orderbook.Items, len(orderbookSeed.Asks)),
|
||||
}
|
||||
|
||||
for i := range orderbookSeed.Asks {
|
||||
newOrderBook.Asks[i] = orderbook.Item{
|
||||
Price: orderbookSeed.Asks[i].Price,
|
||||
Amount: orderbookSeed.Asks[i].Amount,
|
||||
}
|
||||
}
|
||||
newOrderBook.Bids = make(orderbook.Items, len(orderbookSeed.Bids))
|
||||
for i := range orderbookSeed.Bids {
|
||||
newOrderBook.Bids[i] = orderbook.Item{
|
||||
Price: orderbookSeed.Bids[i].Price,
|
||||
Amount: orderbookSeed.Bids[i].Amount,
|
||||
}
|
||||
}
|
||||
newOrderBook.Pair = p[x]
|
||||
newOrderBook.Asset = asset.Spot
|
||||
newOrderBook.Exchange = b.Name
|
||||
newOrderBook.VerifyOrderbook = b.CanVerifyOrderbook
|
||||
|
||||
err = b.Websocket.Orderbook.LoadSnapshot(&newOrderBook)
|
||||
filterOrderbookZeroBidPrice(newOrderBook)
|
||||
|
||||
err = b.Websocket.Orderbook.LoadSnapshot(newOrderBook)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -414,6 +414,8 @@ func (b *Bitstamp) UpdateOrderbook(ctx context.Context, p currency.Pair, assetTy
|
||||
}
|
||||
}
|
||||
|
||||
filterOrderbookZeroBidPrice(book)
|
||||
|
||||
book.Asks = make(orderbook.Items, len(orderbookNew.Asks))
|
||||
for x := range orderbookNew.Asks {
|
||||
book.Asks[x] = orderbook.Item{
|
||||
@@ -421,6 +423,7 @@ func (b *Bitstamp) UpdateOrderbook(ctx context.Context, p currency.Pair, assetTy
|
||||
Price: orderbookNew.Asks[x].Price,
|
||||
}
|
||||
}
|
||||
|
||||
err = book.Process()
|
||||
if err != nil {
|
||||
return book, err
|
||||
|
||||
@@ -379,7 +379,7 @@ func (b *BTSE) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType a
|
||||
Amount: a.SellQuote[x].Size,
|
||||
})
|
||||
}
|
||||
book.Asks.Reverse() // Reverse asks for correct alignment
|
||||
book.Asks.SortAsks() // Sort asks for correct alignment
|
||||
book.Pair = p
|
||||
book.Exchange = b.Name
|
||||
book.Asset = assetType
|
||||
|
||||
@@ -370,11 +370,13 @@ type orderbookResponse struct {
|
||||
Error
|
||||
}
|
||||
|
||||
// DepositWalletInfo stores wallet deposit info
|
||||
type DepositWalletInfo struct {
|
||||
Coin string `json:"coin"`
|
||||
Chains []ChainInfo `json:"chains"`
|
||||
}
|
||||
|
||||
// ChainInfo stores a coins chain info
|
||||
type ChainInfo struct {
|
||||
ChainType string `json:"chain_type"`
|
||||
DepositAddress string `json:"address_deposit"`
|
||||
|
||||
@@ -497,5 +497,5 @@ func (i *instrumentMap) GetInstrumentIDs() []int64 {
|
||||
}
|
||||
|
||||
func getNonce() int64 {
|
||||
return rand.Int63n(coinutMaxNonce-1) + 1 // nolint:gosec // basic number generation required, no need for crypo/rand
|
||||
return rand.Int63n(coinutMaxNonce-1) + 1 //nolint:gosec // basic number generation required, no need for crypo/rand
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
warningBase64DecryptSecretKeyFailed = "exchange %s unable to base64 decode secret key.. Disabling Authenticated API support" // nolint // False positive (G101: Potential hardcoded credentials)
|
||||
warningBase64DecryptSecretKeyFailed = "exchange %s unable to base64 decode secret key.. Disabling Authenticated API support" //nolint // False positive (G101: Potential hardcoded credentials)
|
||||
// DefaultHTTPTimeout is the default HTTP/HTTPS Timeout for exchange requests
|
||||
DefaultHTTPTimeout = time.Second * 15
|
||||
// DefaultWebsocketResponseCheckTimeout is the default delay in checking for an expected websocket response
|
||||
@@ -330,7 +330,7 @@ func (b *Base) GetPairFormat(assetType asset.Item, requestFormat bool) (currency
|
||||
func (b *Base) GetEnabledPairs(a asset.Item) (currency.Pairs, error) {
|
||||
err := b.CurrencyPairs.IsAssetEnabled(a)
|
||||
if err != nil {
|
||||
return nil, nil // nolint:nilerr // non-fatal error
|
||||
return nil, nil //nolint:nilerr // non-fatal error
|
||||
}
|
||||
format, err := b.GetPairFormat(a, false)
|
||||
if err != nil {
|
||||
@@ -1117,7 +1117,7 @@ func (e *Endpoints) SetRunning(key, val string) error {
|
||||
key,
|
||||
val,
|
||||
e.Exchange)
|
||||
return nil // nolint:nilerr // non-fatal error as we won't update the running URL
|
||||
return nil //nolint:nilerr // non-fatal error as we won't update the running URL
|
||||
}
|
||||
e.defaults[key] = val
|
||||
return nil
|
||||
|
||||
@@ -133,11 +133,17 @@ func (g *Gemini) GetAuction(ctx context.Context, currencyPair string) (Auction,
|
||||
//
|
||||
// currencyPair - example "btcusd"
|
||||
// params -- [optional]
|
||||
// since - [timestamp] Only returns auction events after the specified
|
||||
//
|
||||
// since - [timestamp] Only returns auction events after the specified
|
||||
//
|
||||
// timestamp.
|
||||
// limit_auction_results - [integer] The maximum number of auction
|
||||
//
|
||||
// limit_auction_results - [integer] The maximum number of auction
|
||||
//
|
||||
// events to return.
|
||||
// include_indicative - [bool] Whether to include publication of
|
||||
//
|
||||
// include_indicative - [bool] Whether to include publication of
|
||||
//
|
||||
// indicative prices and quantities.
|
||||
func (g *Gemini) GetAuctionHistory(ctx context.Context, currencyPair string, params url.Values) ([]AuctionHistory, error) {
|
||||
path := common.EncodeURLValues(fmt.Sprintf("/v%s/%s/%s/%s", geminiAPIVersion, geminiAuction, currencyPair, geminiAuctionHistory), params)
|
||||
|
||||
@@ -42,7 +42,7 @@ const (
|
||||
huobiBasisData = "/index/market/history/swap_basis"
|
||||
huobiSwapAccInfo = "/swap-api/v1/swap_account_info"
|
||||
huobiSwapPosInfo = "/swap-api/v1/swap_position_info"
|
||||
huobiSwapAssetsAndPos = "/swap-api/v1/swap_account_position_info" // nolint // false positive gosec
|
||||
huobiSwapAssetsAndPos = "/swap-api/v1/swap_account_position_info" //nolint // false positive gosec
|
||||
huobiSwapSubAccList = "/swap-api/v1/swap_sub_account_list"
|
||||
huobiSwapSubAccInfo = "/swap-api/v1/swap_sub_account_info"
|
||||
huobiSwapSubAccPosInfo = "/swap-api/v1/swap_sub_position_info"
|
||||
|
||||
@@ -75,8 +75,9 @@ func (i *ItBit) GetTradeHistory(ctx context.Context, currencyPair, tradeID strin
|
||||
// GetWallets returns information about all wallets associated with the account.
|
||||
//
|
||||
// params --
|
||||
// page - [optional] page to return example 1. default 1
|
||||
// perPage - [optional] items per page example 50, default 50 max 50
|
||||
//
|
||||
// page - [optional] page to return example 1. default 1
|
||||
// perPage - [optional] items per page example 50, default 50 max 50
|
||||
func (i *ItBit) GetWallets(ctx context.Context, params url.Values) ([]Wallet, error) {
|
||||
creds, err := i.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,7 +3,6 @@ package kline
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -106,11 +105,11 @@ func TestCreateKline(t *testing.T) {
|
||||
rand.Seed(time.Now().Unix())
|
||||
for i := 0; i < 24000; i++ {
|
||||
trades = append(trades, order.TradeHistory{
|
||||
Timestamp: time.Now().Add((time.Duration(rand.Intn(10)) * time.Minute) + // nolint:gosec // no need to import crypo/rand for testing
|
||||
(time.Duration(rand.Intn(10)) * time.Second)), // nolint:gosec // no need to import crypo/rand for testing
|
||||
Timestamp: time.Now().Add((time.Duration(rand.Intn(10)) * time.Minute) + //nolint:gosec // no need to import crypo/rand for testing
|
||||
(time.Duration(rand.Intn(10)) * time.Second)), //nolint:gosec // no need to import crypo/rand for testing
|
||||
TID: crypto.HexEncodeToString([]byte(string(rune(i)))),
|
||||
Amount: float64(rand.Intn(20)) + 1, // nolint:gosec // no need to import crypo/rand for testing
|
||||
Price: 1000 + float64(rand.Intn(1000)), // nolint:gosec // no need to import crypo/rand for testing
|
||||
Amount: float64(rand.Intn(20)) + 1, //nolint:gosec // no need to import crypo/rand for testing
|
||||
Price: 1000 + float64(rand.Intn(1000)), //nolint:gosec // no need to import crypo/rand for testing
|
||||
})
|
||||
}
|
||||
|
||||
@@ -461,7 +460,7 @@ func TestItem_SortCandlesByTimestamp(t *testing.T) {
|
||||
}
|
||||
|
||||
for x := 0; x < 100; x++ {
|
||||
y := rand.Float64() // nolint:gosec // used for generating test data, no need to import crypo/rand
|
||||
y := rand.Float64() //nolint:gosec // used for generating test data, no need to import crypo/rand
|
||||
tempKline.Candles = append(tempKline.Candles,
|
||||
Candle{
|
||||
Time: time.Now().AddDate(0, 0, -x),
|
||||
@@ -497,7 +496,7 @@ func setupTest(t *testing.T) {
|
||||
var err error
|
||||
testhelpers.MigrationDir = filepath.Join("..", "..", "database", "migrations")
|
||||
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
|
||||
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
|
||||
testhelpers.TempDir, err = os.MkdirTemp("", "gct-temp")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp file: %v", err)
|
||||
}
|
||||
@@ -857,7 +856,7 @@ func BenchmarkJustifyIntervalTimeStoringUnixValues1(b *testing.B) {
|
||||
tt2 := time.Now().Add(-time.Hour)
|
||||
tt3 := time.Now().Add(time.Hour)
|
||||
for i := 0; i < b.N; i++ {
|
||||
if tt1.Unix() == tt2.Unix() || // nolint:staticcheck // it is a benchmark to demonstrate inefficiency in calling
|
||||
if tt1.Unix() == tt2.Unix() || //nolint:staticcheck // it is a benchmark to demonstrate inefficiency in calling
|
||||
(tt1.Unix() > tt2.Unix() && tt1.Unix() < tt3.Unix()) {
|
||||
|
||||
}
|
||||
@@ -873,7 +872,7 @@ func BenchmarkJustifyIntervalTimeStoringUnixValues2(b *testing.B) {
|
||||
tt2 := time.Now().Add(-time.Hour).Unix()
|
||||
tt3 := time.Now().Add(time.Hour).Unix()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if tt1 >= tt2 && tt1 <= tt3 { // nolint:staticcheck // it is a benchmark to demonstrate inefficiency in calling
|
||||
if tt1 >= tt2 && tt1 <= tt3 { //nolint:staticcheck // it is a benchmark to demonstrate inefficiency in calling
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ func (o *OHLC) GetSimpleMovingAverage(option []float64, period int64) ([]float64
|
||||
}
|
||||
|
||||
// GetExponentialMovingAverageOnClose returns the EMA on the close price set for
|
||||
// the given period.
|
||||
// the given period.
|
||||
func (k *Item) GetExponentialMovingAverageOnClose(period int64) ([]float64, error) {
|
||||
ohlc := k.GetOHLC()
|
||||
return ohlc.GetExponentialMovingAverage(ohlc.Close, period)
|
||||
|
||||
@@ -977,11 +977,12 @@ func (k *Kraken) CancelExistingOrder(ctx context.Context, txid string) (CancelOr
|
||||
return response.Result, GetError(response.Error)
|
||||
}
|
||||
|
||||
// GetError parse Exchange errors in response and return the first one
|
||||
// GetError parse Exchange errors in response and return the first one.
|
||||
//
|
||||
// Error format from API doc:
|
||||
// error = array of error messages in the format of:
|
||||
// <char-severity code><string-error category>:<string-error type>[:<string-extra info>]
|
||||
// severity code can be E for error or W for warning
|
||||
// - error = array of error messages in the format of:
|
||||
// <char-severity code><string-error category>:<string-error type>[:<string-extra info>]
|
||||
// severity code can be E for error or W for warning
|
||||
func GetError(apiErrors []string) error {
|
||||
const exchangeName = "Kraken"
|
||||
for _, e := range apiErrors {
|
||||
|
||||
@@ -475,6 +475,7 @@ func TestGetHistoricCandlesExtended(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_FormatExchangeKlineInterval(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := []struct {
|
||||
name string
|
||||
interval kline.Interval
|
||||
@@ -511,6 +512,7 @@ func Test_FormatExchangeKlineInterval(t *testing.T) {
|
||||
test := testCases[x]
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ret := l.FormatExchangeKlineInterval(test.interval)
|
||||
|
||||
if ret != test.output {
|
||||
|
||||
@@ -404,7 +404,9 @@ var filterOrdersByTypeBenchmark = &[]Detail{
|
||||
{Type: Limit},
|
||||
}
|
||||
|
||||
// 392455 3226 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// BenchmarkFilterOrdersByType benchmark
|
||||
//
|
||||
// 392455 3226 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// 9486490 109.5 ns/op 0 B/op 0 allocs/op // CURRENT
|
||||
func BenchmarkFilterOrdersByType(b *testing.B) {
|
||||
for x := 0; x < b.N; x++ {
|
||||
@@ -454,7 +456,9 @@ var filterOrdersBySideBenchmark = &[]Detail{
|
||||
{Side: Ask},
|
||||
}
|
||||
|
||||
// 372594 3049 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// BenchmarkFilterOrdersBySide benchmark
|
||||
//
|
||||
// 372594 3049 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// 7412187 148.8 ns/op 0 B/op 0 allocs/op // CURRENT
|
||||
func BenchmarkFilterOrdersBySide(b *testing.B) {
|
||||
for x := 0; x < b.N; x++ {
|
||||
@@ -537,7 +541,9 @@ var filterOrdersByTimeRangeBenchmark = &[]Detail{
|
||||
{Date: time.Unix(100, 0)},
|
||||
}
|
||||
|
||||
// 390822 3335 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// BenchmarkFilterOrdersByTimeRange benchmark
|
||||
//
|
||||
// 390822 3335 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// 6201034 172.1 ns/op 0 B/op 0 allocs/op // CURRENT
|
||||
func BenchmarkFilterOrdersByTimeRange(b *testing.B) {
|
||||
for x := 0; x < b.N; x++ {
|
||||
@@ -615,7 +621,9 @@ var filterOrdersByPairsBenchmark = &[]Detail{
|
||||
{Pair: currency.NewPair(currency.BTC, currency.USD)},
|
||||
}
|
||||
|
||||
// 400032 2977 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// BenchmarkFilterOrdersByPairs benchmark
|
||||
//
|
||||
// 400032 2977 ns/op 15840 B/op 5 allocs/op // PREV
|
||||
// 6977242 172.8 ns/op 0 B/op 0 allocs/op // CURRENT
|
||||
func BenchmarkFilterOrdersByPairs(b *testing.B) {
|
||||
pairs := []currency.Pair{currency.NewPair(currency.BTC, currency.USD)}
|
||||
|
||||
@@ -622,7 +622,7 @@ func (t Type) Lower() string {
|
||||
|
||||
// Title returns the type titleized, eg "Limit"
|
||||
func (t Type) Title() string {
|
||||
return strings.Title(strings.ToLower(t.String())) // nolint:staticcheck // Ignore Title usage warning
|
||||
return strings.Title(strings.ToLower(t.String())) //nolint:staticcheck // Ignore Title usage warning
|
||||
}
|
||||
|
||||
// String implements the stringer interface
|
||||
@@ -675,7 +675,7 @@ func (s Side) Lower() string {
|
||||
|
||||
// Title returns the side titleized, eg "Buy"
|
||||
func (s Side) Title() string {
|
||||
return strings.Title(strings.ToLower(s.String())) // nolint:staticcheck // Ignore Title usage warning
|
||||
return strings.Title(strings.ToLower(s.String())) //nolint:staticcheck // Ignore Title usage warning
|
||||
}
|
||||
|
||||
// IsShort returns if the side is short
|
||||
|
||||
@@ -62,7 +62,7 @@ func (s *stack) Display() {
|
||||
fmt.Println("TOTAL COUNT:", s.getCount())
|
||||
}
|
||||
|
||||
// 158 9,521,717 ns/op 9600104 B/op 100001 allocs/op
|
||||
// 158 9,521,717 ns/op 9600104 B/op 100001 allocs/op
|
||||
func BenchmarkWithoutStack(b *testing.B) {
|
||||
var n *Node
|
||||
b.ReportAllocs()
|
||||
|
||||
@@ -492,12 +492,12 @@ func TestProcessOrderbook(t *testing.T) {
|
||||
m.Unlock()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
newName := "Exchange" + strconv.FormatInt(rand.Int63(), 10) // nolint:gosec // no need to import crypo/rand for testing
|
||||
newName := "Exchange" + strconv.FormatInt(rand.Int63(), 10) //nolint:gosec // no need to import crypo/rand for testing
|
||||
newPairs := currency.NewPair(currency.NewCode("BTC"+strconv.FormatInt(rand.Int63(), 10)),
|
||||
currency.NewCode("USD"+strconv.FormatInt(rand.Int63(), 10))) // nolint:gosec // no need to import crypo/rand for testing
|
||||
currency.NewCode("USD"+strconv.FormatInt(rand.Int63(), 10))) //nolint:gosec // no need to import crypo/rand for testing
|
||||
|
||||
asks := []Item{{Price: rand.Float64(), Amount: rand.Float64()}} // nolint:gosec // no need to import crypo/rand for testing
|
||||
bids := []Item{{Price: rand.Float64(), Amount: rand.Float64()}} // nolint:gosec // no need to import crypo/rand for testing
|
||||
asks := []Item{{Price: rand.Float64(), Amount: rand.Float64()}} //nolint:gosec // no need to import crypo/rand for testing
|
||||
bids := []Item{{Price: rand.Float64(), Amount: rand.Float64()}} //nolint:gosec // no need to import crypo/rand for testing
|
||||
base := &Base{
|
||||
Pair: newPairs,
|
||||
Asks: asks,
|
||||
@@ -558,7 +558,7 @@ func deployUnorderedSlice() Items {
|
||||
var items []Item
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
for i := 0; i < 1000; i++ {
|
||||
items = append(items, Item{Amount: 1, Price: rand.Float64(), ID: rand.Int63()}) // nolint:gosec // Not needed in tests
|
||||
items = append(items, Item{Amount: 1, Price: rand.Float64(), ID: rand.Int63()}) //nolint:gosec // Not needed in tests
|
||||
}
|
||||
return items
|
||||
}
|
||||
@@ -596,7 +596,7 @@ func deploySliceOrdered() Items {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
var items []Item
|
||||
for i := 0; i < 1000; i++ {
|
||||
items = append(items, Item{Amount: 1, Price: float64(i + 1), ID: rand.Int63()}) // nolint:gosec // Not needed in tests
|
||||
items = append(items, Item{Amount: 1, Price: float64(i + 1), ID: rand.Int63()}) //nolint:gosec // Not needed in tests
|
||||
}
|
||||
return items
|
||||
}
|
||||
@@ -620,7 +620,7 @@ func TestReverse(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b.Asks = append(b.Bids[:0:0], b.Bids...) // nolint:gocritic // Short hand
|
||||
b.Asks = append(b.Bids[:0:0], b.Bids...) //nolint:gocritic // Short hand
|
||||
err = b.Verify()
|
||||
if !errors.Is(err, errPriceOutOfOrder) {
|
||||
t.Fatalf("error expected %v received %v", errPriceOutOfOrder, err)
|
||||
@@ -645,11 +645,11 @@ func BenchmarkReverse(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// 20209 56385 ns/op 49189 B/op 2 allocs/op
|
||||
// 20209 56385 ns/op 49189 B/op 2 allocs/op
|
||||
func BenchmarkSortAsksDecending(b *testing.B) {
|
||||
s := deploySliceOrdered()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
ts := append(s[:0:0], s...)
|
||||
ts.SortAsks()
|
||||
}
|
||||
@@ -660,7 +660,7 @@ func BenchmarkSortBidsAscending(b *testing.B) {
|
||||
s := deploySliceOrdered()
|
||||
s.Reverse()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
ts := append(s[:0:0], s...)
|
||||
ts.SortBids()
|
||||
}
|
||||
@@ -670,7 +670,7 @@ func BenchmarkSortBidsAscending(b *testing.B) {
|
||||
func BenchmarkSortAsksStandard(b *testing.B) {
|
||||
s := deployUnorderedSlice()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
ts := append(s[:0:0], s...)
|
||||
ts.SortAsks()
|
||||
}
|
||||
@@ -680,7 +680,7 @@ func BenchmarkSortAsksStandard(b *testing.B) {
|
||||
func BenchmarkSortBidsStandard(b *testing.B) {
|
||||
s := deployUnorderedSlice()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
ts := append(s[:0:0], s...)
|
||||
ts.SortBids()
|
||||
}
|
||||
@@ -690,7 +690,7 @@ func BenchmarkSortBidsStandard(b *testing.B) {
|
||||
func BenchmarkSortAsksAscending(b *testing.B) {
|
||||
s := deploySliceOrdered()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
ts := append(s[:0:0], s...)
|
||||
ts.SortAsks()
|
||||
}
|
||||
@@ -701,7 +701,7 @@ func BenchmarkSortBidsDescending(b *testing.B) {
|
||||
s := deploySliceOrdered()
|
||||
s.Reverse()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
ts := append(s[:0:0], s...)
|
||||
ts.SortBids()
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestUnsafe(t *testing.T) {
|
||||
|
||||
ob2 := &externalBook{}
|
||||
ob.Lock()
|
||||
ob.Unlock() // nolint:staticcheck, gocritic // Not needed in test
|
||||
ob.Unlock() //nolint:staticcheck, gocritic // Not needed in test
|
||||
ob.LockWith(ob2)
|
||||
ob.UnlockWith(ob2)
|
||||
}
|
||||
|
||||
@@ -20,268 +20,335 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
// CustomEx creates a mock custom exchange
|
||||
type CustomEx struct {
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
// Setup is a mock method for CustomEx
|
||||
func (c *CustomEx) Setup(exch *config.Exchange) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start is a mock method for CustomEx
|
||||
func (c *CustomEx) Start(wg *sync.WaitGroup) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefaults is a mock method for CustomEx
|
||||
func (c *CustomEx) SetDefaults() {
|
||||
}
|
||||
|
||||
// GetName is a mock method for CustomEx
|
||||
func (c *CustomEx) GetName() string {
|
||||
return "customex"
|
||||
}
|
||||
|
||||
// IsEnabled is a mock method for CustomEx
|
||||
func (c *CustomEx) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetEnabled is a mock method for CustomEx
|
||||
func (c *CustomEx) SetEnabled(bool) {
|
||||
}
|
||||
|
||||
// ValidateCredentials is a mock method for CustomEx
|
||||
func (c *CustomEx) ValidateCredentials(ctx context.Context, a asset.Item) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchTicker is a mock method for CustomEx
|
||||
func (c *CustomEx) FetchTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdateTickers is a mock method for CustomEx
|
||||
func (c *CustomEx) UpdateTickers(ctx context.Context, a asset.Item) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTicker is a mock method for CustomEx
|
||||
func (c *CustomEx) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// FetchOrderbook is a mock method for CustomEx
|
||||
func (c *CustomEx) FetchOrderbook(ctx context.Context, p currency.Pair, a asset.Item) (*orderbook.Base, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook is a mock method for CustomEx
|
||||
func (c *CustomEx) UpdateOrderbook(ctx context.Context, p currency.Pair, a asset.Item) (*orderbook.Base, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// FetchTradablePairs is a mock method for CustomEx
|
||||
func (c *CustomEx) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdateTradablePairs is a mock method for CustomEx
|
||||
func (c *CustomEx) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetEnabledPairs is a mock method for CustomEx
|
||||
func (c *CustomEx) GetEnabledPairs(a asset.Item) (currency.Pairs, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetAvailablePairs is a mock method for CustomEx
|
||||
func (c *CustomEx) GetAvailablePairs(a asset.Item) (currency.Pairs, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// FetchAccountInfo is a mock method for CustomEx
|
||||
func (c *CustomEx) FetchAccountInfo(ctx context.Context, a asset.Item) (account.Holdings, error) {
|
||||
return account.Holdings{}, nil
|
||||
}
|
||||
|
||||
// UpdateAccountInfo is a mock method for CustomEx
|
||||
func (c *CustomEx) UpdateAccountInfo(ctx context.Context, a asset.Item) (account.Holdings, error) {
|
||||
return account.Holdings{}, nil
|
||||
}
|
||||
|
||||
// SetPairs is a mock method for CustomEx
|
||||
func (c *CustomEx) SetPairs(pairs currency.Pairs, a asset.Item, enabled bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAssetTypes is a mock method for CustomEx
|
||||
func (c *CustomEx) GetAssetTypes(enabled bool) asset.Items {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRecentTrades is a mock method for CustomEx
|
||||
func (c *CustomEx) GetRecentTrades(ctx context.Context, p currency.Pair, a asset.Item) ([]trade.Data, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetHistoricTrades is a mock method for CustomEx
|
||||
func (c *CustomEx) GetHistoricTrades(ctx context.Context, p currency.Pair, a asset.Item, startTime, endTime time.Time) ([]trade.Data, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SupportsAutoPairUpdates is a mock method for CustomEx
|
||||
func (c *CustomEx) SupportsAutoPairUpdates() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SupportsRESTTickerBatchUpdates is a mock method for CustomEx
|
||||
func (c *CustomEx) SupportsRESTTickerBatchUpdates() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetFeeByType is a mock method for CustomEx
|
||||
func (c *CustomEx) GetFeeByType(ctx context.Context, f *exchange.FeeBuilder) (float64, error) {
|
||||
return 0.0, nil
|
||||
}
|
||||
|
||||
// GetLastPairsUpdateTime is a mock method for CustomEx
|
||||
func (c *CustomEx) GetLastPairsUpdateTime() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetWithdrawPermissions is a mock method for CustomEx
|
||||
func (c *CustomEx) GetWithdrawPermissions() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// FormatWithdrawPermissions is a mock method for CustomEx
|
||||
func (c *CustomEx) FormatWithdrawPermissions() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// SupportsWithdrawPermissions is a mock method for CustomEx
|
||||
func (c *CustomEx) SupportsWithdrawPermissions(permissions uint32) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetFundingHistory is a mock method for CustomEx
|
||||
func (c *CustomEx) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SubmitOrder is a mock method for CustomEx
|
||||
func (c *CustomEx) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ModifyOrder is a mock method for CustomEx
|
||||
func (c *CustomEx) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CancelOrder is a mock method for CustomEx
|
||||
func (c *CustomEx) CancelOrder(ctx context.Context, o *order.Cancel) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CancelBatchOrders is a mock method for CustomEx
|
||||
func (c *CustomEx) CancelBatchOrders(ctx context.Context, o []order.Cancel) (order.CancelBatchResponse, error) {
|
||||
return order.CancelBatchResponse{}, nil
|
||||
}
|
||||
|
||||
// CancelAllOrders is a mock method for CustomEx
|
||||
func (c *CustomEx) CancelAllOrders(ctx context.Context, orders *order.Cancel) (order.CancelAllResponse, error) {
|
||||
return order.CancelAllResponse{}, nil
|
||||
}
|
||||
|
||||
// GetOrderInfo is a mock method for CustomEx
|
||||
func (c *CustomEx) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) {
|
||||
return order.Detail{}, nil
|
||||
}
|
||||
|
||||
// GetDepositAddress is a mock method for CustomEx
|
||||
func (c *CustomEx) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, accountID, chain string) (*deposit.Address, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetOrderHistory is a mock method for CustomEx
|
||||
func (c *CustomEx) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory is a mock method for CustomEx
|
||||
func (c *CustomEx) GetWithdrawalsHistory(ctx context.Context, code currency.Code, a asset.Item) ([]exchange.WithdrawalHistory, error) {
|
||||
return []exchange.WithdrawalHistory{}, nil
|
||||
}
|
||||
|
||||
// GetActiveOrders is a mock method for CustomEx
|
||||
func (c *CustomEx) GetActiveOrders(ctx context.Context, getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
return []order.Detail{}, nil
|
||||
}
|
||||
|
||||
// WithdrawCryptocurrencyFunds is a mock method for CustomEx
|
||||
func (c *CustomEx) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// WithdrawFiatFunds is a mock method for CustomEx
|
||||
func (c *CustomEx) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// WithdrawFiatFundsToInternationalBank is a mock method for CustomEx
|
||||
func (c *CustomEx) WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SetHTTPClientUserAgent is a mock method for CustomEx
|
||||
func (c *CustomEx) SetHTTPClientUserAgent(ua string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetHTTPClientUserAgent is a mock method for CustomEx
|
||||
func (c *CustomEx) GetHTTPClientUserAgent() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// SetClientProxyAddress is a mock method for CustomEx
|
||||
func (c *CustomEx) SetClientProxyAddress(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SupportsREST is a mock method for CustomEx
|
||||
func (c *CustomEx) SupportsREST() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetSubscriptions is a mock method for CustomEx
|
||||
func (c *CustomEx) GetSubscriptions() ([]stream.ChannelSubscription, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetDefaultConfig is a mock method for CustomEx
|
||||
func (c *CustomEx) GetDefaultConfig() (*config.Exchange, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetBase is a mock method for CustomEx
|
||||
func (c *CustomEx) GetBase() *exchange.Base {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SupportsAsset is a mock method for CustomEx
|
||||
func (c *CustomEx) SupportsAsset(assetType asset.Item) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetHistoricCandles is a mock method for CustomEx
|
||||
func (c *CustomEx) GetHistoricCandles(ctx context.Context, p currency.Pair, a asset.Item, timeStart, timeEnd time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
return kline.Item{}, nil
|
||||
}
|
||||
|
||||
// GetHistoricCandlesExtended is a mock method for CustomEx
|
||||
func (c *CustomEx) GetHistoricCandlesExtended(ctx context.Context, p currency.Pair, a asset.Item, timeStart, timeEnd time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
return kline.Item{}, nil
|
||||
}
|
||||
|
||||
// DisableRateLimiter is a mock method for CustomEx
|
||||
func (c *CustomEx) DisableRateLimiter() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableRateLimiter is a mock method for CustomEx
|
||||
func (c *CustomEx) EnableRateLimiter() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetWebsocket is a mock method for CustomEx
|
||||
func (c *CustomEx) GetWebsocket() (*stream.Websocket, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// IsWebsocketEnabled is a mock method for CustomEx
|
||||
func (c *CustomEx) IsWebsocketEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SupportsWebsocket is a mock method for CustomEx
|
||||
func (c *CustomEx) SupportsWebsocket() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SubscribeToWebsocketChannels is a mock method for CustomEx
|
||||
func (c *CustomEx) SubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsubscribeToWebsocketChannels is a mock method for CustomEx
|
||||
func (c *CustomEx) UnsubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsAssetWebsocketSupported is a mock method for CustomEx
|
||||
func (c *CustomEx) IsAssetWebsocketSupported(aType asset.Item) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// FlushWebsocketChannels is a mock method for CustomEx
|
||||
func (c *CustomEx) FlushWebsocketChannels() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthenticateWebsocket is a mock method for CustomEx
|
||||
func (c *CustomEx) AuthenticateWebsocket(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOrderExecutionLimits is a mock method for CustomEx
|
||||
func (c *CustomEx) GetOrderExecutionLimits(a asset.Item, cp currency.Pair) (order.MinMaxLevel, error) {
|
||||
return order.MinMaxLevel{}, nil
|
||||
}
|
||||
|
||||
// CheckOrderExecutionLimits is a mock method for CustomEx
|
||||
func (c *CustomEx) CheckOrderExecutionLimits(a asset.Item, cp currency.Pair, price, amount float64, orderType order.Type) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits is a mock method for CustomEx
|
||||
func (c *CustomEx) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -63,12 +63,12 @@ func bidAskGenerator() []orderbook.Item {
|
||||
var response []orderbook.Item
|
||||
randIterator := 100
|
||||
for i := 0; i < randIterator; i++ {
|
||||
price := float64(rand.Intn(1000)) // nolint:gosec // no need to import crypo/rand for testing
|
||||
price := float64(rand.Intn(1000)) //nolint:gosec // no need to import crypo/rand for testing
|
||||
if price == 0 {
|
||||
price = 1
|
||||
}
|
||||
response = append(response, orderbook.Item{
|
||||
Amount: float64(rand.Intn(10)), // nolint:gosec // no need to import crypo/rand for testing
|
||||
Amount: float64(rand.Intn(10)), //nolint:gosec // no need to import crypo/rand for testing
|
||||
Price: price,
|
||||
ID: int64(i),
|
||||
})
|
||||
@@ -134,7 +134,7 @@ func BenchmarkBufferPerformance(b *testing.B) {
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
randomIndex := rand.Intn(4) // nolint:gosec // no need to import crypo/rand for testing
|
||||
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
|
||||
update.Asks = itemArray[randomIndex]
|
||||
update.Bids = itemArray[randomIndex]
|
||||
err = holder.Update(update)
|
||||
@@ -145,7 +145,8 @@ func BenchmarkBufferPerformance(b *testing.B) {
|
||||
}
|
||||
|
||||
// BenchmarkBufferSortingPerformance benchmark
|
||||
// 613964 2093 ns/op 440 B/op 4 allocs/op
|
||||
//
|
||||
// 613964 2093 ns/op 440 B/op 4 allocs/op
|
||||
func BenchmarkBufferSortingPerformance(b *testing.B) {
|
||||
holder, asks, bids, err := createSnapshot()
|
||||
if err != nil {
|
||||
@@ -162,7 +163,7 @@ func BenchmarkBufferSortingPerformance(b *testing.B) {
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
randomIndex := rand.Intn(4) // nolint:gosec // no need to import crypo/rand for testing
|
||||
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
|
||||
update.Asks = itemArray[randomIndex]
|
||||
update.Bids = itemArray[randomIndex]
|
||||
err = holder.Update(update)
|
||||
@@ -191,7 +192,7 @@ func BenchmarkBufferSortingByIDPerformance(b *testing.B) {
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
randomIndex := rand.Intn(4) // nolint:gosec // no need to import crypo/rand for testing
|
||||
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
|
||||
update.Asks = itemArray[randomIndex]
|
||||
update.Bids = itemArray[randomIndex]
|
||||
err = holder.Update(update)
|
||||
@@ -221,7 +222,7 @@ func BenchmarkNoBufferPerformance(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
randomIndex := rand.Intn(4) // nolint:gosec // no need to import crypo/rand for testing
|
||||
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
|
||||
update.Asks = itemArray[randomIndex]
|
||||
update.Bids = itemArray[randomIndex]
|
||||
err = obl.Update(update)
|
||||
@@ -897,7 +898,7 @@ func deploySliceOrdered(size int) orderbook.Items {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
var items []orderbook.Item
|
||||
for i := 0; i < size; i++ {
|
||||
items = append(items, orderbook.Item{Amount: 1, Price: rand.Float64() + float64(i), ID: rand.Int63()}) // nolint:gosec // Not needed for tests
|
||||
items = append(items, orderbook.Item{Amount: 1, Price: rand.Float64() + float64(i), ID: rand.Int63()}) //nolint:gosec // Not needed for tests
|
||||
}
|
||||
return items
|
||||
}
|
||||
@@ -907,7 +908,7 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
holder := orderbookHolder{}
|
||||
|
||||
asks := deploySliceOrdered(100)
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
bids := append(asks[:0:0], asks...)
|
||||
bids.Reverse()
|
||||
|
||||
@@ -1053,7 +1054,7 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
t.Fatal("did not adjust ask item placement and details")
|
||||
}
|
||||
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) // nolint:gocritic
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) //nolint:gocritic
|
||||
// Delete - not found
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.Delete,
|
||||
@@ -1069,7 +1070,7 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errDeleteFailure)
|
||||
}
|
||||
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) // nolint:gocritic
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) //nolint:gocritic
|
||||
// Delete - found
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.Delete,
|
||||
|
||||
@@ -180,7 +180,7 @@ func (s *Service) update(p *Price) error {
|
||||
}
|
||||
|
||||
t.Price = *p
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
ids := append(t.Assoc, t.Main)
|
||||
s.mu.Unlock()
|
||||
return s.mux.Publish(p, ids...)
|
||||
|
||||
@@ -342,17 +342,17 @@ func TestProcessTicker(t *testing.T) { // non-appending function to tickers
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
// nolint:gosec // no need to import crypo/rand for testing
|
||||
//nolint:gosec // no need to import crypo/rand for testing
|
||||
newName := "Exchange" + strconv.FormatInt(rand.Int63(), 10)
|
||||
newPairs, err := currency.NewPairFromStrings("BTC"+strconv.FormatInt(rand.Int63(), 10), // nolint:gosec // no need to import crypo/rand for testing
|
||||
"USD"+strconv.FormatInt(rand.Int63(), 10)) // nolint:gosec // no need to import crypo/rand for testing
|
||||
newPairs, err := currency.NewPairFromStrings("BTC"+strconv.FormatInt(rand.Int63(), 10), //nolint:gosec // no need to import crypo/rand for testing
|
||||
"USD"+strconv.FormatInt(rand.Int63(), 10)) //nolint:gosec // no need to import crypo/rand for testing
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tp := Price{
|
||||
Pair: newPairs,
|
||||
Last: rand.Float64(), // nolint:gosec // no need to import crypo/rand for testing
|
||||
Last: rand.Float64(), //nolint:gosec // no need to import crypo/rand for testing
|
||||
ExchangeName: newName,
|
||||
AssetType: asset.Spot,
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (p *Processor) Run(wg *sync.WaitGroup) {
|
||||
for {
|
||||
<-ticker.C
|
||||
p.mutex.Lock()
|
||||
// nolint: gocritic
|
||||
//nolint: gocritic
|
||||
bufferCopy := append(p.buffer[:0:0], p.buffer...)
|
||||
p.buffer = nil
|
||||
p.mutex.Unlock()
|
||||
|
||||
@@ -23,7 +23,7 @@ var (
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
for x := 0; x < 100; x++ {
|
||||
v := rand.Float64() // nolint:gosec // no need to import crypo/rand for testing
|
||||
v := rand.Float64() //nolint:gosec // no need to import crypo/rand for testing
|
||||
candle := &objects.Array{}
|
||||
candle.Value = append(candle.Value, &objects.Time{Value: time.Now()},
|
||||
&objects.Float{Value: v},
|
||||
|
||||
@@ -25,7 +25,7 @@ const (
|
||||
// Wrapper instance of GCT to use for modules
|
||||
var Wrapper GCTExchange
|
||||
|
||||
// Exchange interface requirements
|
||||
// GCTExchange interface requirements
|
||||
type GCTExchange interface {
|
||||
Exchanges(enabledOnly bool) []string
|
||||
IsEnabled(exch string) bool
|
||||
|
||||
@@ -263,7 +263,7 @@ func (w Wrapper) OHLCV(ctx context.Context, exch string, p currency.Pair, a asse
|
||||
})
|
||||
|
||||
for x := 1; x < 200; x++ {
|
||||
r := validatorLow + rand.Float64()*(validatorHigh-validatorLow) // nolint:gosec // no need to import crypo/rand
|
||||
r := validatorLow + rand.Float64()*(validatorHigh-validatorLow) //nolint:gosec // no need to import crypo/rand
|
||||
candle := kline.Candle{
|
||||
Time: candles[x-1].Time.Add(-i.Duration()),
|
||||
Open: r,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -18,7 +17,7 @@ func TestMain(m *testing.M) {
|
||||
if err != nil {
|
||||
log.Fatal("cannot set up test loggers", err)
|
||||
}
|
||||
tempDir, err := ioutil.TempDir(os.TempDir(), "")
|
||||
tempDir, err := os.MkdirTemp(os.TempDir(), "")
|
||||
if err != nil {
|
||||
log.Fatal("Cannot create temporary file", err)
|
||||
}
|
||||
|
||||
4
testdata/configtest.json
vendored
4
testdata/configtest.json
vendored
@@ -591,8 +591,8 @@
|
||||
],
|
||||
"pairs": {
|
||||
"spot": {
|
||||
"enabled": "BTCKRW,ETHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,QTUMKRW,BTGKRW,EOSKRW",
|
||||
"available": "BHPKRW,STEEMKRW,GTOKRW,ETCKRW,STRATKRW,FXKRW,LTCKRW,MIXKRW,THETAKRW,QTUMKRW,ADAKRW,MCOKRW,INSKRW,RDNKRW,CONKRW,FABKRW,ETHKRW,HDACKRW,BTCKRW,POWRKRW,CMTKRW,LBAKRW,ETHOSKRW,HCKRW,ETZKRW,PPTKRW,XVGKRW,WTCKRW,TMTGKRW,LOOMKRW,WETKRW,ABTKRW,ITCKRW,GXCKRW,ORBSKRW,ICXKRW,BSVKRW,MXCKRW,MITHKRW,AEKRW,SALTKRW,ARNKRW,TRUEKRW,ENJKRW,GNTKRW,PLYKRW,REPKRW,ZRXKRW,BTGKRW,APISKRW,QKCKRW,LRCKRW,DVPKRW,DADKRW,CHRKRW,BCHKRW,NPXSKRW,PIVXKRW,AMOKRW,RNTKRW,XEMKRW,FCTKRW,WOMKRW,WAXPKRW,DACKRW,OMGKRW,PCMKRW,CROKRW,FNBKRW,ANKRKRW,EOSKRW,KNCKRW,OCNKRW,MTLKRW,XSRKRW,VALORKRW,TRVKRW,AUTOKRW,HYCKRW,AOAKRW,BTTKRW,MBLKRW,VETKRW,XRPKRW,ZILKRW,ELFKRW,LAMBKRW,POLYKRW,IOSTKRW,BZNTKRW,CTXCKRW,BATKRW,FZZKRW,PAYKRW,BCDKRW,SNTKRW,WAVESKRW,XLMKRW,LINKKRW,OGOKRW,WICCKRW,TRXKRW"
|
||||
"enabled": "BTCKRW,ETHKRW,ETCKRW,XRPKRW,BCHKRW,QTUMKRW,BTGKRW,EOSKRW",
|
||||
"available": "BHPKRW,STEEMKRW,GTOKRW,ETCKRW,STRATKRW,FXKRW,MIXKRW,THETAKRW,QTUMKRW,ADAKRW,MCOKRW,INSKRW,RDNKRW,CONKRW,FABKRW,ETHKRW,HDACKRW,BTCKRW,POWRKRW,CMTKRW,LBAKRW,ETHOSKRW,HCKRW,ETZKRW,PPTKRW,XVGKRW,WTCKRW,TMTGKRW,LOOMKRW,WETKRW,ABTKRW,ITCKRW,GXCKRW,ORBSKRW,ICXKRW,BSVKRW,MXCKRW,MITHKRW,AEKRW,SALTKRW,ARNKRW,TRUEKRW,ENJKRW,GNTKRW,PLYKRW,REPKRW,ZRXKRW,BTGKRW,APISKRW,QKCKRW,LRCKRW,DVPKRW,DADKRW,CHRKRW,BCHKRW,NPXSKRW,PIVXKRW,AMOKRW,RNTKRW,XEMKRW,FCTKRW,WOMKRW,WAXPKRW,DACKRW,OMGKRW,PCMKRW,CROKRW,FNBKRW,ANKRKRW,EOSKRW,KNCKRW,OCNKRW,MTLKRW,XSRKRW,VALORKRW,TRVKRW,AUTOKRW,HYCKRW,AOAKRW,BTTKRW,MBLKRW,VETKRW,XRPKRW,ZILKRW,ELFKRW,LAMBKRW,POLYKRW,IOSTKRW,BZNTKRW,CTXCKRW,BATKRW,FZZKRW,PAYKRW,BCDKRW,SNTKRW,WAVESKRW,XLMKRW,LINKKRW,OGOKRW,WICCKRW,TRXKRW"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user