├── .gitignore ├── vendor ├── github.com │ ├── gorilla │ │ └── websocket │ │ │ ├── go.sum │ │ │ ├── go.mod │ │ │ ├── AUTHORS │ │ │ ├── trace_17.go │ │ │ ├── .gitignore │ │ │ ├── mask_safe.go │ │ │ ├── conn_write.go │ │ │ ├── client_clone.go │ │ │ ├── trace.go │ │ │ ├── conn_write_legacy.go │ │ │ ├── join.go │ │ │ ├── LICENSE │ │ │ ├── mask.go │ │ │ ├── client_clone_legacy.go │ │ │ ├── json.go │ │ │ ├── proxy.go │ │ │ ├── prepared.go │ │ │ ├── compression.go │ │ │ ├── README.md │ │ │ └── util.go │ ├── avast │ │ └── retry-go │ │ │ ├── VERSION │ │ │ ├── Gopkg.toml │ │ │ ├── .travis.yml │ │ │ ├── .gitignore │ │ │ ├── appveyor.yml │ │ │ ├── LICENSE │ │ │ ├── .godocdown.tmpl │ │ │ ├── Makefile │ │ │ ├── options.go │ │ │ └── retry.go │ ├── robfig │ │ └── cron │ │ │ └── v3 │ │ │ ├── .travis.yml │ │ │ ├── go.mod │ │ │ ├── .gitignore │ │ │ ├── constantdelay.go │ │ │ ├── LICENSE │ │ │ ├── option.go │ │ │ ├── chain.go │ │ │ ├── logger.go │ │ │ ├── spec.go │ │ │ ├── README.md │ │ │ └── doc.go │ ├── akamensky │ │ └── argparse │ │ │ ├── go.mod │ │ │ ├── .travis.yml │ │ │ ├── errors.go │ │ │ ├── .gitignore │ │ │ ├── extras.go │ │ │ ├── LICENSE │ │ │ └── command.go │ ├── shopspring │ │ └── decimal │ │ │ ├── go.mod │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE │ │ │ ├── rounding.go │ │ │ └── README.md │ ├── andres-erbsen │ │ └── clock │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ └── README.md │ ├── pkg │ │ └── errors │ │ │ ├── .travis.yml │ │ │ ├── .gitignore │ │ │ ├── appveyor.yml │ │ │ ├── Makefile │ │ │ ├── LICENSE │ │ │ ├── go113.go │ │ │ ├── README.md │ │ │ ├── stack.go │ │ │ └── errors.go │ └── grishinsana │ │ └── goftx │ │ ├── .gitignore │ │ ├── models │ │ ├── spotmargin.go │ │ ├── subaccounts.go │ │ ├── account.go │ │ ├── types.go │ │ ├── websocket.go │ │ ├── orders.go │ │ └── markets.go │ │ ├── go.mod │ │ ├── utils.go │ │ ├── LICENSE │ │ ├── spotmargin.go │ │ ├── README.md │ │ ├── account.go │ │ ├── go.sum │ │ ├── subaccounts.go │ │ ├── markets.go │ │ ├── client.go │ │ └── orders.go ├── go.uber.org │ └── ratelimit │ │ ├── .gitignore │ │ ├── go.mod │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── go.sum │ │ ├── limiter_mutexbased.go │ │ ├── limiter_atomic.go │ │ └── ratelimit.go └── modules.txt ├── go.mod ├── .vscode └── launch.json ├── logger.go ├── README.md ├── LICENSE ├── main.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | __debug_bin 3 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/VERSION: -------------------------------------------------------------------------------- 1 | 3.0.0 2 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/robfig/cron/v3 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /vendor/github.com/akamensky/argparse/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/akamensky/argparse 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gorilla/websocket 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /vendor/github.com/shopspring/decimal/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/shopspring/decimal 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /vendor/github.com/shopspring/decimal/.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | *.swp 3 | 4 | # IntelliJ 5 | .idea/ 6 | *.iml 7 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /vendor 3 | cover.html 4 | cover.out 5 | 6 | *.swp 7 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/Gopkg.toml: -------------------------------------------------------------------------------- 1 | [[constraint]] 2 | name = "github.com/stretchr/testify" 3 | version = "1.1.4" 4 | -------------------------------------------------------------------------------- /vendor/github.com/andres-erbsen/clock/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.3 4 | - 1.4 5 | - release 6 | - tip 7 | sudo: false 8 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: github.com/pkg/errors 3 | go: 4 | - 1.11.x 5 | - 1.12.x 6 | - 1.13.x 7 | - tip 8 | 9 | script: 10 | - make check 11 | -------------------------------------------------------------------------------- /vendor/github.com/shopspring/decimal/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7.x 5 | - 1.12.x 6 | - 1.13.x 7 | - tip 8 | 9 | install: 10 | - go build . 11 | 12 | script: 13 | - go test -v 14 | -------------------------------------------------------------------------------- /vendor/github.com/akamensky/argparse/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - "1.x" 5 | before_install: 6 | - go get github.com/mattn/goveralls 7 | script: 8 | - go test -v . 9 | - $GOPATH/bin/goveralls -service=travis-ci 10 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/go.mod: -------------------------------------------------------------------------------- 1 | module go.uber.org/ratelimit 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 7 | github.com/stretchr/testify v1.6.1 8 | go.uber.org/atomic v1.7.0 9 | ) 10 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Gorilla WebSocket authors for copyright 2 | # purposes. 3 | # 4 | # Please keep the list sorted. 5 | 6 | Gary Burd 7 | Google LLC (https://opensource.google.com/) 8 | Joachim Bauch 9 | 10 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/trace_17.go: -------------------------------------------------------------------------------- 1 | // +build !go1.8 2 | 3 | package websocket 4 | 5 | import ( 6 | "crypto/tls" 7 | "net/http/httptrace" 8 | ) 9 | 10 | func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { 11 | return doHandshake(tlsConn, cfg) 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8 5 | - 1.9 6 | - "1.10" 7 | - 1.11 8 | - 1.12 9 | - 1.13 10 | - 1.14 11 | - 1.15 12 | 13 | install: 14 | - make setup 15 | 16 | script: 17 | - make ci 18 | 19 | after_success: 20 | - bash <(curl -s https://codecov.io/bash) 21 | -------------------------------------------------------------------------------- /vendor/github.com/akamensky/argparse/errors.go: -------------------------------------------------------------------------------- 1 | package argparse 2 | 3 | type subCommandError struct { 4 | error 5 | cmd *Command 6 | } 7 | 8 | func (e subCommandError) Error() string { 9 | return "[sub]Command required" 10 | } 11 | 12 | func newSubCommandError(cmd *Command) error { 13 | return subCommandError{cmd: cmd} 14 | } 15 | -------------------------------------------------------------------------------- /vendor/github.com/akamensky/argparse/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | .idea/ -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/trading-peter/ftx-auto-lend 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/akamensky/argparse v1.2.2 7 | github.com/avast/retry-go v3.0.0+incompatible 8 | github.com/grishinsana/goftx v1.2.0 9 | github.com/robfig/cron/v3 v3.0.0 10 | github.com/shopspring/decimal v1.2.0 11 | go.uber.org/ratelimit v0.2.0 12 | ) 13 | 14 | replace github.com/grishinsana/goftx => ../goftx 15 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | .idea/ 25 | *.iml 26 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.idea 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Dependency directories (remove the comment below to include it) 16 | # vendor/ 17 | 18 | # Enviroment variables 19 | .env -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask_safe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build appengine 6 | 7 | package websocket 8 | 9 | func maskBytes(key [4]byte, pos int, b []byte) int { 10 | for i := range b { 11 | b[i] ^= key[pos&3] 12 | pos++ 13 | } 14 | return pos & 3 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_write.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.8 6 | 7 | package websocket 8 | 9 | import "net" 10 | 11 | func (c *Conn) writeBufs(bufs ...[]byte) error { 12 | b := net.Buffers(bufs) 13 | _, err := b.WriteTo(c.conn) 14 | return err 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | # dep 17 | vendor/ 18 | Gopkg.lock 19 | 20 | # cover 21 | coverage.txt 22 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 12 | if cfg == nil { 13 | return &tls.Config{} 14 | } 15 | return cfg.Clone() 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/trace.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package websocket 4 | 5 | import ( 6 | "crypto/tls" 7 | "net/http/httptrace" 8 | ) 9 | 10 | func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { 11 | if trace.TLSHandshakeStart != nil { 12 | trace.TLSHandshakeStart() 13 | } 14 | err := doHandshake(tlsConn, cfg) 15 | if trace.TLSHandshakeDone != nil { 16 | trace.TLSHandshakeDone(tlsConn.ConnectionState(), err) 17 | } 18 | return err 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | clone_folder: c:\Users\appveyor\go\src\github.com\avast\retry-go 4 | 5 | #os: Windows Server 2012 R2 6 | platform: x64 7 | 8 | install: 9 | - copy c:\MinGW\bin\mingw32-make.exe c:\MinGW\bin\make.exe 10 | - set GOPATH=C:\Users\appveyor\go 11 | - set PATH=%PATH%;c:\MinGW\bin 12 | - set PATH=%PATH%;%GOPATH%\bin;c:\go\bin 13 | - set GOBIN=%GOPATH%\bin 14 | - go version 15 | - go env 16 | - make setup 17 | 18 | build_script: 19 | - make ci 20 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_write_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !go1.8 6 | 7 | package websocket 8 | 9 | func (c *Conn) writeBufs(bufs ...[]byte) error { 10 | for _, buf := range bufs { 11 | if len(buf) > 0 { 12 | if _, err := c.conn.Write(buf); err != nil { 13 | return err 14 | } 15 | } 16 | } 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/models/spotmargin.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "github.com/shopspring/decimal" 4 | 5 | type LendingInfo struct { 6 | Coin string `json:"coin"` 7 | Lendable decimal.Decimal `json:"lendable"` 8 | Locked decimal.Decimal `json:"locked"` 9 | MinRate decimal.Decimal `json:"minRate"` 10 | Offered decimal.Decimal `json:"offered"` 11 | } 12 | 13 | type LendingRate struct { 14 | Coin string `json:"coin"` 15 | Estimate decimal.Decimal `json:"estimate"` 16 | Previous decimal.Decimal `json:"previous"` 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "debug", 12 | "program": "${workspaceFolder}", 13 | "args": [ 14 | "-k", "xxx", 15 | "-s", "xxx", 16 | "-a", "xxx" 17 | ] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "os" 7 | ) 8 | 9 | var ( 10 | Trace *log.Logger 11 | Info *log.Logger 12 | Warning *log.Logger 13 | Error *log.Logger 14 | ) 15 | 16 | func init() { 17 | Trace = log.New(ioutil.Discard, 18 | "TRACE: ", 19 | log.LUTC|log.Ldate|log.Ltime|log.Lshortfile) 20 | 21 | Info = log.New(os.Stdout, 22 | "INFO: ", 23 | log.LUTC|log.Ldate|log.Ltime|log.Lshortfile) 24 | 25 | Warning = log.New(os.Stdout, 26 | "WARNING: ", 27 | log.LUTC|log.Ldate|log.Ltime|log.Lshortfile) 28 | 29 | Error = log.New(os.Stderr, 30 | "ERROR: ", 31 | log.LUTC|log.Ldate|log.Ltime|log.Lshortfile) 32 | } 33 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/grishinsana/goftx 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/dustin/go-humanize v1.0.0 // indirect 8 | github.com/go-numb/go-ftx v0.0.0-20200829181514-3144aa68f505 // indirect 9 | github.com/google/go-querystring v1.0.0 // indirect 10 | github.com/gorilla/websocket v1.4.2 11 | github.com/joho/godotenv v1.3.0 12 | github.com/json-iterator/go v1.1.10 // indirect 13 | github.com/pkg/errors v0.9.1 14 | github.com/shopspring/decimal v1.2.0 15 | github.com/stretchr/testify v1.6.1 16 | github.com/valyala/fasthttp v1.16.0 // indirect 17 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: build-{build}.{branch} 2 | 3 | clone_folder: C:\gopath\src\github.com\pkg\errors 4 | shallow_clone: true # for startup speed 5 | 6 | environment: 7 | GOPATH: C:\gopath 8 | 9 | platform: 10 | - x64 11 | 12 | # http://www.appveyor.com/docs/installed-software 13 | install: 14 | # some helpful output for debugging builds 15 | - go version 16 | - go env 17 | # pre-installed MinGW at C:\MinGW is 32bit only 18 | # but MSYS2 at C:\msys64 has mingw64 19 | - set PATH=C:\msys64\mingw64\bin;%PATH% 20 | - gcc --version 21 | - g++ --version 22 | 23 | build_script: 24 | - go install -v ./... 25 | 26 | test_script: 27 | - set PATH=C:\gopath\bin;%PATH% 28 | - go test -v ./... 29 | 30 | #artifacts: 31 | # - path: '%GOPATH%\bin\*.exe' 32 | deploy: off 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FTX AUTO LENDING 2 | 3 | This is a CLI tool that allows to automatically compound payouts earned from lending coins. 4 | It will check for newly available funds every hour (5 min after hour elapsed to be precise) and automatically update the lending offer to the max. size that can be lend out on the account. 5 | 6 | **Warning:** 7 | Do note that any coin lent out is removed from your collateral pool, so have a lending only subaccount... Otherwise its very easy to find yourself overleveraged on drawdowns even if margin levels previously looked healthy. 8 | 9 | ## Example 10 | This will compound lending for USD and ETH once per hour (always xx:05:00). You can add more coins by repeating the `--coin [coin name]` parameter. 11 | 12 | ftx-auto-lend-win.exe --key xxxxxxx --secret yyyyyyyy --subaccount mylendingsubacc --coin USD --coin ETH 13 | 14 | -------------------------------------------------------------------------------- /vendor/github.com/akamensky/argparse/extras.go: -------------------------------------------------------------------------------- 1 | package argparse 2 | 3 | import "strings" 4 | 5 | func getLastLine(input string) string { 6 | slice := strings.Split(input, "\n") 7 | return slice[len(slice)-1] 8 | } 9 | 10 | func addToLastLine(base string, add string, width int, padding int, canSplit bool) string { 11 | // If last line has less than 10% space left, do not try to fill in by splitting else just try to split 12 | hasTen := (width - len(getLastLine(base))) > width/10 13 | if len(getLastLine(base)+" "+add) >= width { 14 | if hasTen && canSplit { 15 | adds := strings.Split(add, " ") 16 | for _, v := range adds { 17 | base = addToLastLine(base, v, width, padding, false) 18 | } 19 | return base 20 | } 21 | base = base + "\n" + strings.Repeat(" ", padding) 22 | } 23 | base = base + " " + add 24 | return base 25 | } 26 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/akamensky/argparse v1.2.2 2 | ## explicit 3 | github.com/akamensky/argparse 4 | # github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 5 | github.com/andres-erbsen/clock 6 | # github.com/avast/retry-go v3.0.0+incompatible 7 | ## explicit 8 | github.com/avast/retry-go 9 | # github.com/gorilla/websocket v1.4.2 10 | github.com/gorilla/websocket 11 | # github.com/grishinsana/goftx v1.2.0 => ../goftx 12 | ## explicit 13 | github.com/grishinsana/goftx 14 | github.com/grishinsana/goftx/models 15 | # github.com/pkg/errors v0.9.1 16 | github.com/pkg/errors 17 | # github.com/robfig/cron/v3 v3.0.0 18 | ## explicit 19 | github.com/robfig/cron/v3 20 | # github.com/shopspring/decimal v1.2.0 21 | ## explicit 22 | github.com/shopspring/decimal 23 | # go.uber.org/ratelimit v0.2.0 24 | ## explicit 25 | go.uber.org/ratelimit 26 | # github.com/grishinsana/goftx => ../goftx 27 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## v0.2.0 - 2021-03-02 8 | ### Added 9 | - Allow configuring the limiter with custom slack. 10 | [#64](https://github.com/uber-go/ratelimit/pull/64) 11 | - Allow configuring the limiter per arbitrary time duration. 12 | [#54](https://github.com/uber-go/ratelimit/pull/54) 13 | ### Changed 14 | - Switched from Glide to Go Modules. 15 | ### Fixed 16 | - Fix not working slack. 17 | [#60](https://github.com/uber-go/ratelimit/pull/60) 18 | 19 | ## v0.1.0 20 | ### Fixed 21 | - Changed the import path for `go.uber.org/atomic` to its newer, canonical 22 | import path. 23 | [#18](https://github.com/uber-go/ratelimit/issues/18) 24 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/utils.go: -------------------------------------------------------------------------------- 1 | package goftx 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | func PrepareQueryParams(params interface{}) (map[string]string, error) { 11 | result := make(map[string]string) 12 | 13 | val := reflect.ValueOf(params).Elem() 14 | if val.Kind() != reflect.Struct { 15 | return result, nil 16 | } 17 | 18 | for i := 0; i < val.NumField(); i++ { 19 | valueField := val.Field(i) 20 | typeField := val.Type().Field(i) 21 | tag := typeField.Tag.Get("json") 22 | 23 | switch valueField.Kind() { 24 | case reflect.Ptr: 25 | if valueField.IsNil() { 26 | continue 27 | } 28 | result[tag] = fmt.Sprintf("%v", valueField.Elem().Interface()) 29 | default: 30 | if valueField.IsZero() { 31 | return result, errors.Errorf("required field: %v", tag) 32 | } 33 | result[tag] = fmt.Sprintf("%v", valueField.Interface()) 34 | } 35 | } 36 | 37 | return result, nil 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/shopspring/decimal/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Decimal v1.2.0 2 | 3 | #### BREAKING 4 | - Drop support for Go version older than 1.7 [#172](https://github.com/shopspring/decimal/pull/172) 5 | 6 | #### FEATURES 7 | - Add NewFromInt and NewFromInt32 initializers [#72](https://github.com/shopspring/decimal/pull/72) 8 | - Add support for Go modules [#157](https://github.com/shopspring/decimal/pull/157) 9 | - Add BigInt, BigFloat helper methods [#171](https://github.com/shopspring/decimal/pull/171) 10 | 11 | #### ENHANCEMENTS 12 | - Memory usage optimization [#160](https://github.com/shopspring/decimal/pull/160) 13 | - Updated travis CI golang versions [#156](https://github.com/shopspring/decimal/pull/156) 14 | - Update documentation [#173](https://github.com/shopspring/decimal/pull/173) 15 | - Improve code quality [#174](https://github.com/shopspring/decimal/pull/174) 16 | 17 | #### BUGFIXES 18 | - Revert remove insignificant digits [#159](https://github.com/shopspring/decimal/pull/159) 19 | - Remove 15 interval for RoundCash [#166](https://github.com/shopspring/decimal/pull/166) 20 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/constantdelay.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "time" 4 | 5 | // ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". 6 | // It does not support jobs more frequent than once a second. 7 | type ConstantDelaySchedule struct { 8 | Delay time.Duration 9 | } 10 | 11 | // Every returns a crontab Schedule that activates once every duration. 12 | // Delays of less than a second are not supported (will round up to 1 second). 13 | // Any fields less than a Second are truncated. 14 | func Every(duration time.Duration) ConstantDelaySchedule { 15 | if duration < time.Second { 16 | duration = time.Second 17 | } 18 | return ConstantDelaySchedule{ 19 | Delay: duration - time.Duration(duration.Nanoseconds())%time.Second, 20 | } 21 | } 22 | 23 | // Next returns the next time this should be run. 24 | // This rounds so that the next activation time will be on the second. 25 | func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time { 26 | return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond) 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/Makefile: -------------------------------------------------------------------------------- 1 | PKGS := github.com/pkg/errors 2 | SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS)) 3 | GO := go 4 | 5 | check: test vet gofmt misspell unconvert staticcheck ineffassign unparam 6 | 7 | test: 8 | $(GO) test $(PKGS) 9 | 10 | vet: | test 11 | $(GO) vet $(PKGS) 12 | 13 | staticcheck: 14 | $(GO) get honnef.co/go/tools/cmd/staticcheck 15 | staticcheck -checks all $(PKGS) 16 | 17 | misspell: 18 | $(GO) get github.com/client9/misspell/cmd/misspell 19 | misspell \ 20 | -locale GB \ 21 | -error \ 22 | *.md *.go 23 | 24 | unconvert: 25 | $(GO) get github.com/mdempsky/unconvert 26 | unconvert -v $(PKGS) 27 | 28 | ineffassign: 29 | $(GO) get github.com/gordonklaus/ineffassign 30 | find $(SRCDIRS) -name '*.go' | xargs ineffassign 31 | 32 | pedantic: check errcheck 33 | 34 | unparam: 35 | $(GO) get mvdan.cc/unparam 36 | unparam ./... 37 | 38 | errcheck: 39 | $(GO) get github.com/kisielk/errcheck 40 | errcheck $(PKGS) 41 | 42 | gofmt: 43 | @echo Checking code is gofmted 44 | @test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)" 45 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/join.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "io" 9 | "strings" 10 | ) 11 | 12 | // JoinMessages concatenates received messages to create a single io.Reader. 13 | // The string term is appended to each message. The returned reader does not 14 | // support concurrent calls to the Read method. 15 | func JoinMessages(c *Conn, term string) io.Reader { 16 | return &joinReader{c: c, term: term} 17 | } 18 | 19 | type joinReader struct { 20 | c *Conn 21 | term string 22 | r io.Reader 23 | } 24 | 25 | func (r *joinReader) Read(p []byte) (int, error) { 26 | if r.r == nil { 27 | var err error 28 | _, r.r, err = r.c.NextReader() 29 | if err != nil { 30 | return 0, err 31 | } 32 | if r.term != "" { 33 | r.r = io.MultiReader(r.r, strings.NewReader(r.term)) 34 | } 35 | } 36 | n, err := r.r.Read(p) 37 | if err == io.EOF { 38 | err = nil 39 | r.r = nil 40 | } 41 | return n, err 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Peter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Avast 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 grishinsana 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/akamensky/argparse/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alexey Kamenskiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Uber Technologies, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Rob Figueiredo 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/andres-erbsen/clock/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ben Johnson, Copyright (c) 2015 Yahoo Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/Makefile: -------------------------------------------------------------------------------- 1 | # Directory to put `go install`ed binaries in. 2 | export GOBIN ?= $(shell pwd)/bin 3 | 4 | GO_FILES := $(shell \ 5 | find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ 6 | -o -name '*.go' -print | cut -b3-) 7 | 8 | .PHONY: bench 9 | bench: 10 | go test -bench=. ./... 11 | 12 | bin/golint: tools/go.mod 13 | @cd tools && go install golang.org/x/lint/golint 14 | 15 | bin/staticcheck: tools/go.mod 16 | @cd tools && go install honnef.co/go/tools/cmd/staticcheck 17 | 18 | .PHONY: build 19 | build: 20 | go build ./... 21 | 22 | .PHONY: cover 23 | cover: 24 | go test -coverprofile=cover.out -coverpkg=./... -v ./... 25 | go tool cover -html=cover.out -o cover.html 26 | 27 | .PHONY: gofmt 28 | gofmt: 29 | $(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX)) 30 | @gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true 31 | @[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false) 32 | 33 | .PHONY: golint 34 | golint: bin/golint 35 | @$(GOBIN)/golint -set_exit_status ./... 36 | 37 | .PHONY: lint 38 | lint: gofmt golint staticcheck 39 | 40 | .PHONY: staticcheck 41 | staticcheck: bin/staticcheck 42 | @$(GOBIN)/staticcheck ./... 43 | 44 | .PHONY: test 45 | test: 46 | go test -race ./... 47 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/models/subaccounts.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/shopspring/decimal" 7 | ) 8 | 9 | type SubAccount struct { 10 | Nickname string `json:"nickname"` 11 | Deletable bool `json:"deletable"` 12 | Editable bool `json:"editable"` 13 | Competition bool `json:"competition,omitempty"` 14 | } 15 | 16 | type Balance struct { 17 | Coin string `json:"coin"` 18 | Free decimal.Decimal `json:"free"` 19 | Total decimal.Decimal `json:"total"` 20 | SpotBorrow decimal.Decimal `json:"spotBorrow"` 21 | AvailableWithoutBorrow decimal.Decimal `json:"availableWithoutBorrow"` 22 | } 23 | 24 | type TransferPayload struct { 25 | Coin string `json:"coin"` 26 | Size decimal.Decimal `json:"size"` 27 | Source *string `json:"source"` 28 | Destination *string `json:"destination"` 29 | } 30 | 31 | type TransferResponse struct { 32 | ID int64 `json:"id"` 33 | Coin string `json:"coin"` 34 | Size decimal.Decimal `json:"size"` 35 | Time time.Time `json:"time"` 36 | Notes string `json:"notes"` 37 | Status TransferStatus `json:"status"` 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/option.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Option represents a modification to the default behavior of a Cron. 8 | type Option func(*Cron) 9 | 10 | // WithLocation overrides the timezone of the cron instance. 11 | func WithLocation(loc *time.Location) Option { 12 | return func(c *Cron) { 13 | c.location = loc 14 | } 15 | } 16 | 17 | // WithSeconds overrides the parser used for interpreting job schedules to 18 | // include a seconds field as the first one. 19 | func WithSeconds() Option { 20 | return WithParser(NewParser( 21 | Second | Minute | Hour | Dom | Month | Dow | Descriptor, 22 | )) 23 | } 24 | 25 | // WithParser overrides the parser used for interpreting job schedules. 26 | func WithParser(p Parser) Option { 27 | return func(c *Cron) { 28 | c.parser = p 29 | } 30 | } 31 | 32 | // WithChain specifies Job wrappers to apply to all jobs added to this cron. 33 | // Refer to the Chain* functions in this package for provided wrappers. 34 | func WithChain(wrappers ...JobWrapper) Option { 35 | return func(c *Cron) { 36 | c.chain = NewChain(wrappers...) 37 | } 38 | } 39 | 40 | // WithLogger uses the provided logger. 41 | func WithLogger(logger Logger) Option { 42 | return func(c *Cron) { 43 | c.logger = logger 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build !appengine 6 | 7 | package websocket 8 | 9 | import "unsafe" 10 | 11 | const wordSize = int(unsafe.Sizeof(uintptr(0))) 12 | 13 | func maskBytes(key [4]byte, pos int, b []byte) int { 14 | // Mask one byte at a time for small buffers. 15 | if len(b) < 2*wordSize { 16 | for i := range b { 17 | b[i] ^= key[pos&3] 18 | pos++ 19 | } 20 | return pos & 3 21 | } 22 | 23 | // Mask one byte at a time to word boundary. 24 | if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { 25 | n = wordSize - n 26 | for i := range b[:n] { 27 | b[i] ^= key[pos&3] 28 | pos++ 29 | } 30 | b = b[n:] 31 | } 32 | 33 | // Create aligned word size key. 34 | var k [wordSize]byte 35 | for i := range k { 36 | k[i] = key[(pos+i)&3] 37 | } 38 | kw := *(*uintptr)(unsafe.Pointer(&k)) 39 | 40 | // Mask one word at a time. 41 | n := (len(b) / wordSize) * wordSize 42 | for i := 0; i < n; i += wordSize { 43 | *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw 44 | } 45 | 46 | // Mask one byte at a time for remaining bytes. 47 | b = b[n:] 48 | for i := range b { 49 | b[i] ^= key[pos&3] 50 | pos++ 51 | } 52 | 53 | return pos & 3 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/README.md: -------------------------------------------------------------------------------- 1 | # Go rate limiter [![GoDoc][doc-img]][doc] [![Coverage Status][cov-img]][cov] ![test][test-img] 2 | 3 | This package provides a Golang implementation of the leaky-bucket rate limit algorithm. 4 | This implementation refills the bucket based on the time elapsed between 5 | requests instead of requiring an interval clock to fill the bucket discretely. 6 | 7 | Create a rate limiter with a maximum number of operations to perform per second. 8 | Call Take() before each operation. Take will sleep until you can continue. 9 | 10 | ```go 11 | import ( 12 | "fmt" 13 | "time" 14 | 15 | "go.uber.org/ratelimit" 16 | ) 17 | 18 | func main() { 19 | rl := ratelimit.New(100) // per second 20 | 21 | prev := time.Now() 22 | for i := 0; i < 10; i++ { 23 | now := rl.Take() 24 | fmt.Println(i, now.Sub(prev)) 25 | prev = now 26 | } 27 | 28 | // Output: 29 | // 0 0 30 | // 1 10ms 31 | // 2 10ms 32 | // 3 10ms 33 | // 4 10ms 34 | // 5 10ms 35 | // 6 10ms 36 | // 7 10ms 37 | // 8 10ms 38 | // 9 10ms 39 | } 40 | ``` 41 | 42 | [cov-img]: https://codecov.io/gh/uber-go/ratelimit/branch/master/graph/badge.svg?token=zhLeUjjrm2 43 | [cov]: https://codecov.io/gh/uber-go/ratelimit 44 | [doc-img]: https://pkg.go.dev/badge/go.uber.org/ratelimit 45 | [doc]: https://pkg.go.dev/go.uber.org/ratelimit 46 | [test-img]: https://github.com/uber-go/ratelimit/workflows/test/badge.svg 47 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | // cloneTLSConfig clones all public fields except the fields 12 | // SessionTicketsDisabled and SessionTicketKey. This avoids copying the 13 | // sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a 14 | // config in active use. 15 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 16 | if cfg == nil { 17 | return &tls.Config{} 18 | } 19 | return &tls.Config{ 20 | Rand: cfg.Rand, 21 | Time: cfg.Time, 22 | Certificates: cfg.Certificates, 23 | NameToCertificate: cfg.NameToCertificate, 24 | GetCertificate: cfg.GetCertificate, 25 | RootCAs: cfg.RootCAs, 26 | NextProtos: cfg.NextProtos, 27 | ServerName: cfg.ServerName, 28 | ClientAuth: cfg.ClientAuth, 29 | ClientCAs: cfg.ClientCAs, 30 | InsecureSkipVerify: cfg.InsecureSkipVerify, 31 | CipherSuites: cfg.CipherSuites, 32 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 33 | ClientSessionCache: cfg.ClientSessionCache, 34 | MinVersion: cfg.MinVersion, 35 | MaxVersion: cfg.MaxVersion, 36 | CurvePreferences: cfg.CurvePreferences, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/go113.go: -------------------------------------------------------------------------------- 1 | // +build go1.13 2 | 3 | package errors 4 | 5 | import ( 6 | stderrors "errors" 7 | ) 8 | 9 | // Is reports whether any error in err's chain matches target. 10 | // 11 | // The chain consists of err itself followed by the sequence of errors obtained by 12 | // repeatedly calling Unwrap. 13 | // 14 | // An error is considered to match a target if it is equal to that target or if 15 | // it implements a method Is(error) bool such that Is(target) returns true. 16 | func Is(err, target error) bool { return stderrors.Is(err, target) } 17 | 18 | // As finds the first error in err's chain that matches target, and if so, sets 19 | // target to that error value and returns true. 20 | // 21 | // The chain consists of err itself followed by the sequence of errors obtained by 22 | // repeatedly calling Unwrap. 23 | // 24 | // An error matches target if the error's concrete value is assignable to the value 25 | // pointed to by target, or if the error has a method As(interface{}) bool such that 26 | // As(target) returns true. In the latter case, the As method is responsible for 27 | // setting target. 28 | // 29 | // As will panic if target is not a non-nil pointer to either a type that implements 30 | // error, or to any interface type. As returns false if err is nil. 31 | func As(err error, target interface{}) bool { return stderrors.As(err, target) } 32 | 33 | // Unwrap returns the result of calling the Unwrap method on err, if err's 34 | // type contains an Unwrap method returning error. 35 | // Otherwise, Unwrap returns nil. 36 | func Unwrap(err error) error { 37 | return stderrors.Unwrap(err) 38 | } 39 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/go.sum: -------------------------------------------------------------------------------- 1 | github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= 2 | github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 11 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 13 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 15 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 16 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 17 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "encoding/json" 9 | "io" 10 | ) 11 | 12 | // WriteJSON writes the JSON encoding of v as a message. 13 | // 14 | // Deprecated: Use c.WriteJSON instead. 15 | func WriteJSON(c *Conn, v interface{}) error { 16 | return c.WriteJSON(v) 17 | } 18 | 19 | // WriteJSON writes the JSON encoding of v as a message. 20 | // 21 | // See the documentation for encoding/json Marshal for details about the 22 | // conversion of Go values to JSON. 23 | func (c *Conn) WriteJSON(v interface{}) error { 24 | w, err := c.NextWriter(TextMessage) 25 | if err != nil { 26 | return err 27 | } 28 | err1 := json.NewEncoder(w).Encode(v) 29 | err2 := w.Close() 30 | if err1 != nil { 31 | return err1 32 | } 33 | return err2 34 | } 35 | 36 | // ReadJSON reads the next JSON-encoded message from the connection and stores 37 | // it in the value pointed to by v. 38 | // 39 | // Deprecated: Use c.ReadJSON instead. 40 | func ReadJSON(c *Conn, v interface{}) error { 41 | return c.ReadJSON(v) 42 | } 43 | 44 | // ReadJSON reads the next JSON-encoded message from the connection and stores 45 | // it in the value pointed to by v. 46 | // 47 | // See the documentation for the encoding/json Unmarshal function for details 48 | // about the conversion of JSON to a Go value. 49 | func (c *Conn) ReadJSON(v interface{}) error { 50 | _, r, err := c.NextReader() 51 | if err != nil { 52 | return err 53 | } 54 | err = json.NewDecoder(r).Decode(v) 55 | if err == io.EOF { 56 | // One value is expected in the message. 57 | err = io.ErrUnexpectedEOF 58 | } 59 | return err 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/.godocdown.tmpl: -------------------------------------------------------------------------------- 1 | # {{ .Name }} 2 | 3 | [![Release](https://img.shields.io/github/release/avast/retry-go.svg?style=flat-square)](https://github.com/avast/retry-go/releases/latest) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![Travis](https://img.shields.io/travis/avast/retry-go.svg?style=flat-square)](https://travis-ci.org/avast/retry-go) 6 | [![AppVeyor](https://ci.appveyor.com/api/projects/status/fieg9gon3qlq0a9a?svg=true)](https://ci.appveyor.com/project/JaSei/retry-go) 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/avast/retry-go?style=flat-square)](https://goreportcard.com/report/github.com/avast/retry-go) 8 | [![GoDoc](https://godoc.org/github.com/avast/retry-go?status.svg&style=flat-square)](http://godoc.org/github.com/avast/retry-go) 9 | [![codecov.io](https://codecov.io/github/avast/retry-go/coverage.svg?branch=master)](https://codecov.io/github/avast/retry-go?branch=master) 10 | [![Sourcegraph](https://sourcegraph.com/github.com/avast/retry-go/-/badge.svg)](https://sourcegraph.com/github.com/avast/retry-go?badge) 11 | 12 | {{ .EmitSynopsis }} 13 | 14 | {{ .EmitUsage }} 15 | 16 | ## Contributing 17 | 18 | Contributions are very much welcome. 19 | 20 | ### Makefile 21 | 22 | Makefile provides several handy rules, like README.md `generator` , `setup` for prepare build/dev environment, `test`, `cover`, etc... 23 | 24 | Try `make help` for more information. 25 | 26 | ### Before pull request 27 | 28 | please try: 29 | * run tests (`make test`) 30 | * run linter (`make lint`) 31 | * if your IDE don't automaticaly do `go fmt`, run `go fmt` (`make fmt`) 32 | 33 | ### README 34 | 35 | README.md are generate from template [.godocdown.tmpl](.godocdown.tmpl) and code documentation via [godocdown](https://github.com/robertkrimen/godocdown). 36 | 37 | Never edit README.md direct, because your change will be lost. 38 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/proxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bufio" 9 | "encoding/base64" 10 | "errors" 11 | "net" 12 | "net/http" 13 | "net/url" 14 | "strings" 15 | ) 16 | 17 | type netDialerFunc func(network, addr string) (net.Conn, error) 18 | 19 | func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { 20 | return fn(network, addr) 21 | } 22 | 23 | func init() { 24 | proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { 25 | return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil 26 | }) 27 | } 28 | 29 | type httpProxyDialer struct { 30 | proxyURL *url.URL 31 | forwardDial func(network, addr string) (net.Conn, error) 32 | } 33 | 34 | func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { 35 | hostPort, _ := hostPortNoPort(hpd.proxyURL) 36 | conn, err := hpd.forwardDial(network, hostPort) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | connectHeader := make(http.Header) 42 | if user := hpd.proxyURL.User; user != nil { 43 | proxyUser := user.Username() 44 | if proxyPassword, passwordSet := user.Password(); passwordSet { 45 | credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) 46 | connectHeader.Set("Proxy-Authorization", "Basic "+credential) 47 | } 48 | } 49 | 50 | connectReq := &http.Request{ 51 | Method: "CONNECT", 52 | URL: &url.URL{Opaque: addr}, 53 | Host: addr, 54 | Header: connectHeader, 55 | } 56 | 57 | if err := connectReq.Write(conn); err != nil { 58 | conn.Close() 59 | return nil, err 60 | } 61 | 62 | // Read response. It's OK to use and discard buffered reader here becaue 63 | // the remote server does not speak until spoken to. 64 | br := bufio.NewReader(conn) 65 | resp, err := http.ReadResponse(br, connectReq) 66 | if err != nil { 67 | conn.Close() 68 | return nil, err 69 | } 70 | 71 | if resp.StatusCode != 200 { 72 | conn.Close() 73 | f := strings.SplitN(resp.Status, " ", 2) 74 | return nil, errors.New(f[1]) 75 | } 76 | return conn, nil 77 | } 78 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/models/account.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "github.com/shopspring/decimal" 4 | 5 | type AccountInformation struct { 6 | BackstopProvider bool `json:"backstopProvider"` 7 | Collateral decimal.Decimal `json:"collateral"` 8 | FreeCollateral decimal.Decimal `json:"freeCollateral"` 9 | InitialMarginRequirement decimal.Decimal `json:"initialMarginRequirement"` 10 | Liquidating bool `json:"liquidating"` 11 | MaintenanceMarginRequirement decimal.Decimal `json:"maintenanceMarginRequirement"` 12 | MakerFee decimal.Decimal `json:"makerFee"` 13 | MarginFraction decimal.Decimal `json:"marginFraction"` 14 | OpenMarginFraction decimal.Decimal `json:"openMarginFraction"` 15 | TakerFee decimal.Decimal `json:"takerFee"` 16 | TotalAccountValue decimal.Decimal `json:"totalAccountValue"` 17 | TotalPositionSize decimal.Decimal `json:"totalPositionSize"` 18 | Username string `json:"username"` 19 | Leverage decimal.Decimal `json:"leverage"` 20 | Positions []Position `json:"positions"` 21 | } 22 | 23 | type Position struct { 24 | Cost decimal.Decimal `json:"cost"` 25 | EntryPrice decimal.Decimal `json:"entryPrice"` 26 | EstimatedLiquidationPrice decimal.Decimal `json:"estimatedLiquidationPrice"` 27 | Future string `json:"future"` 28 | InitialMarginRequirement decimal.Decimal `json:"initialMarginRequirement"` 29 | LongOrderSize decimal.Decimal `json:"longOrderSize"` 30 | MaintenanceMarginRequirement decimal.Decimal `json:"maintenanceMarginRequirement"` 31 | NetSize decimal.Decimal `json:"netSize"` 32 | OpenSize decimal.Decimal `json:"openSize"` 33 | RealizedPnl decimal.Decimal `json:"realizedPnl"` 34 | ShortOrderSize decimal.Decimal `json:"shortOrderSize"` 35 | Side string `json:"side"` 36 | Size decimal.Decimal `json:"size"` 37 | UnrealizedPnl decimal.Decimal `json:"unrealizedPnl"` 38 | CollateralUsed decimal.Decimal `json:"collateralUsed"` 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/shopspring/decimal/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Spring, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | - Based on https://github.com/oguzbilgic/fpd, which has the following license: 24 | """ 25 | The MIT License (MIT) 26 | 27 | Copyright (c) 2013 Oguz Bilgic 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of 30 | this software and associated documentation files (the "Software"), to deal in 31 | the Software without restriction, including without limitation the rights to 32 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 33 | the Software, and to permit persons to whom the Software is furnished to do so, 34 | subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all 37 | copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 41 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 42 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 43 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 44 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 45 | """ 46 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/chain.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // JobWrapper decorates the given Job with some behavior. 11 | type JobWrapper func(Job) Job 12 | 13 | // Chain is a sequence of JobWrappers that decorates submitted jobs with 14 | // cross-cutting behaviors like logging or synchronization. 15 | type Chain struct { 16 | wrappers []JobWrapper 17 | } 18 | 19 | // NewChain returns a Chain consisting of the given JobWrappers. 20 | func NewChain(c ...JobWrapper) Chain { 21 | return Chain{c} 22 | } 23 | 24 | // Then decorates the given job with all JobWrappers in the chain. 25 | // 26 | // This: 27 | // NewChain(m1, m2, m3).Then(job) 28 | // is equivalent to: 29 | // m1(m2(m3(job))) 30 | func (c Chain) Then(j Job) Job { 31 | for i := range c.wrappers { 32 | j = c.wrappers[len(c.wrappers)-i-1](j) 33 | } 34 | return j 35 | } 36 | 37 | // Recover panics in wrapped jobs and log them with the provided logger. 38 | func Recover(logger Logger) JobWrapper { 39 | return func(j Job) Job { 40 | return FuncJob(func() { 41 | defer func() { 42 | if r := recover(); r != nil { 43 | const size = 64 << 10 44 | buf := make([]byte, size) 45 | buf = buf[:runtime.Stack(buf, false)] 46 | err, ok := r.(error) 47 | if !ok { 48 | err = fmt.Errorf("%v", r) 49 | } 50 | logger.Error(err, "panic", "stack", "...\n"+string(buf)) 51 | } 52 | }() 53 | j.Run() 54 | }) 55 | } 56 | } 57 | 58 | // DelayIfStillRunning serializes jobs, delaying subsequent runs until the 59 | // previous one is complete. Jobs running after a delay of more than a minute 60 | // have the delay logged at Info. 61 | func DelayIfStillRunning(logger Logger) JobWrapper { 62 | return func(j Job) Job { 63 | var mu sync.Mutex 64 | return FuncJob(func() { 65 | start := time.Now() 66 | mu.Lock() 67 | defer mu.Unlock() 68 | if dur := time.Since(start); dur > time.Minute { 69 | logger.Info("delay", "duration", dur) 70 | } 71 | j.Run() 72 | }) 73 | } 74 | } 75 | 76 | // SkipIfStillRunning skips an invocation of the Job if a previous invocation is 77 | // still running. It logs skips to the given logger at Info level. 78 | func SkipIfStillRunning(logger Logger) JobWrapper { 79 | var ch = make(chan struct{}, 1) 80 | ch <- struct{}{} 81 | return func(j Job) Job { 82 | return FuncJob(func() { 83 | select { 84 | case v := <-ch: 85 | j.Run() 86 | ch <- v 87 | default: 88 | logger.Info("skip") 89 | } 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/models/types.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "time" 7 | ) 8 | 9 | type Resolution int 10 | 11 | const ( 12 | Sec15 = 15 13 | Minute = 60 14 | Minute5 = 300 15 | Minute15 = 900 16 | Hour = 3600 17 | Hour4 = 14400 18 | Day = 86400 19 | ) 20 | 21 | type Channel string 22 | 23 | const ( 24 | OrderBookChannel = Channel("orderbook") 25 | TradesChannel = Channel("trades") 26 | TickerChannel = Channel("ticker") 27 | MarketsChannel = Channel("markets") 28 | FillsChannel = Channel("fills") 29 | OrdersChannel = Channel("orders") 30 | ) 31 | 32 | type Operation string 33 | 34 | const ( 35 | Subscribe = Operation("subscribe") 36 | UnSubscribe = Operation("unsubscribe") 37 | Login = Operation("login") 38 | ) 39 | 40 | type ResponseType string 41 | 42 | const ( 43 | Error = ResponseType("error") 44 | Subscribed = ResponseType("subscribed") 45 | UnSubscribed = ResponseType("unsubscribed") 46 | Info = ResponseType("info") 47 | Partial = ResponseType("partial") 48 | Update = ResponseType("update") 49 | ) 50 | 51 | type TransferStatus string 52 | 53 | const Complete = TransferStatus("complete") 54 | 55 | type OrderType string 56 | 57 | const ( 58 | LimitOrder = OrderType("limit") 59 | MarketOrder = OrderType("market") 60 | ) 61 | 62 | type Side string 63 | 64 | const ( 65 | Sell = Side("sell") 66 | Buy = Side("buy") 67 | ) 68 | 69 | type Status string 70 | 71 | const ( 72 | New = Status("new") 73 | Open = Status("open") 74 | Closed = Status("closed") 75 | ) 76 | 77 | type TriggerOrderType string 78 | 79 | const ( 80 | Stop = TriggerOrderType("stop") 81 | TrailingStop = TriggerOrderType("trailing_stop") 82 | TakeProfit = TriggerOrderType("take_profit") 83 | ) 84 | 85 | type FTXTime struct { 86 | Time time.Time 87 | } 88 | 89 | func (f *FTXTime) UnmarshalJSON(data []byte) error { 90 | var t float64 91 | err := json.Unmarshal(data, &t) 92 | 93 | // FTX uses ISO format sometimes so we have to detect and handle that differently. 94 | if err != nil { 95 | var iso time.Time 96 | errIso := json.Unmarshal(data, &iso) 97 | 98 | if errIso != nil { 99 | return err 100 | } 101 | 102 | f.Time = iso 103 | return nil 104 | } 105 | 106 | sec, nsec := math.Modf(t) 107 | f.Time = time.Unix(int64(sec), int64(nsec)) 108 | return nil 109 | } 110 | 111 | func (f FTXTime) MarshalJSON() ([]byte, error) { 112 | return json.Marshal(float64(f.Time.UnixNano()) / float64(1000000000)) 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE_FILES?=$$(go list ./... | grep -v /vendor/) 2 | TEST_PATTERN?=. 3 | TEST_OPTIONS?= 4 | DEP?=$$(which dep) 5 | VERSION?=$$(cat VERSION) 6 | LINTER?=$$(which golangci-lint) 7 | LINTER_VERSION=1.15.0 8 | 9 | ifeq ($(OS),Windows_NT) 10 | DEP_VERS=dep-windows-amd64 11 | LINTER_FILE=golangci-lint-$(LINTER_VERSION)-windows-amd64.zip 12 | LINTER_UNPACK= >| app.zip; unzip -j app.zip -d $$GOPATH/bin; rm app.zip 13 | else ifeq ($(OS), Darwin) 14 | LINTER_FILE=golangci-lint-$(LINTER_VERSION)-darwin-amd64.tar.gz 15 | LINTER_UNPACK= | tar xzf - -C $$GOPATH/bin --wildcards --strip 1 "**/golangci-lint" 16 | else 17 | DEP_VERS=dep-linux-amd64 18 | LINTER_FILE=golangci-lint-$(LINTER_VERSION)-linux-amd64.tar.gz 19 | LINTER_UNPACK= | tar xzf - -C $$GOPATH/bin --wildcards --strip 1 "**/golangci-lint" 20 | endif 21 | 22 | setup: 23 | go get -u github.com/pierrre/gotestcover 24 | go get -u golang.org/x/tools/cmd/cover 25 | go get -u github.com/robertkrimen/godocdown/godocdown 26 | @if [ "$(LINTER)" = "" ]; then\ 27 | curl -L https://github.com/golangci/golangci-lint/releases/download/v$(LINTER_VERSION)/$(LINTER_FILE) $(LINTER_UNPACK) ;\ 28 | chmod +x $$GOPATH/bin/golangci-lint;\ 29 | fi 30 | @if [ "$(DEP)" = "" ]; then\ 31 | curl -L https://github.com/golang/dep/releases/download/v0.3.1/$(DEP_VERS) >| $$GOPATH/bin/dep;\ 32 | chmod +x $$GOPATH/bin/dep;\ 33 | fi 34 | dep ensure 35 | 36 | generate: ## Generate README.md 37 | godocdown >| README.md 38 | 39 | test: generate test_and_cover_report lint 40 | 41 | test_and_cover_report: 42 | gotestcover $(TEST_OPTIONS) -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m 43 | 44 | cover: test ## Run all the tests and opens the coverage report 45 | go tool cover -html=coverage.txt 46 | 47 | fmt: ## gofmt and goimports all go files 48 | find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done 49 | 50 | lint: ## Run all the linters 51 | golangci-lint run 52 | 53 | ci: test_and_cover_report ## Run all the tests but no linters - use https://golangci.com integration instead 54 | 55 | build: 56 | go build 57 | 58 | release: ## Release new version 59 | git tag | grep -q $(VERSION) && echo This version was released! Increase VERSION! || git tag $(VERSION) && git push origin $(VERSION) && git tag v$(VERSION) && git push origin v$(VERSION) 60 | 61 | # Absolutely awesome: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html 62 | help: 63 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 64 | 65 | .DEFAULT_GOAL := build 66 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/spotmargin.go: -------------------------------------------------------------------------------- 1 | package goftx 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/pkg/errors" 9 | "github.com/shopspring/decimal" 10 | 11 | "github.com/grishinsana/goftx/models" 12 | ) 13 | 14 | const ( 15 | apiGetLendingInfo = "/spot_margin/lending_info" 16 | apiGetLendingRates = "/spot_margin/lending_rates" 17 | apiSubmitLendingOffer = "/spot_margin/offers" 18 | ) 19 | 20 | type SpotMargin struct { 21 | client *Client 22 | } 23 | 24 | func (m *SpotMargin) GetLendingInfo() ([]*models.LendingInfo, error) { 25 | request, err := m.client.prepareRequest(Request{ 26 | Auth: true, 27 | Method: http.MethodGet, 28 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetLendingInfo), 29 | }) 30 | if err != nil { 31 | return nil, errors.WithStack(err) 32 | } 33 | 34 | response, err := m.client.do(request) 35 | if err != nil { 36 | return nil, errors.WithStack(err) 37 | } 38 | 39 | var result []*models.LendingInfo 40 | err = json.Unmarshal(response, &result) 41 | if err != nil { 42 | return nil, errors.WithStack(err) 43 | } 44 | 45 | return result, nil 46 | } 47 | 48 | func (m *SpotMargin) GetLendingRates() ([]*models.LendingRate, error) { 49 | request, err := m.client.prepareRequest(Request{ 50 | Auth: true, 51 | Method: http.MethodGet, 52 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetLendingRates), 53 | }) 54 | if err != nil { 55 | return nil, errors.WithStack(err) 56 | } 57 | 58 | response, err := m.client.do(request) 59 | if err != nil { 60 | return nil, errors.WithStack(err) 61 | } 62 | 63 | var result []*models.LendingRate 64 | err = json.Unmarshal(response, &result) 65 | if err != nil { 66 | return nil, errors.WithStack(err) 67 | } 68 | 69 | return result, nil 70 | } 71 | 72 | func (m *SpotMargin) SubmitLendingOffer(coin string, size decimal.Decimal, rate decimal.Decimal) error { 73 | body, err := json.Marshal(struct { 74 | Coin string `json:"coin"` 75 | Size decimal.Decimal `json:"size"` 76 | Rate decimal.Decimal `json:"rate"` 77 | }{ 78 | Coin: coin, 79 | Size: size, 80 | Rate: rate, 81 | }) 82 | if err != nil { 83 | return errors.WithStack(err) 84 | } 85 | 86 | request, err := m.client.prepareRequest(Request{ 87 | Auth: true, 88 | Method: http.MethodPost, 89 | URL: fmt.Sprintf("%s%s", apiUrl, apiSubmitLendingOffer), 90 | Body: body, 91 | }) 92 | if err != nil { 93 | return errors.WithStack(err) 94 | } 95 | 96 | _, err = m.client.do(request) 97 | if err != nil { 98 | return errors.WithStack(err) 99 | } 100 | 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/README.md: -------------------------------------------------------------------------------- 1 | # goftx 2 | FTX exchange golang library 3 | 4 | ### Install 5 | ```shell script 6 | go get github.com/grishinsana/goftx 7 | ``` 8 | 9 | ### Usage 10 | 11 | > See examples directory and test cases for more examples 12 | 13 | ### TODO 14 | - Private Streams 15 | - Orders 16 | - Futures 17 | - Wallet 18 | - Converts 19 | - Fills 20 | - Funding Payments 21 | - Leveraged Tokens 22 | - Options 23 | - SRM Staking 24 | 25 | #### REST 26 | ```go 27 | package main 28 | 29 | import ( 30 | "fmt" 31 | "net/http" 32 | "time" 33 | 34 | "github.com/grishinsana/goftx" 35 | ) 36 | 37 | func main() { 38 | client := goftx.New( 39 | goftx.WithAuth("API-KEY", "API-SECRET"), 40 | goftx.WithHTTPClient(&http.Client{ 41 | Timeout: 5 * time.Second, 42 | }), 43 | ) 44 | 45 | info, err := client.Account.GetAccountInformation() 46 | if err != nil { 47 | panic(err) 48 | } 49 | fmt.Println(info) 50 | } 51 | ``` 52 | 53 | #### WebSocket 54 | ```go 55 | package main 56 | 57 | import ( 58 | "context" 59 | "log" 60 | "os" 61 | "os/signal" 62 | "syscall" 63 | "time" 64 | 65 | "github.com/grishinsana/goftx" 66 | ) 67 | 68 | func main() { 69 | sigs := make(chan os.Signal, 1) 70 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 71 | 72 | ctx, cancel := context.WithCancel(context.Background()) 73 | 74 | client := goftx.New() 75 | client.Stream.SetDebugMode(true) 76 | 77 | data, err := client.Stream.SubscribeToTickers(ctx, "ETH/BTC") 78 | if err != nil { 79 | log.Fatalf("%+v", err) 80 | } 81 | 82 | go func() { 83 | for { 84 | select { 85 | case <-ctx.Done(): 86 | return 87 | case msg, ok := <-data: 88 | if !ok { 89 | return 90 | } 91 | log.Printf("%+v\n", msg) 92 | } 93 | } 94 | }() 95 | 96 | <-sigs 97 | cancel() 98 | time.Sleep(time.Second) 99 | } 100 | ``` 101 | 102 | ### Websocket Debug Mode 103 | If need, it is possible to set debug mode to look error and system messages in stream methods 104 | ```go 105 | client := goftx.New() 106 | client.Stream.SetDebugMode(true) 107 | ``` 108 | 109 | ### No Logged In Error 110 | "Not logged in" errors usually come from a wrong signatures. FTX released an article on how to authenticate https://blog.ftx.com/blog/api-authentication/ 111 | 112 | If you have unauthorized error to private methods, then you need to use SetServerTimeDiff() 113 | ```go 114 | ftx := New() 115 | ftx.SetServerTimeDiff() 116 | ``` 117 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/README.md: -------------------------------------------------------------------------------- 1 | # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) 2 | 3 | Package errors provides simple error handling primitives. 4 | 5 | `go get github.com/pkg/errors` 6 | 7 | The traditional error handling idiom in Go is roughly akin to 8 | ```go 9 | if err != nil { 10 | return err 11 | } 12 | ``` 13 | which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. 14 | 15 | ## Adding context to an error 16 | 17 | The errors.Wrap function returns a new error that adds context to the original error. For example 18 | ```go 19 | _, err := ioutil.ReadAll(r) 20 | if err != nil { 21 | return errors.Wrap(err, "read failed") 22 | } 23 | ``` 24 | ## Retrieving the cause of an error 25 | 26 | Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. 27 | ```go 28 | type causer interface { 29 | Cause() error 30 | } 31 | ``` 32 | `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: 33 | ```go 34 | switch err := errors.Cause(err).(type) { 35 | case *MyError: 36 | // handle specifically 37 | default: 38 | // unknown error 39 | } 40 | ``` 41 | 42 | [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). 43 | 44 | ## Roadmap 45 | 46 | With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows: 47 | 48 | - 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible) 49 | - 1.0. Final release. 50 | 51 | ## Contributing 52 | 53 | Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports. 54 | 55 | Before sending a PR, please discuss your change by raising an issue. 56 | 57 | ## License 58 | 59 | BSD-2-Clause 60 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/logger.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "os" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // DefaultLogger is used by Cron if none is specified. 12 | var DefaultLogger Logger = PrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags)) 13 | 14 | // DiscardLogger can be used by callers to discard all log messages. 15 | var DiscardLogger Logger = PrintfLogger(log.New(ioutil.Discard, "", 0)) 16 | 17 | // Logger is the interface used in this package for logging, so that any backend 18 | // can be plugged in. It is a subset of the github.com/go-logr/logr interface. 19 | type Logger interface { 20 | // Info logs routine messages about cron's operation. 21 | Info(msg string, keysAndValues ...interface{}) 22 | // Error logs an error condition. 23 | Error(err error, msg string, keysAndValues ...interface{}) 24 | } 25 | 26 | // PrintfLogger wraps a Printf-based logger (such as the standard library "log") 27 | // into an implementation of the Logger interface which logs errors only. 28 | func PrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger { 29 | return printfLogger{l, false} 30 | } 31 | 32 | // VerbosePrintfLogger wraps a Printf-based logger (such as the standard library 33 | // "log") into an implementation of the Logger interface which logs everything. 34 | func VerbosePrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger { 35 | return printfLogger{l, true} 36 | } 37 | 38 | type printfLogger struct { 39 | logger interface{ Printf(string, ...interface{}) } 40 | logInfo bool 41 | } 42 | 43 | func (pl printfLogger) Info(msg string, keysAndValues ...interface{}) { 44 | if pl.logInfo { 45 | keysAndValues = formatTimes(keysAndValues) 46 | pl.logger.Printf( 47 | formatString(len(keysAndValues)), 48 | append([]interface{}{msg}, keysAndValues...)...) 49 | } 50 | } 51 | 52 | func (pl printfLogger) Error(err error, msg string, keysAndValues ...interface{}) { 53 | keysAndValues = formatTimes(keysAndValues) 54 | pl.logger.Printf( 55 | formatString(len(keysAndValues)+2), 56 | append([]interface{}{msg, "error", err}, keysAndValues...)...) 57 | } 58 | 59 | // formatString returns a logfmt-like format string for the number of 60 | // key/values. 61 | func formatString(numKeysAndValues int) string { 62 | var sb strings.Builder 63 | sb.WriteString("%s") 64 | if numKeysAndValues > 0 { 65 | sb.WriteString(", ") 66 | } 67 | for i := 0; i < numKeysAndValues/2; i++ { 68 | if i > 0 { 69 | sb.WriteString(", ") 70 | } 71 | sb.WriteString("%v=%v") 72 | } 73 | return sb.String() 74 | } 75 | 76 | // formatTimes formats any time.Time values as RFC3339. 77 | func formatTimes(keysAndValues []interface{}) []interface{} { 78 | var formattedArgs []interface{} 79 | for _, arg := range keysAndValues { 80 | if t, ok := arg.(time.Time); ok { 81 | arg = t.Format(time.RFC3339) 82 | } 83 | formattedArgs = append(formattedArgs, arg) 84 | } 85 | return formattedArgs 86 | } 87 | -------------------------------------------------------------------------------- /vendor/github.com/andres-erbsen/clock/README.md: -------------------------------------------------------------------------------- 1 | clock [![Build Status](https://travis-ci.org/andres-erbsen/clock.svg)](https://travis-ci.org/andres-erbsen/clock) [![Coverage Status](https://coveralls.io/repos/andres-erbsen/clock/badge.png?branch=master)](https://coveralls.io/r/andres-erbsen/clock?branch=master) [![GoDoc](https://godoc.org/github.com/andres-erbsen/clock?status.png)](https://godoc.org/github.com/andres-erbsen/clock) ![Project status](http://img.shields.io/status/experimental.png?color=red) 2 | ===== 3 | 4 | Clock is a small library for mocking time in Go. It provides an interface 5 | around the standard library's [`time`][time] package so that the application 6 | can use the realtime clock while tests can use the mock clock. 7 | 8 | [time]: http://golang.org/pkg/time/ 9 | 10 | 11 | ## Usage 12 | 13 | ### Realtime Clock 14 | 15 | Your application can maintain a `Clock` variable that will allow realtime and 16 | mock clocks to be interchangable. For example, if you had an `Application` type: 17 | 18 | ```go 19 | import "github.com/andres-erbsen/clock" 20 | 21 | type Application struct { 22 | Clock clock.Clock 23 | } 24 | ``` 25 | 26 | You could initialize it to use the realtime clock like this: 27 | 28 | ```go 29 | var app Application 30 | app.Clock = clock.New() 31 | ... 32 | ``` 33 | 34 | Then all timers and time-related functionality should be performed from the 35 | `Clock` variable. 36 | 37 | 38 | ### Mocking time 39 | 40 | In your tests, you will want to use a `Mock` clock: 41 | 42 | ```go 43 | import ( 44 | "testing" 45 | 46 | "github.com/andres-erbsen/clock" 47 | ) 48 | 49 | func TestApplication_DoSomething(t *testing.T) { 50 | mock := clock.NewMock() 51 | app := Application{Clock: mock} 52 | ... 53 | } 54 | ``` 55 | 56 | Now that you've initialized your application to use the mock clock, you can 57 | adjust the time programmatically. The mock clock always starts from the Unix 58 | epoch (midnight, Jan 1, 1970 UTC). 59 | 60 | 61 | ### Controlling time 62 | 63 | The mock clock provides the same functions that the standard library's `time` 64 | package provides. For example, to find the current time, you use the `Now()` 65 | function: 66 | 67 | ```go 68 | mock := clock.NewMock() 69 | 70 | // Find the current time. 71 | mock.Now().UTC() // 1970-01-01 00:00:00 +0000 UTC 72 | 73 | // Move the clock forward. 74 | mock.Add(2 * time.Hour) 75 | 76 | // Check the time again. It's 2 hours later! 77 | mock.Now().UTC() // 1970-01-01 02:00:00 +0000 UTC 78 | ``` 79 | 80 | Timers and Tickers are also controlled by this same mock clock. They will only 81 | execute when the clock is moved forward: 82 | 83 | ``` 84 | mock := clock.NewMock() 85 | count := 0 86 | 87 | // Kick off a timer to increment every 1 mock second. 88 | go func() { 89 | ticker := clock.Ticker(1 * time.Second) 90 | for { 91 | <-ticker.C 92 | count++ 93 | } 94 | }() 95 | runtime.Gosched() 96 | 97 | // Move the clock forward 10 second. 98 | mock.Add(10 * time.Second) 99 | 100 | // This prints 10. 101 | fmt.Println(count) 102 | ``` 103 | 104 | 105 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/account.go: -------------------------------------------------------------------------------- 1 | package goftx 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/pkg/errors" 9 | "github.com/shopspring/decimal" 10 | 11 | "github.com/grishinsana/goftx/models" 12 | ) 13 | 14 | const ( 15 | apiGetAccountInformation = "/account" 16 | apiGetPositions = "/positions" 17 | apiGetBalances = "/wallet/balances" 18 | apiPostLeverage = "/account/leverage" 19 | ) 20 | 21 | type Account struct { 22 | client *Client 23 | } 24 | 25 | func (a *Account) GetAccountInformation() (*models.AccountInformation, error) { 26 | request, err := a.client.prepareRequest(Request{ 27 | Auth: true, 28 | Method: http.MethodGet, 29 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetAccountInformation), 30 | }) 31 | if err != nil { 32 | return nil, errors.WithStack(err) 33 | } 34 | 35 | response, err := a.client.do(request) 36 | if err != nil { 37 | return nil, errors.WithStack(err) 38 | } 39 | 40 | var result *models.AccountInformation 41 | err = json.Unmarshal(response, &result) 42 | if err != nil { 43 | return nil, errors.WithStack(err) 44 | } 45 | 46 | return result, nil 47 | } 48 | 49 | func (s *Account) GetBalances() ([]*models.Balance, error) { 50 | request, err := s.client.prepareRequest(Request{ 51 | Auth: true, 52 | Method: http.MethodGet, 53 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetBalances), 54 | }) 55 | if err != nil { 56 | return nil, errors.WithStack(err) 57 | } 58 | 59 | response, err := s.client.do(request) 60 | if err != nil { 61 | return nil, errors.WithStack(err) 62 | } 63 | 64 | var result []*models.Balance 65 | err = json.Unmarshal(response, &result) 66 | if err != nil { 67 | return nil, errors.WithStack(err) 68 | } 69 | 70 | return result, nil 71 | } 72 | 73 | func (a *Account) GetPositions() ([]*models.Position, error) { 74 | request, err := a.client.prepareRequest(Request{ 75 | Auth: true, 76 | Method: http.MethodGet, 77 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetPositions), 78 | }) 79 | if err != nil { 80 | return nil, errors.WithStack(err) 81 | } 82 | 83 | response, err := a.client.do(request) 84 | if err != nil { 85 | return nil, errors.WithStack(err) 86 | } 87 | 88 | var result []*models.Position 89 | err = json.Unmarshal(response, &result) 90 | if err != nil { 91 | return nil, errors.WithStack(err) 92 | } 93 | 94 | return result, nil 95 | } 96 | 97 | func (a *Account) ChangeAccountLeverage(leverage decimal.Decimal) error { 98 | body, err := json.Marshal(struct { 99 | Leverage decimal.Decimal `json:"leverage"` 100 | }{Leverage: leverage}) 101 | if err != nil { 102 | return errors.WithStack(err) 103 | } 104 | 105 | request, err := a.client.prepareRequest(Request{ 106 | Auth: true, 107 | Method: http.MethodPost, 108 | URL: fmt.Sprintf("%s%s", apiUrl, apiPostLeverage), 109 | Body: body, 110 | }) 111 | if err != nil { 112 | return errors.WithStack(err) 113 | } 114 | 115 | _, err = a.client.do(request) 116 | if err != nil { 117 | return errors.WithStack(err) 118 | } 119 | 120 | return nil 121 | } 122 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/limiter_mutexbased.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016,2020 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ratelimit // import "go.uber.org/ratelimit" 22 | 23 | import ( 24 | "sync" 25 | "time" 26 | ) 27 | 28 | type mutexLimiter struct { 29 | sync.Mutex 30 | last time.Time 31 | sleepFor time.Duration 32 | perRequest time.Duration 33 | maxSlack time.Duration 34 | clock Clock 35 | } 36 | 37 | // newMutexBased returns a new atomic based limiter. 38 | func newMutexBased(rate int, opts ...Option) *mutexLimiter { 39 | // TODO consider moving config building to the implementation 40 | // independent code. 41 | config := buildConfig(opts) 42 | perRequest := config.per / time.Duration(rate) 43 | l := &mutexLimiter{ 44 | perRequest: perRequest, 45 | maxSlack: -1 * time.Duration(config.slack) * perRequest, 46 | clock: config.clock, 47 | } 48 | return l 49 | } 50 | 51 | // Take blocks to ensure that the time spent between multiple 52 | // Take calls is on average time.Second/rate. 53 | func (t *mutexLimiter) Take() time.Time { 54 | t.Lock() 55 | defer t.Unlock() 56 | 57 | now := t.clock.Now() 58 | 59 | // If this is our first request, then we allow it. 60 | if t.last.IsZero() { 61 | t.last = now 62 | return t.last 63 | } 64 | 65 | // sleepFor calculates how much time we should sleep based on 66 | // the perRequest budget and how long the last request took. 67 | // Since the request may take longer than the budget, this number 68 | // can get negative, and is summed across requests. 69 | t.sleepFor += t.perRequest - now.Sub(t.last) 70 | 71 | // We shouldn't allow sleepFor to get too negative, since it would mean that 72 | // a service that slowed down a lot for a short period of time would get 73 | // a much higher RPS following that. 74 | if t.sleepFor < t.maxSlack { 75 | t.sleepFor = t.maxSlack 76 | } 77 | 78 | // If sleepFor is positive, then we should sleep now. 79 | if t.sleepFor > 0 { 80 | t.clock.Sleep(t.sleepFor) 81 | t.last = now.Add(t.sleepFor) 82 | t.sleepFor = 0 83 | } else { 84 | t.last = now 85 | } 86 | 87 | return t.last 88 | } 89 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/models/websocket.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | type BaseResponse struct { 10 | Type ResponseType 11 | Symbol string 12 | } 13 | 14 | type TickerResponse struct { 15 | Ticker 16 | BaseResponse 17 | } 18 | 19 | type TradesResponse struct { 20 | Trades []Trade 21 | BaseResponse 22 | } 23 | 24 | type TradeResponse struct { 25 | Trade 26 | BaseResponse 27 | } 28 | 29 | type OrderBookResponse struct { 30 | OrderBook 31 | BaseResponse 32 | } 33 | 34 | type FillResponse struct { 35 | Fill 36 | BaseResponse 37 | } 38 | 39 | type OrderResponse struct { 40 | Order 41 | BaseResponse 42 | } 43 | 44 | type WSRequest struct { 45 | Channel Channel `json:"channel"` 46 | Market string `json:"market"` 47 | Op Operation `json:"op"` 48 | Args map[string]interface{} `json:"args"` 49 | } 50 | 51 | type WsResponse struct { 52 | Channel Channel `json:"channel"` 53 | Market string `json:"market"` 54 | Type ResponseType `json:"type"` 55 | Code int `json:"code"` 56 | Message string `json:"msg"` 57 | Data json.RawMessage `json:"data"` 58 | } 59 | 60 | func (wr *WsResponse) MapToTradesResponse() (*TradesResponse, error) { 61 | var trades []Trade 62 | err := json.Unmarshal(wr.Data, &trades) 63 | if err != nil { 64 | return nil, errors.WithStack(err) 65 | } 66 | 67 | return &TradesResponse{ 68 | Trades: trades, 69 | BaseResponse: BaseResponse{ 70 | Type: wr.Type, 71 | Symbol: wr.Market, 72 | }, 73 | }, nil 74 | } 75 | 76 | func (wr *WsResponse) MapToTickerResponse() (*TickerResponse, error) { 77 | ticker := Ticker{} 78 | err := json.Unmarshal(wr.Data, &ticker) 79 | if err != nil { 80 | return nil, errors.WithStack(err) 81 | } 82 | 83 | return &TickerResponse{ 84 | Ticker: ticker, 85 | BaseResponse: BaseResponse{ 86 | Type: wr.Type, 87 | Symbol: wr.Market, 88 | }, 89 | }, nil 90 | } 91 | 92 | func (wr *WsResponse) MapToOrderBookResponse() (*OrderBookResponse, error) { 93 | book := OrderBook{} 94 | err := json.Unmarshal(wr.Data, &book) 95 | if err != nil { 96 | return nil, errors.WithStack(err) 97 | } 98 | 99 | return &OrderBookResponse{ 100 | OrderBook: book, 101 | BaseResponse: BaseResponse{ 102 | Type: wr.Type, 103 | Symbol: wr.Market, 104 | }, 105 | }, nil 106 | } 107 | 108 | func (wr *WsResponse) MapToFillResponse() (*FillResponse, error) { 109 | fill := Fill{} 110 | err := json.Unmarshal(wr.Data, &fill) 111 | if err != nil { 112 | return nil, errors.WithStack(err) 113 | } 114 | 115 | return &FillResponse{ 116 | Fill: fill, 117 | BaseResponse: BaseResponse{ 118 | Type: wr.Type, 119 | Symbol: wr.Market, 120 | }, 121 | }, nil 122 | } 123 | 124 | func (wr *WsResponse) MapToOrderResponse() (*OrderResponse, error) { 125 | order := Order{} 126 | err := json.Unmarshal(wr.Data, &order) 127 | if err != nil { 128 | return nil, errors.WithStack(err) 129 | } 130 | 131 | return &OrderResponse{ 132 | Order: order, 133 | BaseResponse: BaseResponse{ 134 | Type: wr.Type, 135 | Symbol: wr.Market, 136 | }, 137 | }, nil 138 | } 139 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/prepared.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "net" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // PreparedMessage caches on the wire representations of a message payload. 15 | // Use PreparedMessage to efficiently send a message payload to multiple 16 | // connections. PreparedMessage is especially useful when compression is used 17 | // because the CPU and memory expensive compression operation can be executed 18 | // once for a given set of compression options. 19 | type PreparedMessage struct { 20 | messageType int 21 | data []byte 22 | mu sync.Mutex 23 | frames map[prepareKey]*preparedFrame 24 | } 25 | 26 | // prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. 27 | type prepareKey struct { 28 | isServer bool 29 | compress bool 30 | compressionLevel int 31 | } 32 | 33 | // preparedFrame contains data in wire representation. 34 | type preparedFrame struct { 35 | once sync.Once 36 | data []byte 37 | } 38 | 39 | // NewPreparedMessage returns an initialized PreparedMessage. You can then send 40 | // it to connection using WritePreparedMessage method. Valid wire 41 | // representation will be calculated lazily only once for a set of current 42 | // connection options. 43 | func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { 44 | pm := &PreparedMessage{ 45 | messageType: messageType, 46 | frames: make(map[prepareKey]*preparedFrame), 47 | data: data, 48 | } 49 | 50 | // Prepare a plain server frame. 51 | _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | // To protect against caller modifying the data argument, remember the data 57 | // copied to the plain server frame. 58 | pm.data = frameData[len(frameData)-len(data):] 59 | return pm, nil 60 | } 61 | 62 | func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { 63 | pm.mu.Lock() 64 | frame, ok := pm.frames[key] 65 | if !ok { 66 | frame = &preparedFrame{} 67 | pm.frames[key] = frame 68 | } 69 | pm.mu.Unlock() 70 | 71 | var err error 72 | frame.once.Do(func() { 73 | // Prepare a frame using a 'fake' connection. 74 | // TODO: Refactor code in conn.go to allow more direct construction of 75 | // the frame. 76 | mu := make(chan struct{}, 1) 77 | mu <- struct{}{} 78 | var nc prepareConn 79 | c := &Conn{ 80 | conn: &nc, 81 | mu: mu, 82 | isServer: key.isServer, 83 | compressionLevel: key.compressionLevel, 84 | enableWriteCompression: true, 85 | writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), 86 | } 87 | if key.compress { 88 | c.newCompressionWriter = compressNoContextTakeover 89 | } 90 | err = c.WriteMessage(pm.messageType, pm.data) 91 | frame.data = nc.buf.Bytes() 92 | }) 93 | return pm.messageType, frame.data, err 94 | } 95 | 96 | type prepareConn struct { 97 | buf bytes.Buffer 98 | net.Conn 99 | } 100 | 101 | func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } 102 | func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } 103 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/compression.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "compress/flate" 9 | "errors" 10 | "io" 11 | "strings" 12 | "sync" 13 | ) 14 | 15 | const ( 16 | minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 17 | maxCompressionLevel = flate.BestCompression 18 | defaultCompressionLevel = 1 19 | ) 20 | 21 | var ( 22 | flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool 23 | flateReaderPool = sync.Pool{New: func() interface{} { 24 | return flate.NewReader(nil) 25 | }} 26 | ) 27 | 28 | func decompressNoContextTakeover(r io.Reader) io.ReadCloser { 29 | const tail = 30 | // Add four bytes as specified in RFC 31 | "\x00\x00\xff\xff" + 32 | // Add final block to squelch unexpected EOF error from flate reader. 33 | "\x01\x00\x00\xff\xff" 34 | 35 | fr, _ := flateReaderPool.Get().(io.ReadCloser) 36 | fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) 37 | return &flateReadWrapper{fr} 38 | } 39 | 40 | func isValidCompressionLevel(level int) bool { 41 | return minCompressionLevel <= level && level <= maxCompressionLevel 42 | } 43 | 44 | func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { 45 | p := &flateWriterPools[level-minCompressionLevel] 46 | tw := &truncWriter{w: w} 47 | fw, _ := p.Get().(*flate.Writer) 48 | if fw == nil { 49 | fw, _ = flate.NewWriter(tw, level) 50 | } else { 51 | fw.Reset(tw) 52 | } 53 | return &flateWriteWrapper{fw: fw, tw: tw, p: p} 54 | } 55 | 56 | // truncWriter is an io.Writer that writes all but the last four bytes of the 57 | // stream to another io.Writer. 58 | type truncWriter struct { 59 | w io.WriteCloser 60 | n int 61 | p [4]byte 62 | } 63 | 64 | func (w *truncWriter) Write(p []byte) (int, error) { 65 | n := 0 66 | 67 | // fill buffer first for simplicity. 68 | if w.n < len(w.p) { 69 | n = copy(w.p[w.n:], p) 70 | p = p[n:] 71 | w.n += n 72 | if len(p) == 0 { 73 | return n, nil 74 | } 75 | } 76 | 77 | m := len(p) 78 | if m > len(w.p) { 79 | m = len(w.p) 80 | } 81 | 82 | if nn, err := w.w.Write(w.p[:m]); err != nil { 83 | return n + nn, err 84 | } 85 | 86 | copy(w.p[:], w.p[m:]) 87 | copy(w.p[len(w.p)-m:], p[len(p)-m:]) 88 | nn, err := w.w.Write(p[:len(p)-m]) 89 | return n + nn, err 90 | } 91 | 92 | type flateWriteWrapper struct { 93 | fw *flate.Writer 94 | tw *truncWriter 95 | p *sync.Pool 96 | } 97 | 98 | func (w *flateWriteWrapper) Write(p []byte) (int, error) { 99 | if w.fw == nil { 100 | return 0, errWriteClosed 101 | } 102 | return w.fw.Write(p) 103 | } 104 | 105 | func (w *flateWriteWrapper) Close() error { 106 | if w.fw == nil { 107 | return errWriteClosed 108 | } 109 | err1 := w.fw.Flush() 110 | w.p.Put(w.fw) 111 | w.fw = nil 112 | if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { 113 | return errors.New("websocket: internal error, unexpected bytes at end of flate stream") 114 | } 115 | err2 := w.tw.w.Close() 116 | if err1 != nil { 117 | return err1 118 | } 119 | return err2 120 | } 121 | 122 | type flateReadWrapper struct { 123 | fr io.ReadCloser 124 | } 125 | 126 | func (r *flateReadWrapper) Read(p []byte) (int, error) { 127 | if r.fr == nil { 128 | return 0, io.ErrClosedPipe 129 | } 130 | n, err := r.fr.Read(p) 131 | if err == io.EOF { 132 | // Preemptively place the reader back in the pool. This helps with 133 | // scenarios where the application does not call NextReader() soon after 134 | // this final read. 135 | r.Close() 136 | } 137 | return n, err 138 | } 139 | 140 | func (r *flateReadWrapper) Close() error { 141 | if r.fr == nil { 142 | return io.ErrClosedPipe 143 | } 144 | err := r.fr.Close() 145 | flateReaderPool.Put(r.fr) 146 | r.fr = nil 147 | return err 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Gorilla WebSocket 2 | 3 | [![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) 4 | [![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket) 5 | 6 | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the 7 | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. 8 | 9 | ### Documentation 10 | 11 | * [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) 12 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 13 | * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) 14 | * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) 15 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 16 | 17 | ### Status 18 | 19 | The Gorilla WebSocket package provides a complete and tested implementation of 20 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 21 | package API is stable. 22 | 23 | ### Installation 24 | 25 | go get github.com/gorilla/websocket 26 | 27 | ### Protocol Compliance 28 | 29 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 30 | Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn 31 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 32 | 33 | ### Gorilla WebSocket compared with other packages 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
53 | 54 | Notes: 55 | 56 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 57 | 2. The application can get the type of a received data message by implementing 58 | a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) 59 | function. 60 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 61 | Read returns when the input buffer is full or a frame boundary is 62 | encountered. Each call to Write sends a single frame message. The Gorilla 63 | io.Reader and io.WriteCloser operate on a single WebSocket message. 64 | 65 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "time" 8 | 9 | "github.com/akamensky/argparse" 10 | "github.com/avast/retry-go" 11 | "github.com/grishinsana/goftx" 12 | "github.com/robfig/cron/v3" 13 | "github.com/shopspring/decimal" 14 | "go.uber.org/ratelimit" 15 | ) 16 | 17 | var ( 18 | limiter ratelimit.Limiter = ratelimit.New(30) 19 | client *goftx.Client 20 | ) 21 | 22 | func main() { 23 | job := cron.New() 24 | parser := argparse.NewParser("ftx-auto-lend", "Automatically compounds lending payouts.") 25 | apiKey := parser.String("k", "key", &argparse.Options{Required: true, Help: "API key"}) 26 | apiSecret := parser.String("s", "secret", &argparse.Options{Required: true, Help: "API secret"}) 27 | subAcc := parser.String("a", "subaccount", &argparse.Options{Required: false, Help: "Subaccount"}) 28 | coinList := parser.List("c", "coin", &argparse.Options{Required: false, Help: "Coin to lend"}) 29 | rate := parser.String("r", "min-rate", &argparse.Options{Required: false, Help: "Minimum lending rate"}) 30 | err := parser.Parse(os.Args) 31 | 32 | if err != nil { 33 | fmt.Print(parser.Usage(err)) 34 | return 35 | } 36 | 37 | strCoins := []string{} 38 | strRate := *rate 39 | 40 | if len(*coinList) == 0 { 41 | strCoins = append(strCoins, "USD") 42 | } else { 43 | for i := range *coinList { 44 | strCoins = append(strCoins, strings.ToUpper((*coinList)[i])) 45 | } 46 | } 47 | 48 | if strRate == "" { 49 | strRate = "0.000001" 50 | } 51 | 52 | minRate, err := decimal.NewFromString(strRate) 53 | 54 | if err != nil { 55 | Error.Fatal("Min Rate: Invalid number") 56 | } 57 | 58 | client = goftx.New( 59 | goftx.WithAuth(*apiKey, *apiSecret), 60 | goftx.WithSubaccount(*subAcc), 61 | ) 62 | 63 | _, err = client.GetAccountInformation() 64 | 65 | if err != nil { 66 | Error.Fatalln("It seems like the supplied API key is wrong. Please check and try again") 67 | } 68 | 69 | job.Start() 70 | 71 | job.AddFunc("5 * * * *", func() { 72 | for i := range strCoins { 73 | coin := strCoins[i] 74 | Info.Printf("Running lending offer update for %s.\n", coin) 75 | lendable, delta, err := getMaxLendingAmount(coin) 76 | 77 | if err != nil { 78 | Error.Println(err) 79 | continue 80 | } 81 | 82 | if delta.Equal(decimal.Zero) || delta.LessThan(decimal.Zero) { 83 | Info.Println("No increase in funds to update lending offer.") 84 | continue 85 | } 86 | 87 | Info.Printf("New lendable amount of %s is %s (+%s).", coin, lendable, delta) 88 | Info.Printf("Updating lending offer with a minimum rate of %s.", minRate) 89 | 90 | err = updateLendingOffer(coin, lendable, minRate) 91 | 92 | if err != nil { 93 | Error.Println(err) 94 | continue 95 | } 96 | } 97 | }) 98 | 99 | fmt.Printf("I will attempt to update your lending offers once per hour.\nPress any key if you want to stop and exit the program.") 100 | fmt.Scanln() 101 | fmt.Println("Bye!") 102 | } 103 | 104 | func updateLendingOffer(coin string, amount decimal.Decimal, minRate decimal.Decimal) (err error) { 105 | err = retry.Do( 106 | func() error { 107 | limiter.Take() 108 | err := client.SubmitLendingOffer(coin, amount, minRate) 109 | 110 | if err != nil { 111 | fmt.Printf("%+v\n", err) 112 | return err 113 | } 114 | 115 | return nil 116 | }, 117 | retry.Delay(time.Minute), 118 | retry.Attempts(10), 119 | retry.DelayType(retry.FixedDelay), 120 | ) 121 | 122 | return 123 | } 124 | 125 | func getMaxLendingAmount(coin string) (lendable decimal.Decimal, delta decimal.Decimal, err error) { 126 | err = retry.Do( 127 | func() error { 128 | limiter.Take() 129 | resp, err := client.GetLendingInfo() 130 | 131 | if err != nil { 132 | return err 133 | } 134 | 135 | for i := range resp { 136 | if resp[i].Coin == coin { 137 | lendable = resp[i].Lendable 138 | delta = resp[i].Lendable.Sub(resp[i].Offered) 139 | } 140 | } 141 | 142 | return nil 143 | }, 144 | retry.Delay(time.Minute), 145 | retry.Attempts(10), 146 | retry.DelayType(retry.FixedDelay), 147 | ) 148 | 149 | return 150 | } 151 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/limiter_atomic.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016,2020 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ratelimit // import "go.uber.org/ratelimit" 22 | 23 | import ( 24 | "time" 25 | 26 | "sync/atomic" 27 | "unsafe" 28 | ) 29 | 30 | type state struct { 31 | last time.Time 32 | sleepFor time.Duration 33 | } 34 | 35 | type atomicLimiter struct { 36 | state unsafe.Pointer 37 | //lint:ignore U1000 Padding is unused but it is crucial to maintain performance 38 | // of this rate limiter in case of collocation with other frequently accessed memory. 39 | padding [56]byte // cache line size - state pointer size = 64 - 8; created to avoid false sharing. 40 | 41 | perRequest time.Duration 42 | maxSlack time.Duration 43 | clock Clock 44 | } 45 | 46 | // newAtomicBased returns a new atomic based limiter. 47 | func newAtomicBased(rate int, opts ...Option) *atomicLimiter { 48 | // TODO consider moving config building to the implementation 49 | // independent code. 50 | config := buildConfig(opts) 51 | perRequest := config.per / time.Duration(rate) 52 | l := &atomicLimiter{ 53 | perRequest: perRequest, 54 | maxSlack: -1 * time.Duration(config.slack) * perRequest, 55 | clock: config.clock, 56 | } 57 | 58 | initialState := state{ 59 | last: time.Time{}, 60 | sleepFor: 0, 61 | } 62 | atomic.StorePointer(&l.state, unsafe.Pointer(&initialState)) 63 | return l 64 | } 65 | 66 | // Take blocks to ensure that the time spent between multiple 67 | // Take calls is on average time.Second/rate. 68 | func (t *atomicLimiter) Take() time.Time { 69 | var ( 70 | newState state 71 | taken bool 72 | interval time.Duration 73 | ) 74 | for !taken { 75 | now := t.clock.Now() 76 | 77 | previousStatePointer := atomic.LoadPointer(&t.state) 78 | oldState := (*state)(previousStatePointer) 79 | 80 | newState = state{ 81 | last: now, 82 | sleepFor: oldState.sleepFor, 83 | } 84 | 85 | // If this is our first request, then we allow it. 86 | if oldState.last.IsZero() { 87 | taken = atomic.CompareAndSwapPointer(&t.state, previousStatePointer, unsafe.Pointer(&newState)) 88 | continue 89 | } 90 | 91 | // sleepFor calculates how much time we should sleep based on 92 | // the perRequest budget and how long the last request took. 93 | // Since the request may take longer than the budget, this number 94 | // can get negative, and is summed across requests. 95 | newState.sleepFor += t.perRequest - now.Sub(oldState.last) 96 | // We shouldn't allow sleepFor to get too negative, since it would mean that 97 | // a service that slowed down a lot for a short period of time would get 98 | // a much higher RPS following that. 99 | if newState.sleepFor < t.maxSlack { 100 | newState.sleepFor = t.maxSlack 101 | } 102 | if newState.sleepFor > 0 { 103 | newState.last = newState.last.Add(newState.sleepFor) 104 | interval, newState.sleepFor = newState.sleepFor, 0 105 | } 106 | taken = atomic.CompareAndSwapPointer(&t.state, previousStatePointer, unsafe.Pointer(&newState)) 107 | } 108 | t.clock.Sleep(interval) 109 | return newState.last 110 | } 111 | -------------------------------------------------------------------------------- /vendor/go.uber.org/ratelimit/ratelimit.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016,2020 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ratelimit // import "go.uber.org/ratelimit" 22 | 23 | import ( 24 | "time" 25 | 26 | "github.com/andres-erbsen/clock" 27 | ) 28 | 29 | // Note: This file is inspired by: 30 | // https://github.com/prashantv/go-bench/blob/master/ratelimit 31 | 32 | // Limiter is used to rate-limit some process, possibly across goroutines. 33 | // The process is expected to call Take() before every iteration, which 34 | // may block to throttle the goroutine. 35 | type Limiter interface { 36 | // Take should block to make sure that the RPS is met. 37 | Take() time.Time 38 | } 39 | 40 | // Clock is the minimum necessary interface to instantiate a rate limiter with 41 | // a clock or mock clock, compatible with clocks created using 42 | // github.com/andres-erbsen/clock. 43 | type Clock interface { 44 | Now() time.Time 45 | Sleep(time.Duration) 46 | } 47 | 48 | // config configures a limiter. 49 | type config struct { 50 | clock Clock 51 | slack int 52 | per time.Duration 53 | } 54 | 55 | // New returns a Limiter that will limit to the given RPS. 56 | func New(rate int, opts ...Option) Limiter { 57 | return newAtomicBased(rate, opts...) 58 | } 59 | 60 | // buildConfig combines defaults with options. 61 | func buildConfig(opts []Option) config { 62 | c := config{ 63 | clock: clock.New(), 64 | slack: 10, 65 | per: time.Second, 66 | } 67 | 68 | for _, opt := range opts { 69 | opt.apply(&c) 70 | } 71 | return c 72 | } 73 | 74 | // Option configures a Limiter. 75 | type Option interface { 76 | apply(*config) 77 | } 78 | 79 | type clockOption struct { 80 | clock Clock 81 | } 82 | 83 | func (o clockOption) apply(c *config) { 84 | c.clock = o.clock 85 | } 86 | 87 | // WithClock returns an option for ratelimit.New that provides an alternate 88 | // Clock implementation, typically a mock Clock for testing. 89 | func WithClock(clock Clock) Option { 90 | return clockOption{clock: clock} 91 | } 92 | 93 | type slackOption int 94 | 95 | func (o slackOption) apply(c *config) { 96 | c.slack = int(o) 97 | } 98 | 99 | // WithoutSlack configures the limiter to be strict and not to accumulate 100 | // previously "unspent" requests for future bursts of traffic. 101 | var WithoutSlack Option = slackOption(0) 102 | 103 | // WithSlack configures custom slack. 104 | // Slack allows the limiter to accumulate "unspent" requests 105 | // for future bursts of traffic. 106 | func WithSlack(slack int) Option { 107 | return slackOption(slack) 108 | } 109 | 110 | type perOption time.Duration 111 | 112 | func (p perOption) apply(c *config) { 113 | c.per = time.Duration(p) 114 | } 115 | 116 | // Per allows configuring limits for different time windows. 117 | // 118 | // The default window is one second, so New(100) produces a one hundred per 119 | // second (100 Hz) rate limiter. 120 | // 121 | // New(2, Per(60*time.Second)) creates a 2 per minute rate limiter. 122 | func Per(per time.Duration) Option { 123 | return perOption(per) 124 | } 125 | 126 | type unlimited struct{} 127 | 128 | // NewUnlimited returns a RateLimiter that is not limited. 129 | func NewUnlimited() Limiter { 130 | return unlimited{} 131 | } 132 | 133 | func (unlimited) Take() time.Time { 134 | return time.Now() 135 | } 136 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/models/orders.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/shopspring/decimal" 7 | ) 8 | 9 | type Order struct { 10 | ID int64 `json:"id"` 11 | Market string `json:"market"` 12 | Type OrderType `json:"type"` 13 | Side Side `json:"side"` 14 | Price decimal.Decimal `json:"price"` 15 | Size decimal.Decimal `json:"size"` 16 | FilledSize decimal.Decimal `json:"filledSize"` 17 | RemainingSize decimal.Decimal `json:"remainingSize"` 18 | AvgFillPrice decimal.Decimal `json:"avgFillPrice"` 19 | Status Status `json:"status"` 20 | CreatedAt time.Time `json:"createdAt"` 21 | ReduceOnly bool `json:"reduceOnly"` 22 | Ioc bool `json:"ioc"` 23 | PostOnly bool `json:"postOnly"` 24 | Future string `json:"future"` 25 | ClientID string `json:"clientId"` 26 | } 27 | 28 | type PlaceOrderParams struct { 29 | Market string `json:"market"` 30 | Type OrderType `json:"type"` 31 | Side Side `json:"side"` 32 | Price decimal.Decimal `json:"price"` 33 | Size decimal.Decimal `json:"size"` 34 | ReduceOnly bool `json:"reduceOnly"` 35 | Ioc bool `json:"ioc"` 36 | PostOnly bool `json:"postOnly"` 37 | } 38 | 39 | type PlaceStopLossParams struct { 40 | Market string `json:"market"` 41 | Side Side `json:"side"` 42 | Size decimal.Decimal `json:"size"` 43 | ReduceOnly bool `json:"reduceOnly"` 44 | Type TriggerOrderType `json:"type"` 45 | TriggerPrice decimal.Decimal `json:"triggerPrice"` 46 | } 47 | 48 | type PlaceStopLimitParams struct { 49 | Market string `json:"market"` 50 | Side Side `json:"side"` 51 | Size decimal.Decimal `json:"size"` 52 | ReduceOnly bool `json:"reduceOnly"` 53 | Type TriggerOrderType `json:"type"` 54 | TriggerPrice decimal.Decimal `json:"triggerPrice"` 55 | OrderPrice decimal.Decimal `json:"orderPrice"` 56 | } 57 | 58 | type PlaceTrailingStopParams struct { 59 | Market string `json:"market"` 60 | Side Side `json:"side"` 61 | Size decimal.Decimal `json:"size"` 62 | ReduceOnly bool `json:"reduceOnly"` 63 | Type TriggerOrderType `json:"type"` 64 | TrailValue decimal.Decimal `json:"trailValue"` 65 | } 66 | 67 | type GetOrdersHistoryParams struct { 68 | Market *string `json:"market"` 69 | Limit *int `json:"limit"` 70 | StartTime *int `json:"start_time"` 71 | EndTime *int `json:"end_time"` 72 | } 73 | 74 | type TriggerOrder struct { 75 | ID int64 `json:"id"` 76 | OrderID int64 `json:"orderId"` 77 | Market string `json:"market"` 78 | CreatedAt time.Time `json:"createdAt"` 79 | Error string `json:"error"` 80 | Future string `json:"future"` 81 | OrderPrice decimal.Decimal `json:"orderPrice"` 82 | ReduceOnly bool `json:"reduceOnly"` 83 | Side Side `json:"side"` 84 | Size decimal.Decimal `json:"size"` 85 | Status Status `json:"status"` 86 | TrailStart decimal.Decimal `json:"trailStart"` 87 | TrailValue decimal.Decimal `json:"trailValue"` 88 | TriggerPrice decimal.Decimal `json:"triggerPrice"` 89 | TriggeredAt time.Time `json:"triggeredAt"` 90 | Type TriggerOrderType `json:"type"` 91 | OrderType OrderType `json:"orderType"` 92 | FilledSize decimal.Decimal `json:"filledSize"` 93 | AvgFillPrice decimal.Decimal `json:"avgFillPrice"` 94 | OrderStatus string `json:"orderStatus"` 95 | RetryUntilFilled bool `json:"retryUntilFilled"` 96 | } 97 | 98 | type GetOpenTriggerOrdersParams struct { 99 | Market *string `json:"market"` 100 | Type *TriggerOrderType `json:"type"` 101 | } 102 | 103 | type Trigger struct { 104 | Error string `json:"error"` 105 | FilledSize float64 `json:"filledSize"` 106 | OrderSize float64 `json:"orderSize"` 107 | OrderID int64 `json:"orderId"` 108 | Time time.Time `json:"time"` 109 | } 110 | -------------------------------------------------------------------------------- /vendor/github.com/shopspring/decimal/rounding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Multiprecision decimal numbers. 6 | // For floating-point formatting only; not general purpose. 7 | // Only operations are assign and (binary) left/right shift. 8 | // Can do binary floating point in multiprecision decimal precisely 9 | // because 2 divides 10; cannot do decimal floating point 10 | // in multiprecision binary precisely. 11 | 12 | package decimal 13 | 14 | type floatInfo struct { 15 | mantbits uint 16 | expbits uint 17 | bias int 18 | } 19 | 20 | var float32info = floatInfo{23, 8, -127} 21 | var float64info = floatInfo{52, 11, -1023} 22 | 23 | // roundShortest rounds d (= mant * 2^exp) to the shortest number of digits 24 | // that will let the original floating point value be precisely reconstructed. 25 | func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { 26 | // If mantissa is zero, the number is zero; stop now. 27 | if mant == 0 { 28 | d.nd = 0 29 | return 30 | } 31 | 32 | // Compute upper and lower such that any decimal number 33 | // between upper and lower (possibly inclusive) 34 | // will round to the original floating point number. 35 | 36 | // We may see at once that the number is already shortest. 37 | // 38 | // Suppose d is not denormal, so that 2^exp <= d < 10^dp. 39 | // The closest shorter number is at least 10^(dp-nd) away. 40 | // The lower/upper bounds computed below are at distance 41 | // at most 2^(exp-mantbits). 42 | // 43 | // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits), 44 | // or equivalently log2(10)*(dp-nd) > exp-mantbits. 45 | // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32). 46 | minexp := flt.bias + 1 // minimum possible exponent 47 | if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) { 48 | // The number is already shortest. 49 | return 50 | } 51 | 52 | // d = mant << (exp - mantbits) 53 | // Next highest floating point number is mant+1 << exp-mantbits. 54 | // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1. 55 | upper := new(decimal) 56 | upper.Assign(mant*2 + 1) 57 | upper.Shift(exp - int(flt.mantbits) - 1) 58 | 59 | // d = mant << (exp - mantbits) 60 | // Next lowest floating point number is mant-1 << exp-mantbits, 61 | // unless mant-1 drops the significant bit and exp is not the minimum exp, 62 | // in which case the next lowest is mant*2-1 << exp-mantbits-1. 63 | // Either way, call it mantlo << explo-mantbits. 64 | // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1. 65 | var mantlo uint64 66 | var explo int 67 | if mant > 1<::::::... 37 | // For example, if the orderbook was comprised of the following two bids and asks: 38 | // 39 | // bids: [[5000.5, 10], [4995.0, 5]] 40 | // asks: [[5001.0, 6], [5002.0, 7]] 41 | // The string would be '5005.5:10:5001.0:6:4995.0:5:5002.0:7' 42 | // 43 | // If there are more orders on one side of the book than the other, then simply omit the information about orders that don't exist. 44 | // 45 | // For example, if the orderbook had the following bids and asks: 46 | // 47 | // bids: [[5000.5, 10], [4995.0, 5]] 48 | // asks: [[5001.0, 6]] 49 | // The string would be '5005.5:10:5001.0:6:4995.0:5' 50 | // 51 | // The final checksum is the crc32 value of this string. 52 | type OrderBook struct { 53 | Asks [][]decimal.Decimal `json:"asks"` 54 | Bids [][]decimal.Decimal `json:"bids"` 55 | Checksum int64 `json:"checksum,omitempty"` 56 | Time FTXTime `json:"time"` 57 | } 58 | 59 | type Trade struct { 60 | ID int64 `json:"id"` 61 | Liquidation bool `json:"liquidation"` 62 | Price decimal.Decimal `json:"price"` 63 | Side string `json:"side"` 64 | Size decimal.Decimal `json:"size"` 65 | Time time.Time `json:"time"` 66 | } 67 | 68 | type HistoricalPrice struct { 69 | StartTime time.Time `json:"startTime"` 70 | Open decimal.Decimal `json:"open"` 71 | Close decimal.Decimal `json:"close"` 72 | High decimal.Decimal `json:"high"` 73 | Low decimal.Decimal `json:"low"` 74 | Volume decimal.Decimal `json:"volume"` 75 | } 76 | 77 | type Ticker struct { 78 | Bid decimal.Decimal `json:"bid"` 79 | Ask decimal.Decimal `json:"ask"` 80 | BidSize decimal.Decimal `json:"bidSize"` 81 | AskSize decimal.Decimal `json:"askSize"` 82 | Last decimal.Decimal `json:"last"` 83 | Time FTXTime `json:"time"` 84 | } 85 | 86 | type Fill struct { 87 | Fee decimal.Decimal `json:"fee"` 88 | FeeRate decimal.Decimal `json:"feeRate"` 89 | Future string `json:"future"` 90 | ID int64 `json:"id"` 91 | Liquidity string `json:"liquidity"` 92 | Market string `json:"market"` 93 | OrderID int64 `json:"orderId"` 94 | TradeID int64 `json:"tradeId"` 95 | Price decimal.Decimal `json:"price"` 96 | Side string `json:"side"` 97 | Size decimal.Decimal `json:"size"` 98 | Time FTXTime `json:"time"` 99 | Type string `json:"type"` 100 | } 101 | 102 | type GetTradesParams struct { 103 | Limit *int `json:"limit"` 104 | StartTime *int `json:"start_time"` 105 | EndTime *int `json:"end_time"` 106 | } 107 | 108 | type GetHistoricalPricesParams struct { 109 | Resolution Resolution `json:"resolution"` 110 | Limit *int `json:"limit"` 111 | StartTime *int `json:"start_time"` 112 | EndTime *int `json:"end_time"` 113 | } 114 | 115 | type GetLastCandleParams struct { 116 | Resolution Resolution `json:"resolution"` 117 | } 118 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/akamensky/argparse v1.2.2 h1:P17T0ZjlUNJuWTPPJ2A5dM1wxarHgHqfYH+AZTo2xQA= 2 | github.com/akamensky/argparse v1.2.2/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA= 3 | github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= 4 | github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= 5 | github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 6 | github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= 7 | github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 12 | github.com/go-numb/go-ftx v0.0.0-20200829181514-3144aa68f505/go.mod h1:rjG/Mg/la6U9w0NN/oaMZkgCpEQgseKPOl6EkvYkjCw= 13 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 14 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 15 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 16 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 17 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 18 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 19 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 20 | github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 21 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 22 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 23 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 24 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 25 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 26 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 27 | github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E= 28 | github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 29 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 30 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 31 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 32 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 33 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 34 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 35 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 36 | github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= 37 | github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= 38 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 39 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 40 | go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= 41 | go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= 42 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 43 | golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 44 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 45 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 46 | golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 47 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 48 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 49 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 50 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 51 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 52 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= 2 | github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 3 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= 8 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 9 | github.com/go-numb/go-ftx v0.0.0-20200829181514-3144aa68f505 h1:iOIIPP+XjnNbEH7N139XWTRdjTK3ZmM/R4RFwUZc9Cc= 10 | github.com/go-numb/go-ftx v0.0.0-20200829181514-3144aa68f505/go.mod h1:rjG/Mg/la6U9w0NN/oaMZkgCpEQgseKPOl6EkvYkjCw= 11 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 12 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 13 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 14 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 15 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 16 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 17 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 18 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 19 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 20 | github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= 21 | github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 22 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 23 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 24 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 25 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 26 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 27 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 28 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 29 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 30 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 31 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 32 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 33 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 34 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 35 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 36 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 37 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 38 | github.com/valyala/fasthttp v1.16.0 h1:9zAqOYLl8Tuy3E5R6ckzGDJ1g8+pw15oQp2iL9Jl6gQ= 39 | github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= 40 | github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= 41 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 42 | golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 43 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 46 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 47 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 48 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 49 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 50 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 51 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 52 | -------------------------------------------------------------------------------- /vendor/github.com/akamensky/argparse/command.go: -------------------------------------------------------------------------------- 1 | package argparse 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func (o *Command) help(sname, lname string) { 9 | result := &help{} 10 | 11 | if lname == "" { 12 | sname, lname = "h", "help" 13 | } 14 | 15 | a := &arg{ 16 | result: result, 17 | sname: sname, 18 | lname: lname, 19 | size: 1, 20 | opts: &Options{Help: "Print help information"}, 21 | unique: true, 22 | } 23 | 24 | o.addArg(a) 25 | } 26 | 27 | func (o *Command) addArg(a *arg) error { 28 | // long name should be provided 29 | if a.lname == "" { 30 | return fmt.Errorf("long name should be provided") 31 | } 32 | // short name could be provided and must not exceed 1 character 33 | if len(a.sname) > 1 { 34 | return fmt.Errorf("short name must not exceed 1 character") 35 | } 36 | // Search parents for overlapping commands and fail if any 37 | current := o 38 | for current != nil { 39 | if current.args != nil { 40 | for _, v := range current.args { 41 | if a.lname != "help" || a.sname != "h" { 42 | if a.sname != "" && a.sname == v.sname { 43 | return fmt.Errorf("short name %s occurs more than once", a.sname) 44 | } 45 | if a.lname == v.lname { 46 | return fmt.Errorf("long name %s occurs more than once", a.lname) 47 | } 48 | } 49 | } 50 | } 51 | current = current.parent 52 | } 53 | a.parent = o 54 | o.args = append(o.args, a) 55 | return nil 56 | } 57 | 58 | //parseSubCommands - Parses subcommands if any 59 | func (o *Command) parseSubCommands(args *[]string) error { 60 | if o.commands != nil && len(o.commands) > 0 { 61 | // If we have subcommands and 0 args left 62 | // that is an error of SubCommandError type 63 | if len(*args) < 1 { 64 | return newSubCommandError(o) 65 | } 66 | for _, v := range o.commands { 67 | err := v.parse(args) 68 | if err != nil { 69 | return err 70 | } 71 | } 72 | } 73 | return nil 74 | } 75 | 76 | //parseArguments - Parses arguments 77 | func (o *Command) parseArguments(args *[]string) error { 78 | // Iterate over the args 79 | for i := 0; i < len(o.args); i++ { 80 | oarg := o.args[i] 81 | for j := 0; j < len(*args); j++ { 82 | arg := (*args)[j] 83 | if arg == "" { 84 | continue 85 | } 86 | if strings.Contains(arg, "=") { 87 | splitInd := strings.LastIndex(arg, "=") 88 | equalArg := []string{arg[:splitInd], arg[splitInd+1:]} 89 | if cnt, err := oarg.check(equalArg[0]); err != nil { 90 | return err 91 | } else if cnt > 0 { 92 | if equalArg[1] == "" { 93 | return fmt.Errorf("not enough arguments for %s", oarg.name()) 94 | } 95 | oarg.eqChar = true 96 | oarg.size = 1 97 | currArg := []string{equalArg[1]} 98 | err := oarg.parse(currArg, cnt) 99 | if err != nil { 100 | return err 101 | } 102 | oarg.reduce(j, args) 103 | continue 104 | } 105 | } 106 | if cnt, err := oarg.check(arg); err != nil { 107 | return err 108 | } else if cnt > 0 { 109 | if len(*args) < j+oarg.size { 110 | return fmt.Errorf("not enough arguments for %s", oarg.name()) 111 | } 112 | err := oarg.parse((*args)[j+1:j+oarg.size], cnt) 113 | if err != nil { 114 | return err 115 | } 116 | oarg.reduce(j, args) 117 | continue 118 | } 119 | } 120 | 121 | // Check if arg is required and not provided 122 | if oarg.opts != nil && oarg.opts.Required && !oarg.parsed { 123 | return fmt.Errorf("[%s] is required", oarg.name()) 124 | } 125 | 126 | // Check for argument default value and if provided try to type cast and assign 127 | if oarg.opts != nil && oarg.opts.Default != nil && !oarg.parsed { 128 | err := oarg.setDefault() 129 | if err != nil { 130 | return err 131 | } 132 | } 133 | } 134 | return nil 135 | } 136 | 137 | // Will parse provided list of arguments 138 | // common usage would be to pass directly os.Args 139 | func (o *Command) parse(args *[]string) error { 140 | // If we already been parsed do nothing 141 | if o.parsed { 142 | return nil 143 | } 144 | 145 | // If no arguments left to parse do nothing 146 | if len(*args) < 1 { 147 | return nil 148 | } 149 | 150 | // Parse only matching commands 151 | // But we always have to parse top level 152 | if o.name == "" { 153 | o.name = (*args)[0] 154 | } else { 155 | if o.name != (*args)[0] && o.parent != nil { 156 | return nil 157 | } 158 | } 159 | 160 | // Set happened status to true when command happend 161 | o.happened = true 162 | 163 | // Reduce arguments by removing Command name 164 | *args = (*args)[1:] 165 | 166 | // Parse subcommands if any 167 | if err := o.parseSubCommands(args); err != nil { 168 | return err 169 | } 170 | 171 | // Parse arguments if any 172 | if err := o.parseArguments(args); err != nil { 173 | return err 174 | } 175 | 176 | // Set parsed status to true and return quietly 177 | o.parsed = true 178 | return nil 179 | } 180 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/subaccounts.go: -------------------------------------------------------------------------------- 1 | package goftx 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/grishinsana/goftx/models" 7 | "github.com/pkg/errors" 8 | "net/http" 9 | ) 10 | 11 | const ( 12 | apiSubaccounts = "/subaccounts" 13 | apiChangeSubaccountName = "/subaccounts/update_name" 14 | apiGetSubaccountBalances = "/subaccounts/%s/balances" 15 | apiTransfer = "/subaccounts/transfer" 16 | ) 17 | 18 | type SubAccounts struct { 19 | client *Client 20 | } 21 | 22 | func (s *SubAccounts) GetSubaccounts() ([]*models.SubAccount, error) { 23 | request, err := s.client.prepareRequest(Request{ 24 | Auth: true, 25 | Method: http.MethodGet, 26 | URL: fmt.Sprintf("%s%s", apiUrl, apiSubaccounts), 27 | }) 28 | if err != nil { 29 | return nil, errors.WithStack(err) 30 | } 31 | 32 | response, err := s.client.do(request) 33 | if err != nil { 34 | return nil, errors.WithStack(err) 35 | } 36 | 37 | var result []*models.SubAccount 38 | err = json.Unmarshal(response, &result) 39 | if err != nil { 40 | return nil, errors.WithStack(err) 41 | } 42 | 43 | return result, nil 44 | } 45 | 46 | func (s *SubAccounts) CreateSubaccount(nickname string) (*models.SubAccount, error) { 47 | body, err := json.Marshal(struct { 48 | Nickname string `json:"nickname"` 49 | }{Nickname: nickname}) 50 | if err != nil { 51 | return nil, errors.WithStack(err) 52 | } 53 | 54 | request, err := s.client.prepareRequest(Request{ 55 | Auth: true, 56 | Method: http.MethodPost, 57 | URL: fmt.Sprintf("%s%s", apiUrl, apiSubaccounts), 58 | Body: body, 59 | }) 60 | if err != nil { 61 | return nil, errors.WithStack(err) 62 | } 63 | 64 | response, err := s.client.do(request) 65 | if err != nil { 66 | return nil, errors.WithStack(err) 67 | } 68 | 69 | var result models.SubAccount 70 | err = json.Unmarshal(response, &result) 71 | if err != nil { 72 | return nil, errors.WithStack(err) 73 | } 74 | 75 | return &result, nil 76 | } 77 | 78 | func (s *SubAccounts) ChangeSubaccount(nickname, newNickname string) error { 79 | body, err := json.Marshal(struct { 80 | Nickname string `json:"nickname"` 81 | NewNickname string `json:"newNickname"` 82 | }{Nickname: nickname, NewNickname: newNickname}) 83 | if err != nil { 84 | return errors.WithStack(err) 85 | } 86 | 87 | request, err := s.client.prepareRequest(Request{ 88 | Auth: true, 89 | Method: http.MethodPost, 90 | URL: fmt.Sprintf("%s%s", apiUrl, apiChangeSubaccountName), 91 | Body: body, 92 | }) 93 | if err != nil { 94 | return errors.WithStack(err) 95 | } 96 | 97 | _, err = s.client.do(request) 98 | if err != nil { 99 | return errors.WithStack(err) 100 | } 101 | 102 | return nil 103 | } 104 | 105 | func (s *SubAccounts) DeleteSubaccount(nickname string) error { 106 | body, err := json.Marshal(struct { 107 | Nickname string `json:"nickname"` 108 | }{Nickname: nickname}) 109 | if err != nil { 110 | return errors.WithStack(err) 111 | } 112 | 113 | request, err := s.client.prepareRequest(Request{ 114 | Auth: true, 115 | Method: http.MethodDelete, 116 | URL: fmt.Sprintf("%s%s", apiUrl, apiSubaccounts), 117 | Body: body, 118 | }) 119 | if err != nil { 120 | return errors.WithStack(err) 121 | } 122 | 123 | _, err = s.client.do(request) 124 | if err != nil { 125 | return errors.WithStack(err) 126 | } 127 | 128 | return nil 129 | } 130 | 131 | func (s *SubAccounts) GetSubaccountBalances(nickname string) ([]*models.Balance, error) { 132 | request, err := s.client.prepareRequest(Request{ 133 | Auth: true, 134 | Method: http.MethodGet, 135 | URL: fmt.Sprintf("%s%s", apiUrl, fmt.Sprintf(apiGetSubaccountBalances, nickname)), 136 | }) 137 | if err != nil { 138 | return nil, errors.WithStack(err) 139 | } 140 | 141 | response, err := s.client.do(request) 142 | if err != nil { 143 | return nil, errors.WithStack(err) 144 | } 145 | 146 | var result []*models.Balance 147 | err = json.Unmarshal(response, &result) 148 | if err != nil { 149 | return nil, errors.WithStack(err) 150 | } 151 | 152 | return result, nil 153 | } 154 | 155 | func (s *SubAccounts) Transfer(payload *models.TransferPayload) (*models.TransferResponse, error) { 156 | body, err := json.Marshal(payload) 157 | if err != nil { 158 | return nil, errors.WithStack(err) 159 | } 160 | 161 | request, err := s.client.prepareRequest(Request{ 162 | Auth: true, 163 | Method: http.MethodPost, 164 | URL: fmt.Sprintf("%s%s", apiUrl, apiTransfer), 165 | Body: body, 166 | }) 167 | if err != nil { 168 | return nil, errors.WithStack(err) 169 | } 170 | 171 | response, err := s.client.do(request) 172 | if err != nil { 173 | return nil, errors.WithStack(err) 174 | } 175 | 176 | var result models.TransferResponse 177 | err = json.Unmarshal(response, &result) 178 | if err != nil { 179 | return nil, errors.WithStack(err) 180 | } 181 | 182 | return &result, nil 183 | } 184 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/stack.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "path" 7 | "runtime" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // Frame represents a program counter inside a stack frame. 13 | // For historical reasons if Frame is interpreted as a uintptr 14 | // its value represents the program counter + 1. 15 | type Frame uintptr 16 | 17 | // pc returns the program counter for this frame; 18 | // multiple frames may have the same PC value. 19 | func (f Frame) pc() uintptr { return uintptr(f) - 1 } 20 | 21 | // file returns the full path to the file that contains the 22 | // function for this Frame's pc. 23 | func (f Frame) file() string { 24 | fn := runtime.FuncForPC(f.pc()) 25 | if fn == nil { 26 | return "unknown" 27 | } 28 | file, _ := fn.FileLine(f.pc()) 29 | return file 30 | } 31 | 32 | // line returns the line number of source code of the 33 | // function for this Frame's pc. 34 | func (f Frame) line() int { 35 | fn := runtime.FuncForPC(f.pc()) 36 | if fn == nil { 37 | return 0 38 | } 39 | _, line := fn.FileLine(f.pc()) 40 | return line 41 | } 42 | 43 | // name returns the name of this function, if known. 44 | func (f Frame) name() string { 45 | fn := runtime.FuncForPC(f.pc()) 46 | if fn == nil { 47 | return "unknown" 48 | } 49 | return fn.Name() 50 | } 51 | 52 | // Format formats the frame according to the fmt.Formatter interface. 53 | // 54 | // %s source file 55 | // %d source line 56 | // %n function name 57 | // %v equivalent to %s:%d 58 | // 59 | // Format accepts flags that alter the printing of some verbs, as follows: 60 | // 61 | // %+s function name and path of source file relative to the compile time 62 | // GOPATH separated by \n\t (\n\t) 63 | // %+v equivalent to %+s:%d 64 | func (f Frame) Format(s fmt.State, verb rune) { 65 | switch verb { 66 | case 's': 67 | switch { 68 | case s.Flag('+'): 69 | io.WriteString(s, f.name()) 70 | io.WriteString(s, "\n\t") 71 | io.WriteString(s, f.file()) 72 | default: 73 | io.WriteString(s, path.Base(f.file())) 74 | } 75 | case 'd': 76 | io.WriteString(s, strconv.Itoa(f.line())) 77 | case 'n': 78 | io.WriteString(s, funcname(f.name())) 79 | case 'v': 80 | f.Format(s, 's') 81 | io.WriteString(s, ":") 82 | f.Format(s, 'd') 83 | } 84 | } 85 | 86 | // MarshalText formats a stacktrace Frame as a text string. The output is the 87 | // same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. 88 | func (f Frame) MarshalText() ([]byte, error) { 89 | name := f.name() 90 | if name == "unknown" { 91 | return []byte(name), nil 92 | } 93 | return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil 94 | } 95 | 96 | // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). 97 | type StackTrace []Frame 98 | 99 | // Format formats the stack of Frames according to the fmt.Formatter interface. 100 | // 101 | // %s lists source files for each Frame in the stack 102 | // %v lists the source file and line number for each Frame in the stack 103 | // 104 | // Format accepts flags that alter the printing of some verbs, as follows: 105 | // 106 | // %+v Prints filename, function, and line number for each Frame in the stack. 107 | func (st StackTrace) Format(s fmt.State, verb rune) { 108 | switch verb { 109 | case 'v': 110 | switch { 111 | case s.Flag('+'): 112 | for _, f := range st { 113 | io.WriteString(s, "\n") 114 | f.Format(s, verb) 115 | } 116 | case s.Flag('#'): 117 | fmt.Fprintf(s, "%#v", []Frame(st)) 118 | default: 119 | st.formatSlice(s, verb) 120 | } 121 | case 's': 122 | st.formatSlice(s, verb) 123 | } 124 | } 125 | 126 | // formatSlice will format this StackTrace into the given buffer as a slice of 127 | // Frame, only valid when called with '%s' or '%v'. 128 | func (st StackTrace) formatSlice(s fmt.State, verb rune) { 129 | io.WriteString(s, "[") 130 | for i, f := range st { 131 | if i > 0 { 132 | io.WriteString(s, " ") 133 | } 134 | f.Format(s, verb) 135 | } 136 | io.WriteString(s, "]") 137 | } 138 | 139 | // stack represents a stack of program counters. 140 | type stack []uintptr 141 | 142 | func (s *stack) Format(st fmt.State, verb rune) { 143 | switch verb { 144 | case 'v': 145 | switch { 146 | case st.Flag('+'): 147 | for _, pc := range *s { 148 | f := Frame(pc) 149 | fmt.Fprintf(st, "\n%+v", f) 150 | } 151 | } 152 | } 153 | } 154 | 155 | func (s *stack) StackTrace() StackTrace { 156 | f := make([]Frame, len(*s)) 157 | for i := 0; i < len(f); i++ { 158 | f[i] = Frame((*s)[i]) 159 | } 160 | return f 161 | } 162 | 163 | func callers() *stack { 164 | const depth = 32 165 | var pcs [depth]uintptr 166 | n := runtime.Callers(3, pcs[:]) 167 | var st stack = pcs[0:n] 168 | return &st 169 | } 170 | 171 | // funcname removes the path prefix component of a function's name reported by func.Name(). 172 | func funcname(name string) string { 173 | i := strings.LastIndex(name, "/") 174 | name = name[i+1:] 175 | i = strings.Index(name, ".") 176 | return name[i+1:] 177 | } 178 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/markets.go: -------------------------------------------------------------------------------- 1 | package goftx 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "github.com/grishinsana/goftx/models" 11 | ) 12 | 13 | const ( 14 | apiGetMarkets = "/markets" 15 | apiGetOrderBook = "/markets/%s/orderbook" 16 | apiGetTrades = "/markets/%s/trades" 17 | apiGetHistoricalPrices = "/markets/%s/candles" 18 | apiGetLastCandle = "/markets/%s/candles/last" 19 | ) 20 | 21 | type Markets struct { 22 | client *Client 23 | } 24 | 25 | func (m *Markets) GetMarkets() ([]*models.Market, error) { 26 | request, err := m.client.prepareRequest(Request{ 27 | Method: http.MethodGet, 28 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetMarkets), 29 | }) 30 | if err != nil { 31 | return nil, errors.WithStack(err) 32 | } 33 | 34 | response, err := m.client.do(request) 35 | if err != nil { 36 | return nil, errors.WithStack(err) 37 | } 38 | 39 | var result []*models.Market 40 | err = json.Unmarshal(response, &result) 41 | if err != nil { 42 | return nil, errors.WithStack(err) 43 | } 44 | 45 | return result, nil 46 | } 47 | 48 | func (m *Markets) GetMarketByName(name string) (*models.Market, error) { 49 | request, err := m.client.prepareRequest(Request{ 50 | Method: http.MethodGet, 51 | URL: fmt.Sprintf("%s%s/%s", apiUrl, apiGetMarkets, name), 52 | }) 53 | if err != nil { 54 | return nil, errors.WithStack(err) 55 | } 56 | 57 | response, err := m.client.do(request) 58 | if err != nil { 59 | return nil, errors.WithStack(err) 60 | } 61 | 62 | var result models.Market 63 | err = json.Unmarshal(response, &result) 64 | if err != nil { 65 | return nil, errors.WithStack(err) 66 | } 67 | 68 | return &result, nil 69 | } 70 | 71 | func (m *Markets) GetOrderBook(marketName string, depth *int) (*models.OrderBook, error) { 72 | params := map[string]string{} 73 | if depth != nil { 74 | params["depth"] = fmt.Sprintf("%d", *depth) 75 | } 76 | 77 | path := fmt.Sprintf(apiGetOrderBook, marketName) 78 | 79 | request, err := m.client.prepareRequest(Request{ 80 | Method: http.MethodGet, 81 | URL: fmt.Sprintf("%s%s", apiUrl, path), 82 | Params: params, 83 | }) 84 | if err != nil { 85 | return nil, errors.WithStack(err) 86 | } 87 | 88 | response, err := m.client.do(request) 89 | if err != nil { 90 | return nil, errors.WithStack(err) 91 | } 92 | 93 | var result models.OrderBook 94 | err = json.Unmarshal(response, &result) 95 | if err != nil { 96 | return nil, errors.WithStack(err) 97 | } 98 | 99 | return &result, nil 100 | } 101 | 102 | func (m *Markets) GetTrades(marketName string, params *models.GetTradesParams) ([]*models.Trade, error) { 103 | queryParams, err := PrepareQueryParams(params) 104 | if err != nil { 105 | return nil, errors.WithStack(err) 106 | } 107 | 108 | path := fmt.Sprintf(apiGetTrades, marketName) 109 | request, err := m.client.prepareRequest(Request{ 110 | Method: http.MethodGet, 111 | URL: fmt.Sprintf("%s%s", apiUrl, path), 112 | Params: queryParams, 113 | }) 114 | if err != nil { 115 | return nil, errors.WithStack(err) 116 | } 117 | 118 | response, err := m.client.do(request) 119 | if err != nil { 120 | return nil, errors.WithStack(err) 121 | } 122 | 123 | var result []*models.Trade 124 | err = json.Unmarshal(response, &result) 125 | if err != nil { 126 | return nil, errors.WithStack(err) 127 | } 128 | 129 | return result, nil 130 | } 131 | 132 | func (m *Markets) GetHistoricalPrices(marketName string, params *models.GetHistoricalPricesParams) ([]*models.HistoricalPrice, error) { 133 | queryParams, err := PrepareQueryParams(params) 134 | if err != nil { 135 | return nil, errors.WithStack(err) 136 | } 137 | 138 | path := fmt.Sprintf(apiGetHistoricalPrices, marketName) 139 | request, err := m.client.prepareRequest(Request{ 140 | Method: http.MethodGet, 141 | URL: fmt.Sprintf("%s%s", apiUrl, path), 142 | Params: queryParams, 143 | }) 144 | if err != nil { 145 | return nil, errors.WithStack(err) 146 | } 147 | 148 | response, err := m.client.do(request) 149 | if err != nil { 150 | return nil, errors.WithStack(err) 151 | } 152 | 153 | var result []*models.HistoricalPrice 154 | err = json.Unmarshal(response, &result) 155 | if err != nil { 156 | return nil, errors.WithStack(err) 157 | } 158 | 159 | return result, nil 160 | } 161 | 162 | func (m *Markets) GetLastCandle(marketName string, params *models.GetLastCandleParams) (*models.HistoricalPrice, error) { 163 | queryParams, err := PrepareQueryParams(params) 164 | if err != nil { 165 | return nil, errors.WithStack(err) 166 | } 167 | 168 | path := fmt.Sprintf(apiGetLastCandle, marketName) 169 | request, err := m.client.prepareRequest(Request{ 170 | Method: http.MethodGet, 171 | URL: fmt.Sprintf("%s%s", apiUrl, path), 172 | Params: queryParams, 173 | }) 174 | if err != nil { 175 | return nil, errors.WithStack(err) 176 | } 177 | 178 | response, err := m.client.do(request) 179 | if err != nil { 180 | return nil, errors.WithStack(err) 181 | } 182 | 183 | var result *models.HistoricalPrice 184 | err = json.Unmarshal(response, &result) 185 | if err != nil { 186 | return nil, errors.WithStack(err) 187 | } 188 | 189 | return result, nil 190 | } 191 | -------------------------------------------------------------------------------- /vendor/github.com/shopspring/decimal/README.md: -------------------------------------------------------------------------------- 1 | # decimal 2 | 3 | [![Build Status](https://travis-ci.org/shopspring/decimal.png?branch=master)](https://travis-ci.org/shopspring/decimal) [![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal) [![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal) 4 | 5 | Arbitrary-precision fixed-point decimal numbers in go. 6 | 7 | _Note:_ Decimal library can "only" represent numbers with a maximum of 2^31 digits after the decimal point. 8 | 9 | ## Features 10 | 11 | * The zero-value is 0, and is safe to use without initialization 12 | * Addition, subtraction, multiplication with no loss of precision 13 | * Division with specified precision 14 | * Database/sql serialization/deserialization 15 | * JSON and XML serialization/deserialization 16 | 17 | ## Install 18 | 19 | Run `go get github.com/shopspring/decimal` 20 | 21 | ## Requirements 22 | 23 | Decimal library requires Go version `>=1.7` 24 | 25 | ## Usage 26 | 27 | ```go 28 | package main 29 | 30 | import ( 31 | "fmt" 32 | "github.com/shopspring/decimal" 33 | ) 34 | 35 | func main() { 36 | price, err := decimal.NewFromString("136.02") 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | quantity := decimal.NewFromInt(3) 42 | 43 | fee, _ := decimal.NewFromString(".035") 44 | taxRate, _ := decimal.NewFromString(".08875") 45 | 46 | subtotal := price.Mul(quantity) 47 | 48 | preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1))) 49 | 50 | total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1))) 51 | 52 | fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06 53 | fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421 54 | fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375 55 | fmt.Println("Total:", total) // Total: 459.824961375 56 | fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875 57 | } 58 | ``` 59 | 60 | ## Documentation 61 | 62 | http://godoc.org/github.com/shopspring/decimal 63 | 64 | ## Production Usage 65 | 66 | * [Spring](https://shopspring.com/), since August 14, 2014. 67 | * If you are using this in production, please let us know! 68 | 69 | ## FAQ 70 | 71 | #### Why don't you just use float64? 72 | 73 | Because float64 (or any binary floating point type, actually) can't represent 74 | numbers such as `0.1` exactly. 75 | 76 | Consider this code: http://play.golang.org/p/TQBd4yJe6B You might expect that 77 | it prints out `10`, but it actually prints `9.999999999999831`. Over time, 78 | these small errors can really add up! 79 | 80 | #### Why don't you just use big.Rat? 81 | 82 | big.Rat is fine for representing rational numbers, but Decimal is better for 83 | representing money. Why? Here's a (contrived) example: 84 | 85 | Let's say you use big.Rat, and you have two numbers, x and y, both 86 | representing 1/3, and you have `z = 1 - x - y = 1/3`. If you print each one 87 | out, the string output has to stop somewhere (let's say it stops at 3 decimal 88 | digits, for simplicity), so you'll get 0.333, 0.333, and 0.333. But where did 89 | the other 0.001 go? 90 | 91 | Here's the above example as code: http://play.golang.org/p/lCZZs0w9KE 92 | 93 | With Decimal, the strings being printed out represent the number exactly. So, 94 | if you have `x = y = 1/3` (with precision 3), they will actually be equal to 95 | 0.333, and when you do `z = 1 - x - y`, `z` will be equal to .334. No money is 96 | unaccounted for! 97 | 98 | You still have to be careful. If you want to split a number `N` 3 ways, you 99 | can't just send `N/3` to three different people. You have to pick one to send 100 | `N - (2/3*N)` to. That person will receive the fraction of a penny remainder. 101 | 102 | But, it is much easier to be careful with Decimal than with big.Rat. 103 | 104 | #### Why isn't the API similar to big.Int's? 105 | 106 | big.Int's API is built to reduce the number of memory allocations for maximal 107 | performance. This makes sense for its use-case, but the trade-off is that the 108 | API is awkward and easy to misuse. 109 | 110 | For example, to add two big.Ints, you do: `z := new(big.Int).Add(x, y)`. A 111 | developer unfamiliar with this API might try to do `z := a.Add(a, b)`. This 112 | modifies `a` and sets `z` as an alias for `a`, which they might not expect. It 113 | also modifies any other aliases to `a`. 114 | 115 | Here's an example of the subtle bugs you can introduce with big.Int's API: 116 | https://play.golang.org/p/x2R_78pa8r 117 | 118 | In contrast, it's difficult to make such mistakes with decimal. Decimals 119 | behave like other go numbers types: even though `a = b` will not deep copy 120 | `b` into `a`, it is impossible to modify a Decimal, since all Decimal methods 121 | return new Decimals and do not modify the originals. The downside is that 122 | this causes extra allocations, so Decimal is less performant. My assumption 123 | is that if you're using Decimals, you probably care more about correctness 124 | than performance. 125 | 126 | ## License 127 | 128 | The MIT License (MIT) 129 | 130 | This is a heavily modified fork of [fpd.Decimal](https://github.com/oguzbilgic/fpd), which was also released under the MIT License. 131 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/spec.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "time" 4 | 5 | // SpecSchedule specifies a duty cycle (to the second granularity), based on a 6 | // traditional crontab specification. It is computed initially and stored as bit sets. 7 | type SpecSchedule struct { 8 | Second, Minute, Hour, Dom, Month, Dow uint64 9 | 10 | // Override location for this schedule. 11 | Location *time.Location 12 | } 13 | 14 | // bounds provides a range of acceptable values (plus a map of name to value). 15 | type bounds struct { 16 | min, max uint 17 | names map[string]uint 18 | } 19 | 20 | // The bounds for each field. 21 | var ( 22 | seconds = bounds{0, 59, nil} 23 | minutes = bounds{0, 59, nil} 24 | hours = bounds{0, 23, nil} 25 | dom = bounds{1, 31, nil} 26 | months = bounds{1, 12, map[string]uint{ 27 | "jan": 1, 28 | "feb": 2, 29 | "mar": 3, 30 | "apr": 4, 31 | "may": 5, 32 | "jun": 6, 33 | "jul": 7, 34 | "aug": 8, 35 | "sep": 9, 36 | "oct": 10, 37 | "nov": 11, 38 | "dec": 12, 39 | }} 40 | dow = bounds{0, 6, map[string]uint{ 41 | "sun": 0, 42 | "mon": 1, 43 | "tue": 2, 44 | "wed": 3, 45 | "thu": 4, 46 | "fri": 5, 47 | "sat": 6, 48 | }} 49 | ) 50 | 51 | const ( 52 | // Set the top bit if a star was included in the expression. 53 | starBit = 1 << 63 54 | ) 55 | 56 | // Next returns the next time this schedule is activated, greater than the given 57 | // time. If no time can be found to satisfy the schedule, return the zero time. 58 | func (s *SpecSchedule) Next(t time.Time) time.Time { 59 | // General approach 60 | // 61 | // For Month, Day, Hour, Minute, Second: 62 | // Check if the time value matches. If yes, continue to the next field. 63 | // If the field doesn't match the schedule, then increment the field until it matches. 64 | // While incrementing the field, a wrap-around brings it back to the beginning 65 | // of the field list (since it is necessary to re-verify previous field 66 | // values) 67 | 68 | // Convert the given time into the schedule's timezone, if one is specified. 69 | // Save the original timezone so we can convert back after we find a time. 70 | // Note that schedules without a time zone specified (time.Local) are treated 71 | // as local to the time provided. 72 | origLocation := t.Location() 73 | loc := s.Location 74 | if loc == time.Local { 75 | loc = t.Location() 76 | } 77 | if s.Location != time.Local { 78 | t = t.In(s.Location) 79 | } 80 | 81 | // Start at the earliest possible time (the upcoming second). 82 | t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond) 83 | 84 | // This flag indicates whether a field has been incremented. 85 | added := false 86 | 87 | // If no time is found within five years, return zero. 88 | yearLimit := t.Year() + 5 89 | 90 | WRAP: 91 | if t.Year() > yearLimit { 92 | return time.Time{} 93 | } 94 | 95 | // Find the first applicable month. 96 | // If it's this month, then do nothing. 97 | for 1< 12 { 127 | t = t.Add(time.Duration(24-t.Hour()) * time.Hour) 128 | } else { 129 | t = t.Add(time.Duration(-t.Hour()) * time.Hour) 130 | } 131 | } 132 | 133 | if t.Day() == 1 { 134 | goto WRAP 135 | } 136 | } 137 | 138 | for 1< 0 182 | dowMatch bool = 1< 0 183 | ) 184 | if s.Dom&starBit > 0 || s.Dow&starBit > 0 { 185 | return domMatch && dowMatch 186 | } 187 | return domMatch || dowMatch 188 | } 189 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/options.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | import ( 4 | "context" 5 | "math" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // Function signature of retry if function 11 | type RetryIfFunc func(error) bool 12 | 13 | // Function signature of OnRetry function 14 | // n = count of attempts 15 | type OnRetryFunc func(n uint, err error) 16 | 17 | // DelayTypeFunc is called to return the next delay to wait after the retriable function fails on `err` after `n` attempts. 18 | type DelayTypeFunc func(n uint, err error, config *Config) time.Duration 19 | 20 | type Config struct { 21 | attempts uint 22 | delay time.Duration 23 | maxDelay time.Duration 24 | maxJitter time.Duration 25 | onRetry OnRetryFunc 26 | retryIf RetryIfFunc 27 | delayType DelayTypeFunc 28 | lastErrorOnly bool 29 | context context.Context 30 | 31 | maxBackOffN uint 32 | } 33 | 34 | // Option represents an option for retry. 35 | type Option func(*Config) 36 | 37 | // return the direct last error that came from the retried function 38 | // default is false (return wrapped errors with everything) 39 | func LastErrorOnly(lastErrorOnly bool) Option { 40 | return func(c *Config) { 41 | c.lastErrorOnly = lastErrorOnly 42 | } 43 | } 44 | 45 | // Attempts set count of retry 46 | // default is 10 47 | func Attempts(attempts uint) Option { 48 | return func(c *Config) { 49 | c.attempts = attempts 50 | } 51 | } 52 | 53 | // Delay set delay between retry 54 | // default is 100ms 55 | func Delay(delay time.Duration) Option { 56 | return func(c *Config) { 57 | c.delay = delay 58 | } 59 | } 60 | 61 | // MaxDelay set maximum delay between retry 62 | // does not apply by default 63 | func MaxDelay(maxDelay time.Duration) Option { 64 | return func(c *Config) { 65 | c.maxDelay = maxDelay 66 | } 67 | } 68 | 69 | // MaxJitter sets the maximum random Jitter between retries for RandomDelay 70 | func MaxJitter(maxJitter time.Duration) Option { 71 | return func(c *Config) { 72 | c.maxJitter = maxJitter 73 | } 74 | } 75 | 76 | // DelayType set type of the delay between retries 77 | // default is BackOff 78 | func DelayType(delayType DelayTypeFunc) Option { 79 | return func(c *Config) { 80 | c.delayType = delayType 81 | } 82 | } 83 | 84 | // BackOffDelay is a DelayType which increases delay between consecutive retries 85 | func BackOffDelay(n uint, _ error, config *Config) time.Duration { 86 | // 1 << 63 would overflow signed int64 (time.Duration), thus 62. 87 | const max uint = 62 88 | 89 | if config.maxBackOffN == 0 { 90 | if config.delay <= 0 { 91 | config.delay = 1 92 | } 93 | 94 | config.maxBackOffN = max - uint(math.Floor(math.Log2(float64(config.delay)))) 95 | } 96 | 97 | if n > config.maxBackOffN { 98 | n = config.maxBackOffN 99 | } 100 | 101 | return config.delay << n 102 | } 103 | 104 | // FixedDelay is a DelayType which keeps delay the same through all iterations 105 | func FixedDelay(_ uint, _ error, config *Config) time.Duration { 106 | return config.delay 107 | } 108 | 109 | // RandomDelay is a DelayType which picks a random delay up to config.maxJitter 110 | func RandomDelay(_ uint, _ error, config *Config) time.Duration { 111 | return time.Duration(rand.Int63n(int64(config.maxJitter))) 112 | } 113 | 114 | // CombineDelay is a DelayType the combines all of the specified delays into a new DelayTypeFunc 115 | func CombineDelay(delays ...DelayTypeFunc) DelayTypeFunc { 116 | const maxInt64 = uint64(math.MaxInt64) 117 | 118 | return func(n uint, err error, config *Config) time.Duration { 119 | var total uint64 120 | for _, delay := range delays { 121 | total += uint64(delay(n, err, config)) 122 | if total > maxInt64 { 123 | total = maxInt64 124 | } 125 | } 126 | 127 | return time.Duration(total) 128 | } 129 | } 130 | 131 | // OnRetry function callback are called each retry 132 | // 133 | // log each retry example: 134 | // 135 | // retry.Do( 136 | // func() error { 137 | // return errors.New("some error") 138 | // }, 139 | // retry.OnRetry(func(n uint, err error) { 140 | // log.Printf("#%d: %s\n", n, err) 141 | // }), 142 | // ) 143 | func OnRetry(onRetry OnRetryFunc) Option { 144 | return func(c *Config) { 145 | c.onRetry = onRetry 146 | } 147 | } 148 | 149 | // RetryIf controls whether a retry should be attempted after an error 150 | // (assuming there are any retry attempts remaining) 151 | // 152 | // skip retry if special error example: 153 | // 154 | // retry.Do( 155 | // func() error { 156 | // return errors.New("special error") 157 | // }, 158 | // retry.RetryIf(func(err error) bool { 159 | // if err.Error() == "special error" { 160 | // return false 161 | // } 162 | // return true 163 | // }) 164 | // ) 165 | // 166 | // By default RetryIf stops execution if the error is wrapped using `retry.Unrecoverable`, 167 | // so above example may also be shortened to: 168 | // 169 | // retry.Do( 170 | // func() error { 171 | // return retry.Unrecoverable(errors.New("special error")) 172 | // } 173 | // ) 174 | func RetryIf(retryIf RetryIfFunc) Option { 175 | return func(c *Config) { 176 | c.retryIf = retryIf 177 | } 178 | } 179 | 180 | // Context allow to set context of retry 181 | // default are Background context 182 | // 183 | // example of immediately cancellation (maybe it isn't the best example, but it describes behavior enough; I hope) 184 | // 185 | // ctx, cancel := context.WithCancel(context.Background()) 186 | // cancel() 187 | // 188 | // retry.Do( 189 | // func() error { 190 | // ... 191 | // }, 192 | // retry.Context(ctx), 193 | // ) 194 | func Context(ctx context.Context) Option { 195 | return func(c *Config) { 196 | c.context = ctx 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron) 2 | [![Build Status](https://travis-ci.org/robfig/cron.svg?branch=master)](https://travis-ci.org/robfig/cron) 3 | 4 | # cron 5 | 6 | Cron V3 has been released! 7 | 8 | To download the specific tagged release, run: 9 | 10 | go get github.com/robfig/cron/v3@v3.0.0 11 | 12 | Import it in your program as: 13 | 14 | import "github.com/robfig/cron/v3" 15 | 16 | It requires Go 1.11 or later due to usage of Go Modules. 17 | 18 | Refer to the documentation here: 19 | http://godoc.org/github.com/robfig/cron 20 | 21 | The rest of this document describes the the advances in v3 and a list of 22 | breaking changes for users that wish to upgrade from an earlier version. 23 | 24 | ## Upgrading to v3 (June 2019) 25 | 26 | cron v3 is a major upgrade to the library that addresses all outstanding bugs, 27 | feature requests, and rough edges. It is based on a merge of master which 28 | contains various fixes to issues found over the years and the v2 branch which 29 | contains some backwards-incompatible features like the ability to remove cron 30 | jobs. In addition, v3 adds support for Go Modules, cleans up rough edges like 31 | the timezone support, and fixes a number of bugs. 32 | 33 | New features: 34 | 35 | - Support for Go modules. Callers must now import this library as 36 | `github.com/robfig/cron/v3`, instead of `gopkg.in/...` 37 | 38 | - Fixed bugs: 39 | - 0f01e6b parser: fix combining of Dow and Dom (#70) 40 | - dbf3220 adjust times when rolling the clock forward to handle non-existent midnight (#157) 41 | - eeecf15 spec_test.go: ensure an error is returned on 0 increment (#144) 42 | - 70971dc cron.Entries(): update request for snapshot to include a reply channel (#97) 43 | - 1cba5e6 cron: fix: removing a job causes the next scheduled job to run too late (#206) 44 | 45 | - Standard cron spec parsing by default (first field is "minute"), with an easy 46 | way to opt into the seconds field (quartz-compatible). Although, note that the 47 | year field (optional in Quartz) is not supported. 48 | 49 | - Extensible, key/value logging via an interface that complies with 50 | the https://github.com/go-logr/logr project. 51 | 52 | - The new Chain & JobWrapper types allow you to install "interceptors" to add 53 | cross-cutting behavior like the following: 54 | - Recover any panics from jobs 55 | - Delay a job's execution if the previous run hasn't completed yet 56 | - Skip a job's execution if the previous run hasn't completed yet 57 | - Log each job's invocations 58 | - Notification when jobs are completed 59 | 60 | It is backwards incompatible with both v1 and v2. These updates are required: 61 | 62 | - The v1 branch accepted an optional seconds field at the beginning of the cron 63 | spec. This is non-standard and has led to a lot of confusion. The new default 64 | parser conforms to the standard as described by [the Cron wikipedia page]. 65 | 66 | UPDATING: To retain the old behavior, construct your Cron with a custom 67 | parser: 68 | 69 | // Seconds field, required 70 | cron.New(cron.WithSeconds()) 71 | 72 | // Seconds field, optional 73 | cron.New( 74 | cron.WithParser( 75 | cron.SecondOptional | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)) 76 | 77 | - The Cron type now accepts functional options on construction rather than the 78 | previous ad-hoc behavior modification mechanisms (setting a field, calling a setter). 79 | 80 | UPDATING: Code that sets Cron.ErrorLogger or calls Cron.SetLocation must be 81 | updated to provide those values on construction. 82 | 83 | - CRON_TZ is now the recommended way to specify the timezone of a single 84 | schedule, which is sanctioned by the specification. The legacy "TZ=" prefix 85 | will continue to be supported since it is unambiguous and easy to do so. 86 | 87 | UPDATING: No update is required. 88 | 89 | - By default, cron will no longer recover panics in jobs that it runs. 90 | Recovering can be surprising (see issue #192) and seems to be at odds with 91 | typical behavior of libraries. Relatedly, the `cron.WithPanicLogger` option 92 | has been removed to accommodate the more general JobWrapper type. 93 | 94 | UPDATING: To opt into panic recovery and configure the panic logger: 95 | 96 | cron.New(cron.WithChain( 97 | cron.Recover(logger), // or use cron.DefaultLogger 98 | )) 99 | 100 | - In adding support for https://github.com/go-logr/logr, `cron.WithVerboseLogger` was 101 | removed, since it is duplicative with the leveled logging. 102 | 103 | UPDATING: Callers should use `WithLogger` and specify a logger that does not 104 | discard `Info` logs. For convenience, one is provided that wraps `*log.Logger`: 105 | 106 | cron.New( 107 | cron.WithLogger(cron.VerbosePrintfLogger(logger))) 108 | 109 | 110 | ### Background - Cron spec format 111 | 112 | There are two cron spec formats in common usage: 113 | 114 | - The "standard" cron format, described on [the Cron wikipedia page] and used by 115 | the cron Linux system utility. 116 | 117 | - The cron format used by [the Quartz Scheduler], commonly used for scheduled 118 | jobs in Java software 119 | 120 | [the Cron wikipedia page]: https://en.wikipedia.org/wiki/Cron 121 | [the Quartz Scheduler]: http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html 122 | 123 | The original version of this package included an optional "seconds" field, which 124 | made it incompatible with both of these formats. Now, the "standard" format is 125 | the default format accepted, and the Quartz format is opt-in. 126 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/client.go: -------------------------------------------------------------------------------- 1 | package goftx 2 | 3 | import ( 4 | "bytes" 5 | "crypto/hmac" 6 | "crypto/sha256" 7 | "encoding/hex" 8 | "encoding/json" 9 | "fmt" 10 | "io/ioutil" 11 | "net/http" 12 | "reflect" 13 | "strconv" 14 | "sync" 15 | "time" 16 | 17 | "github.com/gorilla/websocket" 18 | "github.com/pkg/errors" 19 | ) 20 | 21 | const ( 22 | apiUrl = "https://ftx.com/api" 23 | apiOtcUrl = "https://otc.ftx.com/api" 24 | 25 | keyHeader = "FTX-KEY" 26 | signHeader = "FTX-SIGN" 27 | tsHeader = "FTX-TS" 28 | subAccountHeader = "FTX-SUBACCOUNT" 29 | ) 30 | 31 | type Option func(c *Client) 32 | 33 | func WithHTTPClient(client *http.Client) Option { 34 | return func(c *Client) { 35 | c.client = client 36 | } 37 | } 38 | 39 | func WithAuth(key, secret string) Option { 40 | return func(c *Client) { 41 | c.apiKey = key 42 | c.secret = secret 43 | c.Stream.apiKey = key 44 | c.Stream.secret = secret 45 | } 46 | } 47 | 48 | func WithSubaccount(subAccount string) Option { 49 | return func(c *Client) { 50 | c.subAccount = subAccount 51 | } 52 | } 53 | 54 | type Client struct { 55 | client *http.Client 56 | apiKey string 57 | secret string 58 | subAccount string 59 | serverTimeDiff time.Duration 60 | SubAccounts 61 | Markets 62 | Account 63 | Stream 64 | Orders 65 | SpotMargin 66 | } 67 | 68 | func New(opts ...Option) *Client { 69 | client := &Client{ 70 | client: http.DefaultClient, 71 | } 72 | 73 | for _, opt := range opts { 74 | opt(client) 75 | } 76 | 77 | client.SubAccounts = SubAccounts{client: client} 78 | client.Markets = Markets{client: client} 79 | client.Account = Account{client: client} 80 | client.Orders = Orders{client: client} 81 | client.SpotMargin = SpotMargin{client: client} 82 | client.Stream = Stream{ 83 | apiKey: client.apiKey, 84 | secret: client.secret, 85 | subAccount: client.subAccount, 86 | mu: &sync.Mutex{}, 87 | url: wsUrl, 88 | dialer: websocket.DefaultDialer, 89 | wsReconnectionCount: reconnectCount, 90 | wsReconnectionInterval: reconnectInterval, 91 | wsTimeout: streamTimeout, 92 | } 93 | 94 | return client 95 | } 96 | 97 | func (c *Client) SetServerTimeDiff() error { 98 | serverTime, err := c.GetServerTime() 99 | if err != nil { 100 | return errors.WithStack(err) 101 | } 102 | c.serverTimeDiff = serverTime.Sub(time.Now().UTC()) 103 | return nil 104 | } 105 | 106 | type Response struct { 107 | Success bool `json:"success"` 108 | Result json.RawMessage `json:"result"` 109 | Error string `json:"error,omitempty"` 110 | } 111 | 112 | type Request struct { 113 | Auth bool 114 | Method string 115 | URL string 116 | Headers map[string]string 117 | Params map[string]string 118 | Body []byte 119 | } 120 | 121 | func (c *Client) prepareRequest(request Request) (*http.Request, error) { 122 | req, err := http.NewRequest(request.Method, request.URL, bytes.NewBuffer(request.Body)) 123 | if err != nil { 124 | return nil, errors.WithStack(err) 125 | } 126 | 127 | query := req.URL.Query() 128 | for k, v := range request.Params { 129 | query.Add(k, v) 130 | } 131 | req.URL.RawQuery = query.Encode() 132 | 133 | if request.Auth { 134 | nonce := strconv.FormatInt(time.Now().UTC().Add(c.serverTimeDiff).Unix()*1000, 10) 135 | payload := nonce + req.Method + req.URL.Path 136 | if req.URL.RawQuery != "" { 137 | payload += "?" + req.URL.RawQuery 138 | } 139 | if len(request.Body) > 0 { 140 | payload += string(request.Body) 141 | } 142 | 143 | req.Header.Set("Content-Type", "application/json") 144 | req.Header.Set(keyHeader, c.apiKey) 145 | req.Header.Set(signHeader, c.signture(payload)) 146 | req.Header.Set(tsHeader, nonce) 147 | 148 | if c.subAccount != "" { 149 | req.Header.Set(subAccountHeader, c.subAccount) 150 | } 151 | } 152 | 153 | for k, v := range request.Headers { 154 | req.Header.Set(k, v) 155 | } 156 | 157 | return req, nil 158 | } 159 | 160 | func (c *Client) do(req *http.Request) ([]byte, error) { 161 | resp, err := c.client.Do(req) 162 | if resp != nil { 163 | defer resp.Body.Close() 164 | } 165 | if err != nil { 166 | return nil, errors.WithStack(err) 167 | } 168 | 169 | res, err := ioutil.ReadAll(resp.Body) 170 | if err != nil { 171 | return nil, errors.WithStack(err) 172 | } 173 | 174 | var response Response 175 | err = json.Unmarshal(res, &response) 176 | if err != nil { 177 | return nil, errors.WithStack(err) 178 | } 179 | 180 | if !response.Success { 181 | return nil, errors.Errorf("Status Code: %d Error: %v", resp.StatusCode, response.Error) 182 | } 183 | 184 | return response.Result, nil 185 | } 186 | 187 | func (c *Client) prepareQueryParams(params interface{}) map[string]string { 188 | result := make(map[string]string) 189 | 190 | val := reflect.ValueOf(params).Elem() 191 | for i := 0; i < val.NumField(); i++ { 192 | valueField := val.Field(i) 193 | typeField := val.Type().Field(i) 194 | tag := typeField.Tag 195 | 196 | result[tag.Get("json")] = valueField.String() 197 | } 198 | 199 | return result 200 | } 201 | 202 | func (c *Client) signture(payload string) string { 203 | mac := hmac.New(sha256.New, []byte(c.secret)) 204 | mac.Write([]byte(payload)) 205 | return hex.EncodeToString(mac.Sum(nil)) 206 | } 207 | 208 | func (c *Client) GetServerTime() (*time.Time, error) { 209 | request, err := c.prepareRequest(Request{ 210 | Method: http.MethodGet, 211 | URL: fmt.Sprintf("%s/time", apiOtcUrl), 212 | }) 213 | if err != nil { 214 | return nil, errors.WithStack(err) 215 | } 216 | 217 | response, err := c.do(request) 218 | if err != nil { 219 | return nil, errors.WithStack(err) 220 | } 221 | 222 | var result time.Time 223 | err = json.Unmarshal(response, &result) 224 | if err != nil { 225 | return nil, errors.WithStack(err) 226 | } 227 | 228 | return &result, nil 229 | } 230 | -------------------------------------------------------------------------------- /vendor/github.com/avast/retry-go/retry.go: -------------------------------------------------------------------------------- 1 | /* 2 | Simple library for retry mechanism 3 | 4 | slightly inspired by [Try::Tiny::Retry](https://metacpan.org/pod/Try::Tiny::Retry) 5 | 6 | SYNOPSIS 7 | 8 | http get with retry: 9 | 10 | url := "http://example.com" 11 | var body []byte 12 | 13 | err := retry.Do( 14 | func() error { 15 | resp, err := http.Get(url) 16 | if err != nil { 17 | return err 18 | } 19 | defer resp.Body.Close() 20 | body, err = ioutil.ReadAll(resp.Body) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | return nil 26 | }, 27 | ) 28 | 29 | fmt.Println(body) 30 | 31 | [next examples](https://github.com/avast/retry-go/tree/master/examples) 32 | 33 | 34 | SEE ALSO 35 | 36 | * [giantswarm/retry-go](https://github.com/giantswarm/retry-go) - slightly complicated interface. 37 | 38 | * [sethgrid/pester](https://github.com/sethgrid/pester) - only http retry for http calls with retries and backoff 39 | 40 | * [cenkalti/backoff](https://github.com/cenkalti/backoff) - Go port of the exponential backoff algorithm from Google's HTTP Client Library for Java. Really complicated interface. 41 | 42 | * [rafaeljesus/retry-go](https://github.com/rafaeljesus/retry-go) - looks good, slightly similar as this package, don't have 'simple' `Retry` method 43 | 44 | * [matryer/try](https://github.com/matryer/try) - very popular package, nonintuitive interface (for me) 45 | 46 | 47 | BREAKING CHANGES 48 | 49 | 3.0.0 50 | 51 | * `DelayTypeFunc` accepts a new parameter `err` - this breaking change affects only your custom Delay Functions. This change allow [make delay functions based on error](examples/delay_based_on_error_test.go). 52 | 53 | 54 | 1.0.2 -> 2.0.0 55 | 56 | * argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore) 57 | 58 | * function `retry.Units` are removed 59 | 60 | * [more about this breaking change](https://github.com/avast/retry-go/issues/7) 61 | 62 | 63 | 0.3.0 -> 1.0.0 64 | 65 | * `retry.Retry` function are changed to `retry.Do` function 66 | 67 | * `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`) 68 | 69 | 70 | */ 71 | package retry 72 | 73 | import ( 74 | "context" 75 | "fmt" 76 | "strings" 77 | "time" 78 | ) 79 | 80 | // Function signature of retryable function 81 | type RetryableFunc func() error 82 | 83 | var ( 84 | DefaultAttempts = uint(10) 85 | DefaultDelay = 100 * time.Millisecond 86 | DefaultMaxJitter = 100 * time.Millisecond 87 | DefaultOnRetry = func(n uint, err error) {} 88 | DefaultRetryIf = IsRecoverable 89 | DefaultDelayType = CombineDelay(BackOffDelay, RandomDelay) 90 | DefaultLastErrorOnly = false 91 | DefaultContext = context.Background() 92 | ) 93 | 94 | func Do(retryableFunc RetryableFunc, opts ...Option) error { 95 | var n uint 96 | 97 | //default 98 | config := &Config{ 99 | attempts: DefaultAttempts, 100 | delay: DefaultDelay, 101 | maxJitter: DefaultMaxJitter, 102 | onRetry: DefaultOnRetry, 103 | retryIf: DefaultRetryIf, 104 | delayType: DefaultDelayType, 105 | lastErrorOnly: DefaultLastErrorOnly, 106 | context: DefaultContext, 107 | } 108 | 109 | //apply opts 110 | for _, opt := range opts { 111 | opt(config) 112 | } 113 | 114 | if err := config.context.Err(); err != nil { 115 | return err 116 | } 117 | 118 | var errorLog Error 119 | if !config.lastErrorOnly { 120 | errorLog = make(Error, config.attempts) 121 | } else { 122 | errorLog = make(Error, 1) 123 | } 124 | 125 | lastErrIndex := n 126 | for n < config.attempts { 127 | err := retryableFunc() 128 | 129 | if err != nil { 130 | errorLog[lastErrIndex] = unpackUnrecoverable(err) 131 | 132 | if !config.retryIf(err) { 133 | break 134 | } 135 | 136 | config.onRetry(n, err) 137 | 138 | // if this is last attempt - don't wait 139 | if n == config.attempts-1 { 140 | break 141 | } 142 | 143 | delayTime := config.delayType(n, err, config) 144 | if config.maxDelay > 0 && delayTime > config.maxDelay { 145 | delayTime = config.maxDelay 146 | } 147 | 148 | select { 149 | case <-time.After(delayTime): 150 | case <-config.context.Done(): 151 | return config.context.Err() 152 | } 153 | 154 | } else { 155 | return nil 156 | } 157 | 158 | n++ 159 | if !config.lastErrorOnly { 160 | lastErrIndex = n 161 | } 162 | } 163 | 164 | if config.lastErrorOnly { 165 | return errorLog[lastErrIndex] 166 | } 167 | return errorLog 168 | } 169 | 170 | // Error type represents list of errors in retry 171 | type Error []error 172 | 173 | // Error method return string representation of Error 174 | // It is an implementation of error interface 175 | func (e Error) Error() string { 176 | logWithNumber := make([]string, lenWithoutNil(e)) 177 | for i, l := range e { 178 | if l != nil { 179 | logWithNumber[i] = fmt.Sprintf("#%d: %s", i+1, l.Error()) 180 | } 181 | } 182 | 183 | return fmt.Sprintf("All attempts fail:\n%s", strings.Join(logWithNumber, "\n")) 184 | } 185 | 186 | func lenWithoutNil(e Error) (count int) { 187 | for _, v := range e { 188 | if v != nil { 189 | count++ 190 | } 191 | } 192 | 193 | return 194 | } 195 | 196 | // WrappedErrors returns the list of errors that this Error is wrapping. 197 | // It is an implementation of the `errwrap.Wrapper` interface 198 | // in package [errwrap](https://github.com/hashicorp/errwrap) so that 199 | // `retry.Error` can be used with that library. 200 | func (e Error) WrappedErrors() []error { 201 | return e 202 | } 203 | 204 | type unrecoverableError struct { 205 | error 206 | } 207 | 208 | // Unrecoverable wraps an error in `unrecoverableError` struct 209 | func Unrecoverable(err error) error { 210 | return unrecoverableError{err} 211 | } 212 | 213 | // IsRecoverable checks if error is an instance of `unrecoverableError` 214 | func IsRecoverable(err error) bool { 215 | _, isUnrecoverable := err.(unrecoverableError) 216 | return !isUnrecoverable 217 | } 218 | 219 | func unpackUnrecoverable(err error) error { 220 | if unrecoverable, isUnrecoverable := err.(unrecoverableError); isUnrecoverable { 221 | return unrecoverable.error 222 | } 223 | 224 | return err 225 | } 226 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/sha1" 10 | "encoding/base64" 11 | "io" 12 | "net/http" 13 | "strings" 14 | "unicode/utf8" 15 | ) 16 | 17 | var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 18 | 19 | func computeAcceptKey(challengeKey string) string { 20 | h := sha1.New() 21 | h.Write([]byte(challengeKey)) 22 | h.Write(keyGUID) 23 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 24 | } 25 | 26 | func generateChallengeKey() (string, error) { 27 | p := make([]byte, 16) 28 | if _, err := io.ReadFull(rand.Reader, p); err != nil { 29 | return "", err 30 | } 31 | return base64.StdEncoding.EncodeToString(p), nil 32 | } 33 | 34 | // Token octets per RFC 2616. 35 | var isTokenOctet = [256]bool{ 36 | '!': true, 37 | '#': true, 38 | '$': true, 39 | '%': true, 40 | '&': true, 41 | '\'': true, 42 | '*': true, 43 | '+': true, 44 | '-': true, 45 | '.': true, 46 | '0': true, 47 | '1': true, 48 | '2': true, 49 | '3': true, 50 | '4': true, 51 | '5': true, 52 | '6': true, 53 | '7': true, 54 | '8': true, 55 | '9': true, 56 | 'A': true, 57 | 'B': true, 58 | 'C': true, 59 | 'D': true, 60 | 'E': true, 61 | 'F': true, 62 | 'G': true, 63 | 'H': true, 64 | 'I': true, 65 | 'J': true, 66 | 'K': true, 67 | 'L': true, 68 | 'M': true, 69 | 'N': true, 70 | 'O': true, 71 | 'P': true, 72 | 'Q': true, 73 | 'R': true, 74 | 'S': true, 75 | 'T': true, 76 | 'U': true, 77 | 'W': true, 78 | 'V': true, 79 | 'X': true, 80 | 'Y': true, 81 | 'Z': true, 82 | '^': true, 83 | '_': true, 84 | '`': true, 85 | 'a': true, 86 | 'b': true, 87 | 'c': true, 88 | 'd': true, 89 | 'e': true, 90 | 'f': true, 91 | 'g': true, 92 | 'h': true, 93 | 'i': true, 94 | 'j': true, 95 | 'k': true, 96 | 'l': true, 97 | 'm': true, 98 | 'n': true, 99 | 'o': true, 100 | 'p': true, 101 | 'q': true, 102 | 'r': true, 103 | 's': true, 104 | 't': true, 105 | 'u': true, 106 | 'v': true, 107 | 'w': true, 108 | 'x': true, 109 | 'y': true, 110 | 'z': true, 111 | '|': true, 112 | '~': true, 113 | } 114 | 115 | // skipSpace returns a slice of the string s with all leading RFC 2616 linear 116 | // whitespace removed. 117 | func skipSpace(s string) (rest string) { 118 | i := 0 119 | for ; i < len(s); i++ { 120 | if b := s[i]; b != ' ' && b != '\t' { 121 | break 122 | } 123 | } 124 | return s[i:] 125 | } 126 | 127 | // nextToken returns the leading RFC 2616 token of s and the string following 128 | // the token. 129 | func nextToken(s string) (token, rest string) { 130 | i := 0 131 | for ; i < len(s); i++ { 132 | if !isTokenOctet[s[i]] { 133 | break 134 | } 135 | } 136 | return s[:i], s[i:] 137 | } 138 | 139 | // nextTokenOrQuoted returns the leading token or quoted string per RFC 2616 140 | // and the string following the token or quoted string. 141 | func nextTokenOrQuoted(s string) (value string, rest string) { 142 | if !strings.HasPrefix(s, "\"") { 143 | return nextToken(s) 144 | } 145 | s = s[1:] 146 | for i := 0; i < len(s); i++ { 147 | switch s[i] { 148 | case '"': 149 | return s[:i], s[i+1:] 150 | case '\\': 151 | p := make([]byte, len(s)-1) 152 | j := copy(p, s[:i]) 153 | escape := true 154 | for i = i + 1; i < len(s); i++ { 155 | b := s[i] 156 | switch { 157 | case escape: 158 | escape = false 159 | p[j] = b 160 | j++ 161 | case b == '\\': 162 | escape = true 163 | case b == '"': 164 | return string(p[:j]), s[i+1:] 165 | default: 166 | p[j] = b 167 | j++ 168 | } 169 | } 170 | return "", "" 171 | } 172 | } 173 | return "", "" 174 | } 175 | 176 | // equalASCIIFold returns true if s is equal to t with ASCII case folding as 177 | // defined in RFC 4790. 178 | func equalASCIIFold(s, t string) bool { 179 | for s != "" && t != "" { 180 | sr, size := utf8.DecodeRuneInString(s) 181 | s = s[size:] 182 | tr, size := utf8.DecodeRuneInString(t) 183 | t = t[size:] 184 | if sr == tr { 185 | continue 186 | } 187 | if 'A' <= sr && sr <= 'Z' { 188 | sr = sr + 'a' - 'A' 189 | } 190 | if 'A' <= tr && tr <= 'Z' { 191 | tr = tr + 'a' - 'A' 192 | } 193 | if sr != tr { 194 | return false 195 | } 196 | } 197 | return s == t 198 | } 199 | 200 | // tokenListContainsValue returns true if the 1#token header with the given 201 | // name contains a token equal to value with ASCII case folding. 202 | func tokenListContainsValue(header http.Header, name string, value string) bool { 203 | headers: 204 | for _, s := range header[name] { 205 | for { 206 | var t string 207 | t, s = nextToken(skipSpace(s)) 208 | if t == "" { 209 | continue headers 210 | } 211 | s = skipSpace(s) 212 | if s != "" && s[0] != ',' { 213 | continue headers 214 | } 215 | if equalASCIIFold(t, value) { 216 | return true 217 | } 218 | if s == "" { 219 | continue headers 220 | } 221 | s = s[1:] 222 | } 223 | } 224 | return false 225 | } 226 | 227 | // parseExtensions parses WebSocket extensions from a header. 228 | func parseExtensions(header http.Header) []map[string]string { 229 | // From RFC 6455: 230 | // 231 | // Sec-WebSocket-Extensions = extension-list 232 | // extension-list = 1#extension 233 | // extension = extension-token *( ";" extension-param ) 234 | // extension-token = registered-token 235 | // registered-token = token 236 | // extension-param = token [ "=" (token | quoted-string) ] 237 | // ;When using the quoted-string syntax variant, the value 238 | // ;after quoted-string unescaping MUST conform to the 239 | // ;'token' ABNF. 240 | 241 | var result []map[string]string 242 | headers: 243 | for _, s := range header["Sec-Websocket-Extensions"] { 244 | for { 245 | var t string 246 | t, s = nextToken(skipSpace(s)) 247 | if t == "" { 248 | continue headers 249 | } 250 | ext := map[string]string{"": t} 251 | for { 252 | s = skipSpace(s) 253 | if !strings.HasPrefix(s, ";") { 254 | break 255 | } 256 | var k string 257 | k, s = nextToken(skipSpace(s[1:])) 258 | if k == "" { 259 | continue headers 260 | } 261 | s = skipSpace(s) 262 | var v string 263 | if strings.HasPrefix(s, "=") { 264 | v, s = nextTokenOrQuoted(skipSpace(s[1:])) 265 | s = skipSpace(s) 266 | } 267 | if s != "" && s[0] != ',' && s[0] != ';' { 268 | continue headers 269 | } 270 | ext[k] = v 271 | } 272 | if s != "" && s[0] != ',' { 273 | continue headers 274 | } 275 | result = append(result, ext) 276 | if s == "" { 277 | continue headers 278 | } 279 | s = s[1:] 280 | } 281 | } 282 | return result 283 | } 284 | -------------------------------------------------------------------------------- /vendor/github.com/grishinsana/goftx/orders.go: -------------------------------------------------------------------------------- 1 | package goftx 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/grishinsana/goftx/models" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | const ( 13 | apiGetOpenOrders = "/orders" 14 | apiGetOrderStatus = "/orders/%d" 15 | apiGetOrdersHistory = "/orders/history" 16 | apiGetTriggerOrders = "/conditional_orders" 17 | apiGetOrderTriggers = "/conditional_orders/%d/triggers" 18 | apiPlaceTriggerOrder = "/conditional_orders" 19 | apiPlaceOrder = "/orders" 20 | apiCancelOrders = "/orders" 21 | ) 22 | 23 | type Orders struct { 24 | client *Client 25 | } 26 | 27 | func (o *Orders) GetOpenOrders(market string) ([]*models.Order, error) { 28 | request, err := o.client.prepareRequest(Request{ 29 | Auth: true, 30 | Method: http.MethodGet, 31 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetOpenOrders), 32 | Params: map[string]string{ 33 | "market": market, 34 | }, 35 | }) 36 | if err != nil { 37 | return nil, errors.WithStack(err) 38 | } 39 | 40 | response, err := o.client.do(request) 41 | if err != nil { 42 | return nil, errors.WithStack(err) 43 | } 44 | 45 | var result []*models.Order 46 | err = json.Unmarshal(response, &result) 47 | if err != nil { 48 | return nil, errors.WithStack(err) 49 | } 50 | 51 | return result, nil 52 | } 53 | 54 | func (o *Orders) GetOrderStatus(orderID int64) (*models.Order, error) { 55 | request, err := o.client.prepareRequest(Request{ 56 | Auth: true, 57 | Method: http.MethodGet, 58 | URL: fmt.Sprintf("%s%s", apiUrl, fmt.Sprintf(apiGetOrderStatus, orderID)), 59 | }) 60 | if err != nil { 61 | return nil, errors.WithStack(err) 62 | } 63 | 64 | response, err := o.client.do(request) 65 | if err != nil { 66 | return nil, errors.WithStack(err) 67 | } 68 | 69 | var result *models.Order 70 | err = json.Unmarshal(response, &result) 71 | if err != nil { 72 | return nil, errors.WithStack(err) 73 | } 74 | 75 | return result, nil 76 | } 77 | 78 | func (o *Orders) GetOrdersHistory(params *models.GetOrdersHistoryParams) ([]*models.Order, error) { 79 | queryParams, err := PrepareQueryParams(params) 80 | if err != nil { 81 | return nil, errors.WithStack(err) 82 | } 83 | 84 | request, err := o.client.prepareRequest(Request{ 85 | Auth: true, 86 | Method: http.MethodGet, 87 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetOrdersHistory), 88 | Params: queryParams, 89 | }) 90 | if err != nil { 91 | return nil, errors.WithStack(err) 92 | } 93 | 94 | response, err := o.client.do(request) 95 | if err != nil { 96 | return nil, errors.WithStack(err) 97 | } 98 | 99 | var result []*models.Order 100 | err = json.Unmarshal(response, &result) 101 | if err != nil { 102 | return nil, errors.WithStack(err) 103 | } 104 | 105 | return result, nil 106 | } 107 | 108 | func (o *Orders) GetOpenTriggerOrders(params *models.GetOpenTriggerOrdersParams) ([]*models.TriggerOrder, error) { 109 | queryParams, err := PrepareQueryParams(params) 110 | if err != nil { 111 | return nil, errors.WithStack(err) 112 | } 113 | 114 | request, err := o.client.prepareRequest(Request{ 115 | Auth: true, 116 | Method: http.MethodGet, 117 | URL: fmt.Sprintf("%s%s", apiUrl, apiGetTriggerOrders), 118 | Params: queryParams, 119 | }) 120 | if err != nil { 121 | return nil, errors.WithStack(err) 122 | } 123 | 124 | response, err := o.client.do(request) 125 | if err != nil { 126 | return nil, errors.WithStack(err) 127 | } 128 | 129 | var result []*models.TriggerOrder 130 | err = json.Unmarshal(response, &result) 131 | if err != nil { 132 | return nil, errors.WithStack(err) 133 | } 134 | 135 | return result, nil 136 | } 137 | 138 | func (o *Orders) GetOrderTriggers(orderID int64) ([]*models.Trigger, error) { 139 | request, err := o.client.prepareRequest(Request{ 140 | Auth: true, 141 | Method: http.MethodGet, 142 | URL: fmt.Sprintf("%s%s", apiUrl, fmt.Sprintf(apiGetOrderTriggers, orderID)), 143 | }) 144 | if err != nil { 145 | return nil, errors.WithStack(err) 146 | } 147 | 148 | response, err := o.client.do(request) 149 | if err != nil { 150 | return nil, errors.WithStack(err) 151 | } 152 | 153 | var result []*models.Trigger 154 | err = json.Unmarshal(response, &result) 155 | if err != nil { 156 | return nil, errors.WithStack(err) 157 | } 158 | 159 | return result, nil 160 | } 161 | 162 | func (o *Orders) PlaceOrder(orderParams models.PlaceOrderParams) (*models.Order, error) { 163 | body, err := json.Marshal(orderParams) 164 | if err != nil { 165 | return nil, errors.WithStack(err) 166 | } 167 | 168 | request, err := o.client.prepareRequest(Request{ 169 | Auth: true, 170 | Method: http.MethodPost, 171 | URL: fmt.Sprintf("%s%s", apiUrl, apiPlaceOrder), 172 | Body: body, 173 | }) 174 | if err != nil { 175 | return nil, errors.WithStack(err) 176 | } 177 | 178 | response, err := o.client.do(request) 179 | if err != nil { 180 | return nil, errors.WithStack(err) 181 | } 182 | 183 | var result *models.Order 184 | err = json.Unmarshal(response, &result) 185 | if err != nil { 186 | return nil, errors.WithStack(err) 187 | } 188 | 189 | return result, nil 190 | } 191 | 192 | func (o *Orders) PlaceTriggerOrder(orderParams interface{}) (*models.TriggerOrder, error) { 193 | body, err := json.Marshal(orderParams) 194 | if err != nil { 195 | return nil, errors.WithStack(err) 196 | } 197 | 198 | request, err := o.client.prepareRequest(Request{ 199 | Auth: true, 200 | Method: http.MethodPost, 201 | URL: fmt.Sprintf("%s%s", apiUrl, apiPlaceTriggerOrder), 202 | Body: body, 203 | }) 204 | if err != nil { 205 | return nil, errors.WithStack(err) 206 | } 207 | 208 | response, err := o.client.do(request) 209 | if err != nil { 210 | return nil, errors.WithStack(err) 211 | } 212 | 213 | var result *models.TriggerOrder 214 | err = json.Unmarshal(response, &result) 215 | if err != nil { 216 | return nil, errors.WithStack(err) 217 | } 218 | 219 | return result, nil 220 | } 221 | 222 | func (o *Orders) CancelAllOrders(market string) error { 223 | return o.cancelOrders(struct { 224 | Market string `json:"market"` 225 | }{ 226 | Market: market, 227 | }) 228 | } 229 | 230 | func (o *Orders) CancelAllLimitOrders(market string) error { 231 | return o.cancelOrders(struct { 232 | Market string `json:"market"` 233 | LimitOrdersOnly bool `json:"limitOrdersOnly"` 234 | }{ 235 | Market: market, 236 | LimitOrdersOnly: true, 237 | }) 238 | } 239 | 240 | func (o *Orders) CancelAllConditionalOrders(market string) error { 241 | return o.cancelOrders(struct { 242 | Market string `json:"market"` 243 | ConditionalOrdersOnly bool `json:"conditionalOrdersOnly"` 244 | }{ 245 | Market: market, 246 | ConditionalOrdersOnly: true, 247 | }) 248 | } 249 | 250 | func (o *Orders) cancelOrders(req interface{}) error { 251 | body, err := json.Marshal(req) 252 | 253 | if err != nil { 254 | return errors.WithStack(err) 255 | } 256 | 257 | request, err := o.client.prepareRequest(Request{ 258 | Auth: true, 259 | Method: http.MethodDelete, 260 | URL: fmt.Sprintf("%s%s", apiUrl, apiCancelOrders), 261 | Body: body, 262 | }) 263 | if err != nil { 264 | return errors.WithStack(err) 265 | } 266 | 267 | _, err = o.client.do(request) 268 | if err != nil { 269 | return errors.WithStack(err) 270 | } 271 | 272 | return nil 273 | } 274 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/v3/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package cron implements a cron spec parser and job runner. 3 | 4 | Usage 5 | 6 | Callers may register Funcs to be invoked on a given schedule. Cron will run 7 | them in their own goroutines. 8 | 9 | c := cron.New() 10 | c.AddFunc("30 * * * *", func() { fmt.Println("Every hour on the half hour") }) 11 | c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println(".. in the range 3-6am, 8-11pm") }) 12 | c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * * *", func() { fmt.Println("Runs at 04:30 Tokyo time every day") }) 13 | c.AddFunc("@hourly", func() { fmt.Println("Every hour, starting an hour from now") }) 14 | c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") }) 15 | c.Start() 16 | .. 17 | // Funcs are invoked in their own goroutine, asynchronously. 18 | ... 19 | // Funcs may also be added to a running Cron 20 | c.AddFunc("@daily", func() { fmt.Println("Every day") }) 21 | .. 22 | // Inspect the cron job entries' next and previous run times. 23 | inspect(c.Entries()) 24 | .. 25 | c.Stop() // Stop the scheduler (does not stop any jobs already running). 26 | 27 | CRON Expression Format 28 | 29 | A cron expression represents a set of times, using 5 space-separated fields. 30 | 31 | Field name | Mandatory? | Allowed values | Allowed special characters 32 | ---------- | ---------- | -------------- | -------------------------- 33 | Minutes | Yes | 0-59 | * / , - 34 | Hours | Yes | 0-23 | * / , - 35 | Day of month | Yes | 1-31 | * / , - ? 36 | Month | Yes | 1-12 or JAN-DEC | * / , - 37 | Day of week | Yes | 0-6 or SUN-SAT | * / , - ? 38 | 39 | Month and Day-of-week field values are case insensitive. "SUN", "Sun", and 40 | "sun" are equally accepted. 41 | 42 | The specific interpretation of the format is based on the Cron Wikipedia page: 43 | https://en.wikipedia.org/wiki/Cron 44 | 45 | Alternative Formats 46 | 47 | Alternative Cron expression formats support other fields like seconds. You can 48 | implement that by creating a custom Parser as follows. 49 | 50 | cron.New( 51 | cron.WithParser( 52 | cron.SecondOptional | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)) 53 | 54 | The most popular alternative Cron expression format is Quartz: 55 | http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html 56 | 57 | Special Characters 58 | 59 | Asterisk ( * ) 60 | 61 | The asterisk indicates that the cron expression will match for all values of the 62 | field; e.g., using an asterisk in the 5th field (month) would indicate every 63 | month. 64 | 65 | Slash ( / ) 66 | 67 | Slashes are used to describe increments of ranges. For example 3-59/15 in the 68 | 1st field (minutes) would indicate the 3rd minute of the hour and every 15 69 | minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...", 70 | that is, an increment over the largest possible range of the field. The form 71 | "N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the 72 | increment until the end of that specific range. It does not wrap around. 73 | 74 | Comma ( , ) 75 | 76 | Commas are used to separate items of a list. For example, using "MON,WED,FRI" in 77 | the 5th field (day of week) would mean Mondays, Wednesdays and Fridays. 78 | 79 | Hyphen ( - ) 80 | 81 | Hyphens are used to define ranges. For example, 9-17 would indicate every 82 | hour between 9am and 5pm inclusive. 83 | 84 | Question mark ( ? ) 85 | 86 | Question mark may be used instead of '*' for leaving either day-of-month or 87 | day-of-week blank. 88 | 89 | Predefined schedules 90 | 91 | You may use one of several pre-defined schedules in place of a cron expression. 92 | 93 | Entry | Description | Equivalent To 94 | ----- | ----------- | ------------- 95 | @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 1 1 * 96 | @monthly | Run once a month, midnight, first of month | 0 0 1 * * 97 | @weekly | Run once a week, midnight between Sat/Sun | 0 0 * * 0 98 | @daily (or @midnight) | Run once a day, midnight | 0 0 * * * 99 | @hourly | Run once an hour, beginning of hour | 0 * * * * 100 | 101 | Intervals 102 | 103 | You may also schedule a job to execute at fixed intervals, starting at the time it's added 104 | or cron is run. This is supported by formatting the cron spec like this: 105 | 106 | @every 107 | 108 | where "duration" is a string accepted by time.ParseDuration 109 | (http://golang.org/pkg/time/#ParseDuration). 110 | 111 | For example, "@every 1h30m10s" would indicate a schedule that activates after 112 | 1 hour, 30 minutes, 10 seconds, and then every interval after that. 113 | 114 | Note: The interval does not take the job runtime into account. For example, 115 | if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, 116 | it will have only 2 minutes of idle time between each run. 117 | 118 | Time zones 119 | 120 | By default, all interpretation and scheduling is done in the machine's local 121 | time zone (time.Local). You can specify a different time zone on construction: 122 | 123 | cron.New( 124 | cron.WithLocation(time.UTC)) 125 | 126 | Individual cron schedules may also override the time zone they are to be 127 | interpreted in by providing an additional space-separated field at the beginning 128 | of the cron spec, of the form "CRON_TZ=Asia/Tokyo". 129 | 130 | For example: 131 | 132 | # Runs at 6am in time.Local 133 | cron.New().AddFunc("0 6 * * ?", ...) 134 | 135 | # Runs at 6am in America/New_York 136 | nyc, _ := time.LoadLocation("America/New_York") 137 | c := cron.New(cron.WithLocation(nyc)) 138 | c.AddFunc("0 6 * * ?", ...) 139 | 140 | # Runs at 6am in Asia/Tokyo 141 | cron.New().AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...) 142 | 143 | # Runs at 6am in Asia/Tokyo 144 | c := cron.New(cron.WithLocation(nyc)) 145 | c.SetLocation("America/New_York") 146 | c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...) 147 | 148 | The prefix "TZ=(TIME ZONE)" is also supported for legacy compatibility. 149 | 150 | Be aware that jobs scheduled during daylight-savings leap-ahead transitions will 151 | not be run! 152 | 153 | Job Wrappers / Chain 154 | 155 | A Cron runner may be configured with a chain of job wrappers to add 156 | cross-cutting functionality to all submitted jobs. For example, they may be used 157 | to achieve the following effects: 158 | 159 | - Recover any panics from jobs (activated by default) 160 | - Delay a job's execution if the previous run hasn't completed yet 161 | - Skip a job's execution if the previous run hasn't completed yet 162 | - Log each job's invocations 163 | 164 | Install wrappers for all jobs added to a cron using the `cron.WithChain` option: 165 | 166 | cron.New(cron.WithChain( 167 | cron.SkipIfStillRunning(logger), 168 | )) 169 | 170 | Install wrappers for individual jobs by explicitly wrapping them: 171 | 172 | job = cron.NewChain( 173 | cron.SkipIfStillRunning(logger), 174 | ).Then(job) 175 | 176 | Thread safety 177 | 178 | Since the Cron service runs concurrently with the calling code, some amount of 179 | care must be taken to ensure proper synchronization. 180 | 181 | All cron methods are designed to be correctly synchronized as long as the caller 182 | ensures that invocations have a clear happens-before ordering between them. 183 | 184 | Logging 185 | 186 | Cron defines a Logger interface that is a subset of the one defined in 187 | github.com/go-logr/logr. It has two logging levels (Info and Error), and 188 | parameters are key/value pairs. This makes it possible for cron logging to plug 189 | into structured logging systems. An adapter, [Verbose]PrintfLogger, is provided 190 | to wrap the standard library *log.Logger. 191 | 192 | For additional insight into Cron operations, verbose logging may be activated 193 | which will record job runs, scheduling decisions, and added or removed jobs. 194 | Activate it with a one-off logger as follows: 195 | 196 | cron.New( 197 | cron.WithLogger( 198 | cron.VerbosePrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags)))) 199 | 200 | 201 | Implementation 202 | 203 | Cron entries are stored in an array, sorted by their next activation time. Cron 204 | sleeps until the next job is due to be run. 205 | 206 | Upon waking: 207 | - it runs each entry that is active on that second 208 | - it calculates the next run times for the jobs that were run 209 | - it re-sorts the array of entries by next activation time. 210 | - it goes to sleep until the soonest job. 211 | */ 212 | package cron 213 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Package errors provides simple error handling primitives. 2 | // 3 | // The traditional error handling idiom in Go is roughly akin to 4 | // 5 | // if err != nil { 6 | // return err 7 | // } 8 | // 9 | // which when applied recursively up the call stack results in error reports 10 | // without context or debugging information. The errors package allows 11 | // programmers to add context to the failure path in their code in a way 12 | // that does not destroy the original value of the error. 13 | // 14 | // Adding context to an error 15 | // 16 | // The errors.Wrap function returns a new error that adds context to the 17 | // original error by recording a stack trace at the point Wrap is called, 18 | // together with the supplied message. For example 19 | // 20 | // _, err := ioutil.ReadAll(r) 21 | // if err != nil { 22 | // return errors.Wrap(err, "read failed") 23 | // } 24 | // 25 | // If additional control is required, the errors.WithStack and 26 | // errors.WithMessage functions destructure errors.Wrap into its component 27 | // operations: annotating an error with a stack trace and with a message, 28 | // respectively. 29 | // 30 | // Retrieving the cause of an error 31 | // 32 | // Using errors.Wrap constructs a stack of errors, adding context to the 33 | // preceding error. Depending on the nature of the error it may be necessary 34 | // to reverse the operation of errors.Wrap to retrieve the original error 35 | // for inspection. Any error value which implements this interface 36 | // 37 | // type causer interface { 38 | // Cause() error 39 | // } 40 | // 41 | // can be inspected by errors.Cause. errors.Cause will recursively retrieve 42 | // the topmost error that does not implement causer, which is assumed to be 43 | // the original cause. For example: 44 | // 45 | // switch err := errors.Cause(err).(type) { 46 | // case *MyError: 47 | // // handle specifically 48 | // default: 49 | // // unknown error 50 | // } 51 | // 52 | // Although the causer interface is not exported by this package, it is 53 | // considered a part of its stable public interface. 54 | // 55 | // Formatted printing of errors 56 | // 57 | // All error values returned from this package implement fmt.Formatter and can 58 | // be formatted by the fmt package. The following verbs are supported: 59 | // 60 | // %s print the error. If the error has a Cause it will be 61 | // printed recursively. 62 | // %v see %s 63 | // %+v extended format. Each Frame of the error's StackTrace will 64 | // be printed in detail. 65 | // 66 | // Retrieving the stack trace of an error or wrapper 67 | // 68 | // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are 69 | // invoked. This information can be retrieved with the following interface: 70 | // 71 | // type stackTracer interface { 72 | // StackTrace() errors.StackTrace 73 | // } 74 | // 75 | // The returned errors.StackTrace type is defined as 76 | // 77 | // type StackTrace []Frame 78 | // 79 | // The Frame type represents a call site in the stack trace. Frame supports 80 | // the fmt.Formatter interface that can be used for printing information about 81 | // the stack trace of this error. For example: 82 | // 83 | // if err, ok := err.(stackTracer); ok { 84 | // for _, f := range err.StackTrace() { 85 | // fmt.Printf("%+s:%d\n", f, f) 86 | // } 87 | // } 88 | // 89 | // Although the stackTracer interface is not exported by this package, it is 90 | // considered a part of its stable public interface. 91 | // 92 | // See the documentation for Frame.Format for more details. 93 | package errors 94 | 95 | import ( 96 | "fmt" 97 | "io" 98 | ) 99 | 100 | // New returns an error with the supplied message. 101 | // New also records the stack trace at the point it was called. 102 | func New(message string) error { 103 | return &fundamental{ 104 | msg: message, 105 | stack: callers(), 106 | } 107 | } 108 | 109 | // Errorf formats according to a format specifier and returns the string 110 | // as a value that satisfies error. 111 | // Errorf also records the stack trace at the point it was called. 112 | func Errorf(format string, args ...interface{}) error { 113 | return &fundamental{ 114 | msg: fmt.Sprintf(format, args...), 115 | stack: callers(), 116 | } 117 | } 118 | 119 | // fundamental is an error that has a message and a stack, but no caller. 120 | type fundamental struct { 121 | msg string 122 | *stack 123 | } 124 | 125 | func (f *fundamental) Error() string { return f.msg } 126 | 127 | func (f *fundamental) Format(s fmt.State, verb rune) { 128 | switch verb { 129 | case 'v': 130 | if s.Flag('+') { 131 | io.WriteString(s, f.msg) 132 | f.stack.Format(s, verb) 133 | return 134 | } 135 | fallthrough 136 | case 's': 137 | io.WriteString(s, f.msg) 138 | case 'q': 139 | fmt.Fprintf(s, "%q", f.msg) 140 | } 141 | } 142 | 143 | // WithStack annotates err with a stack trace at the point WithStack was called. 144 | // If err is nil, WithStack returns nil. 145 | func WithStack(err error) error { 146 | if err == nil { 147 | return nil 148 | } 149 | return &withStack{ 150 | err, 151 | callers(), 152 | } 153 | } 154 | 155 | type withStack struct { 156 | error 157 | *stack 158 | } 159 | 160 | func (w *withStack) Cause() error { return w.error } 161 | 162 | // Unwrap provides compatibility for Go 1.13 error chains. 163 | func (w *withStack) Unwrap() error { return w.error } 164 | 165 | func (w *withStack) Format(s fmt.State, verb rune) { 166 | switch verb { 167 | case 'v': 168 | if s.Flag('+') { 169 | fmt.Fprintf(s, "%+v", w.Cause()) 170 | w.stack.Format(s, verb) 171 | return 172 | } 173 | fallthrough 174 | case 's': 175 | io.WriteString(s, w.Error()) 176 | case 'q': 177 | fmt.Fprintf(s, "%q", w.Error()) 178 | } 179 | } 180 | 181 | // Wrap returns an error annotating err with a stack trace 182 | // at the point Wrap is called, and the supplied message. 183 | // If err is nil, Wrap returns nil. 184 | func Wrap(err error, message string) error { 185 | if err == nil { 186 | return nil 187 | } 188 | err = &withMessage{ 189 | cause: err, 190 | msg: message, 191 | } 192 | return &withStack{ 193 | err, 194 | callers(), 195 | } 196 | } 197 | 198 | // Wrapf returns an error annotating err with a stack trace 199 | // at the point Wrapf is called, and the format specifier. 200 | // If err is nil, Wrapf returns nil. 201 | func Wrapf(err error, format string, args ...interface{}) error { 202 | if err == nil { 203 | return nil 204 | } 205 | err = &withMessage{ 206 | cause: err, 207 | msg: fmt.Sprintf(format, args...), 208 | } 209 | return &withStack{ 210 | err, 211 | callers(), 212 | } 213 | } 214 | 215 | // WithMessage annotates err with a new message. 216 | // If err is nil, WithMessage returns nil. 217 | func WithMessage(err error, message string) error { 218 | if err == nil { 219 | return nil 220 | } 221 | return &withMessage{ 222 | cause: err, 223 | msg: message, 224 | } 225 | } 226 | 227 | // WithMessagef annotates err with the format specifier. 228 | // If err is nil, WithMessagef returns nil. 229 | func WithMessagef(err error, format string, args ...interface{}) error { 230 | if err == nil { 231 | return nil 232 | } 233 | return &withMessage{ 234 | cause: err, 235 | msg: fmt.Sprintf(format, args...), 236 | } 237 | } 238 | 239 | type withMessage struct { 240 | cause error 241 | msg string 242 | } 243 | 244 | func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } 245 | func (w *withMessage) Cause() error { return w.cause } 246 | 247 | // Unwrap provides compatibility for Go 1.13 error chains. 248 | func (w *withMessage) Unwrap() error { return w.cause } 249 | 250 | func (w *withMessage) Format(s fmt.State, verb rune) { 251 | switch verb { 252 | case 'v': 253 | if s.Flag('+') { 254 | fmt.Fprintf(s, "%+v\n", w.Cause()) 255 | io.WriteString(s, w.msg) 256 | return 257 | } 258 | fallthrough 259 | case 's', 'q': 260 | io.WriteString(s, w.Error()) 261 | } 262 | } 263 | 264 | // Cause returns the underlying cause of the error, if possible. 265 | // An error value has a cause if it implements the following 266 | // interface: 267 | // 268 | // type causer interface { 269 | // Cause() error 270 | // } 271 | // 272 | // If the error does not implement Cause, the original error will 273 | // be returned. If the error is nil, nil will be returned without further 274 | // investigation. 275 | func Cause(err error) error { 276 | type causer interface { 277 | Cause() error 278 | } 279 | 280 | for err != nil { 281 | cause, ok := err.(causer) 282 | if !ok { 283 | break 284 | } 285 | err = cause.Cause() 286 | } 287 | return err 288 | } 289 | --------------------------------------------------------------------------------