feature: add pre-commit

This commit is contained in:
zenix 2022-06-17 16:04:23 +09:00
parent 55fa4cc8f1
commit ba1342cbc3
28 changed files with 134 additions and 35 deletions

View File

@ -17,7 +17,7 @@ jobs:
env: env:
MYSQL_DATABASE: bbgo MYSQL_DATABASE: bbgo
MYSQL_USER: "root" MYSQL_USER: "root"
MYSQL_PASSWORD: "root" MYSQL_PASSWORD: "root" # pragma: allowlist secret
steps: steps:
@ -48,6 +48,15 @@ jobs:
with: with:
go-version: 1.18 go-version: 1.18
- name: Run pre-commit
run: |
pip install pre-commit
go get -v -u github.com/go-critic/go-critic/cmd/gocritic
pre-commit install-hooks
pre-commit run markdownlint --files=README.md --verbose
pre-commit run go-fmt --all-files --verbose
pre-commit run go-critic --all-files --verbose
- name: Install Migration Tool - name: Install Migration Tool
run: go install github.com/c9s/rockhopper/cmd/rockhopper@v1.2.1 run: go install github.com/c9s/rockhopper/cmd/rockhopper@v1.2.1

5
.markdownlint.yaml Normal file
View File

@ -0,0 +1,5 @@
default: true
extends: null
MD033: false
MD010: false
MD013: false

21
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,21 @@
---
repos:
# Secret Detection
- repo: https://github.com/Yelp/detect-secrets
rev: v1.2.0
hooks:
- id: detect-secrets
args: ['--exclude-secrets', '3899a918953e01bfe218116cdfeccbed579e26275c4a89abcbc70d2cb9e9bbb8']
exclude: pacakge.lock.json
# Markdown
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.31.1
hooks:
- id: markdownlint
# Go
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.0
hooks:
- id: go-fmt
args: ['-l']
- id: go-critic

View File

@ -5,6 +5,8 @@ A trading bot framework written in Go. The name bbgo comes from the BB8 bot in t
## Current Status ## Current Status
[![Go](https://github.com/c9s/bbgo/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/c9s/bbgo/actions/workflows/go.yml) [![Go](https://github.com/c9s/bbgo/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/c9s/bbgo/actions/workflows/go.yml)
[![GoDoc](https://godoc.org/github.com/c9s/bbgo?status.svg)](https://pkg.go.dev/github.com/c9s/bbgo)
[![Go Report Card](https://goreportcard.com/badge/github.com/c9s/bbgo)](https://goreportcard.com/report/github.com/c9s/bbgo)
[![DockerHub](https://img.shields.io/docker/pulls/yoanlin/bbgo.svg)](https://hub.docker.com/r/yoanlin/bbgo) [![DockerHub](https://img.shields.io/docker/pulls/yoanlin/bbgo.svg)](https://hub.docker.com/r/yoanlin/bbgo)
[![Coverage Status](http://codecov.io/github/c9s/bbgo/coverage.svg?branch=main)](http://codecov.io/github/c9s/bbgo?branch=main) [![Coverage Status](http://codecov.io/github/c9s/bbgo/coverage.svg?branch=main)](http://codecov.io/github/c9s/bbgo?branch=main)
<img alt="open collective badge" src="https://opencollective.com/bbgo/tiers/badge.svg"> <img alt="open collective badge" src="https://opencollective.com/bbgo/tiers/badge.svg">
@ -42,7 +44,38 @@ You can use BBGO's underlying common exchange API, currently it supports 4+ majo
- Built-in parameter optimization tool. - Built-in parameter optimization tool.
- Built-in Grid strategy and many other built-in strategies. - Built-in Grid strategy and many other built-in strategies.
- Multi-exchange session support: you can connect to more than 2 exchanges with different accounts or subaccounts. - Multi-exchange session support: you can connect to more than 2 exchanges with different accounts or subaccounts.
- Standard indicators, e.g., SMA, EMA, BOLL, VMA, MACD... - Indicators with interface similar to `pandas.Series`([series](https://github.com/c9s/bbgo/blob/main/doc/development/series.md))([usage](https://github.com/c9s/bbgo/blob/main/doc/development/indicator.md)):
- [Accumulation/Distribution Indicator](./pkg/indicator/ad.go)
- [Arnaud Legoux Moving Average](./pkg/indicator/alma.go)
- [Average True Range](./pkg/indicator/atr.go)
- [Bollinger Bands](./pkg/indicator/boll.go)
- [Commodity Channel Index](./pkg/indicator/cci.go)
- [Cumulative Moving Average](./pkg/indicator/cma.go)
- [Double Exponential Moving Average](./pkg/indicator/dema.go)
- [Directional Movement Index](./pkg/indicator/dmi.go)
- [Brownian Motion's Drift Factor](./pkg/indicator/drift.go)
- [Ease of Movement](./pkg/indicator/emv.go)
- [Exponentially Weighted Moving Average](./pkg/indicator/ewma.go)
- [Hull Moving Average](./pkg/indicator/hull.go)
- [Trend Line (Tool)](./pkg/indicator/line.go)
- [Moving Average Convergence Divergence Indicator](./pkg/indicator/macd.go)
- [On-Balance Volume](./pkg/indicator/obv.go)
- [Pivot](./pkg/indicator/pivot.go)
- [Running Moving Average](./pkg/indicator/rma.go)
- [Relative Strength Index](./pkg/indicator/rsi.go)
- [Simple Moving Average](./pkg/indicator/sma.go)
- [Ehler's Super Smoother Filter](./pkg/indicator/ssf.go)
- [Stochastic Oscillator](./pkg/indicator/stoch.go)
- [SuperTrend](./pkg/indicator/supertrend.go)
- [Triple Exponential Moving Average](./pkg/indicator/tema.go)
- [Tillson T3 Moving Average](./pkg/indicator/till.go)
- [Triangular Moving Average](./pkg/indicator/tma.go)
- [Variable Index Dynamic Average](./pkg/indicator/vidya.go)
- [Volatility Indicator](./pkg/indicator/volatility.go)
- [Volume Weighted Average Price](./pkg/indicator/vwap.go)
- [Zero Lag Exponential Moving Average](./pkg/indicator/zlema.go)
- And more...
- HeikinAshi OHLC / Normal OHLC (check [this config](https://github.com/c9s/bbgo/blob/main/config/skeleton.yaml#L5))
- React-powered Web Dashboard. - React-powered Web Dashboard.
- Docker image ready. - Docker image ready.
- Kubernetes support. - Kubernetes support.
@ -63,8 +96,8 @@ You can use BBGO's underlying common exchange API, currently it supports 4+ majo
- Kucoin Spot Exchange - Kucoin Spot Exchange
- MAX Spot Exchange (located in Taiwan) - MAX Spot Exchange (located in Taiwan)
## Documentation and General Topics ## Documentation and General Topics
- Check the [documentation index](doc/README.md) - Check the [documentation index](doc/README.md)
## BBGO Tokenomics ## BBGO Tokenomics
@ -105,6 +138,7 @@ bash <(curl -s https://raw.githubusercontent.com/c9s/bbgo/main/scripts/setup-bol
``` ```
If you already have configuration somewhere, a download-only script might be suitable for you: If you already have configuration somewhere, a download-only script might be suitable for you:
```sh ```sh
bash <(curl -s https://raw.githubusercontent.com/c9s/bbgo/main/scripts/download.sh) bash <(curl -s https://raw.githubusercontent.com/c9s/bbgo/main/scripts/download.sh)
``` ```
@ -114,7 +148,7 @@ Or refer to the [Release Page](https://github.com/c9s/bbgo/releases) and downloa
Since v2, we've added new float point implementation from dnum to support decimals with higher precision. Since v2, we've added new float point implementation from dnum to support decimals with higher precision.
To download & setup, please refer to [Dnum Installation](doc/topics/dnum-binary.md) To download & setup, please refer to [Dnum Installation](doc/topics/dnum-binary.md)
### One-click Linode StackScript: ### One-click Linode StackScript
- BBGO Grid Trading on Binance <https://cloud.linode.com/stackscripts/950715> - BBGO Grid Trading on Binance <https://cloud.linode.com/stackscripts/950715>
- BBGO USDT/TWD Grid Trading on MAX <https://cloud.linode.com/stackscripts/793380> - BBGO USDT/TWD Grid Trading on MAX <https://cloud.linode.com/stackscripts/793380>
@ -165,14 +199,12 @@ Prepare your dotenv file `.env.local` and BBGO yaml config file `bbgo.yaml`.
To check the available environment variables, please see [Environment Variables](./doc/configuration/envvars.md) To check the available environment variables, please see [Environment Variables](./doc/configuration/envvars.md)
The minimal bbgo.yaml could be generated by: The minimal bbgo.yaml could be generated by:
```sh ```sh
curl -o bbgo.yaml https://raw.githubusercontent.com/c9s/bbgo/main/config/minimal.yaml curl -o bbgo.yaml https://raw.githubusercontent.com/c9s/bbgo/main/config/minimal.yaml
``` ```
To run strategy: To run strategy:
```sh ```sh
@ -185,14 +217,12 @@ To start bbgo with the frontend dashboard:
bbgo run --enable-webserver bbgo run --enable-webserver
``` ```
If you want to switch to other dotenv file, you can add an `--dotenv` option or `--config`: If you want to switch to other dotenv file, you can add an `--dotenv` option or `--config`:
```sh ```sh
bbgo sync --dotenv .env.dev --config config/grid.yaml --session binance bbgo sync --dotenv .env.dev --config config/grid.yaml --session binance
``` ```
To query transfer history: To query transfer history:
```sh ```sh
@ -207,13 +237,13 @@ bbgo pnl --exchange binance --asset BTC --since "2019-01-01"
``` ```
---> --->
## Advanced Configuration ## Advanced Configuration
### Testnet (Paper Trading) ### Testnet (Paper Trading)
Currently only supports binance testnet. Currently only supports binance testnet.
To run bbgo in testnet, apply new API keys from [Binance Test Network](https://testnet.binance.vision), and set the following env before you start bbgo: To run bbgo in testnet, apply new API keys from [Binance Test Network](https://testnet.binance.vision), and set the following env before you start bbgo:
```bash ```bash
export PAPER_TRADE=1 export PAPER_TRADE=1
export DISABLE_MARKET_CACHE=1 # the symbols supported in testnet is far less than the mainnet export DISABLE_MARKET_CACHE=1 # the symbols supported in testnet is far less than the mainnet

View File

@ -928,10 +928,11 @@ func (environ *Environment) setupInteraction(persistence service.PersistenceServ
} }
interact.AddCustomInteraction(&interact.AuthInteract{ interact.AddCustomInteraction(&interact.AuthInteract{
Strict: authStrict, Strict: authStrict,
Mode: authMode, Mode: authMode,
Token: authToken, // can be empty string here Token: authToken, // can be empty string here
OneTimePasswordKey: key, // can be nil here // pragma: allowlist nextline secret
OneTimePasswordKey: key, // can be nil here
}) })
return nil return nil
} }

View File

@ -1,4 +1,4 @@
// Code generated by "callbackgen -type StrategyController strategy_controller.go"; DO NOT EDIT. // Code generated by "callbackgen -type StrategyController -interface"; DO NOT EDIT.
package bbgo package bbgo
@ -33,3 +33,11 @@ func (s *StrategyController) EmitEmergencyStop() {
cb() cb()
} }
} }
type StrategyControllerEventHub interface {
OnSuspend(cb func())
OnResume(cb func())
OnEmergencyStop(cb func())
}

View File

@ -35,6 +35,7 @@ func New() *RestClient {
} }
func (c *RestClient) Auth(apiKey string) { func (c *RestClient) Auth(apiKey string) {
// pragma: allowlist nextline secret
c.apiKey = apiKey c.apiKey = apiKey
} }

View File

@ -35,6 +35,7 @@ func NewRestClient() *RestClient {
} }
func (c *RestClient) Auth(apiKey string) { func (c *RestClient) Auth(apiKey string) {
// pragma: allowlist nextline secret
c.apiKey = apiKey c.apiKey = apiKey
} }

View File

@ -60,6 +60,7 @@ func NewClient(baseURL string) *RestClient {
func (c *RestClient) Auth(key, secret string) { func (c *RestClient) Auth(key, secret string) {
c.Key = key c.Key = key
// pragma: allowlist nextline secret
c.Secret = secret c.Secret = secret
} }

View File

@ -122,7 +122,8 @@ func New(key, secret string) *Exchange {
} }
return &Exchange{ return &Exchange{
key: key, key: key,
// pragma: allowlist nextline secret
secret: secret, secret: secret,
client: client, client: client,
futuresClient: futuresClient, futuresClient: futuresClient,

View File

@ -90,8 +90,9 @@ func NewExchange(key, secret string, subAccount string) *Exchange {
client: client, client: client,
restEndpoint: u, restEndpoint: u,
key: key, key: key,
secret: secret, // pragma: allowlist nextline secret
subAccount: subAccount, secret: secret,
subAccount: subAccount,
} }
} }

View File

@ -68,6 +68,7 @@ func NewClient() *RestClient {
func (c *RestClient) Auth(key, secret, subAccount string) { func (c *RestClient) Auth(key, secret, subAccount string) {
c.Key = key c.Key = key
// pragma: allowlist nextline secret
c.Secret = secret c.Secret = secret
c.subAccount = subAccount c.subAccount = subAccount
} }

View File

@ -92,6 +92,7 @@ func newRestRequest(c *http.Client, baseURL *url.URL) *restRequest {
func (r *restRequest) Auth(key, secret string) *restRequest { func (r *restRequest) Auth(key, secret string) *restRequest {
r.key = key r.key = key
// pragma: allowlist nextline secret
r.secret = secret r.secret = secret
return r return r
} }

View File

@ -37,8 +37,9 @@ type klineSubscription struct {
func NewStream(key, secret string, subAccount string, e *Exchange) *Stream { func NewStream(key, secret string, subAccount string, e *Exchange) *Stream {
s := &Stream{ s := &Stream{
exchange: e, exchange: e,
key: key, key: key,
// pragma: allowlist nextline secret
secret: secret, secret: secret,
subAccount: subAccount, subAccount: subAccount,
StandardStream: &types.StandardStream{}, StandardStream: &types.StandardStream{},

View File

@ -182,6 +182,7 @@ func Test_insertAt(t *testing.T) {
func Test_newLoginRequest(t *testing.T) { func Test_newLoginRequest(t *testing.T) {
// From API doc: https://docs.ftx.com/?javascript#authentication-2 // From API doc: https://docs.ftx.com/?javascript#authentication-2
r := newLoginRequest("", "Y2QTHI23f23f23jfjas23f23To0RfUwX3H42fvN-", time.Unix(0, 1557246346499*int64(time.Millisecond)), "") r := newLoginRequest("", "Y2QTHI23f23f23jfjas23f23To0RfUwX3H42fvN-", time.Unix(0, 1557246346499*int64(time.Millisecond)), "")
// pragma: allowlist nextline secret
expectedSignature := "d10b5a67a1a941ae9463a60b285ae845cdeac1b11edc7da9977bef0228b96de9" expectedSignature := "d10b5a67a1a941ae9463a60b285ae845cdeac1b11edc7da9977bef0228b96de9"
assert.Equal(t, expectedSignature, r.Login.Signature) assert.Equal(t, expectedSignature, r.Login.Signature)
jsonStr, err := json.Marshal(r) jsonStr, err := json.Marshal(r)

View File

@ -44,7 +44,8 @@ func New(key, secret, passphrase string) *Exchange {
} }
return &Exchange{ return &Exchange{
key: key, key: key,
// pragma: allowlist nextline secret
secret: secret, secret: secret,
passphrase: passphrase, passphrase: passphrase,
client: client, client: client,

View File

@ -58,6 +58,7 @@ func NewClient() *RestClient {
func (c *RestClient) Auth(key, secret, passphrase string) { func (c *RestClient) Auth(key, secret, passphrase string) {
c.Key = key c.Key = key
// pragma: allowlist nextline secret
c.Secret = secret c.Secret = secret
c.Passphrase = passphrase c.Passphrase = passphrase
} }

View File

@ -47,8 +47,9 @@ func New(key, secret string) *Exchange {
client := maxapi.NewRestClient(baseURL) client := maxapi.NewRestClient(baseURL)
client.Auth(key, secret) client.Auth(key, secret)
return &Exchange{ return &Exchange{
client: client, client: client,
key: key, key: key,
// pragma: allowlist nextline secret
secret: secret, secret: secret,
v3order: &v3.OrderService{Client: client}, v3order: &v3.OrderService{Client: client},
v3margin: &v3.MarginService{Client: client}, v3margin: &v3.MarginService{Client: client},

View File

@ -130,7 +130,9 @@ func NewRestClient(baseURL string) *RestClient {
// Auth sets api key and secret for usage is requests that requires authentication. // Auth sets api key and secret for usage is requests that requires authentication.
func (c *RestClient) Auth(key string, secret string) *RestClient { func (c *RestClient) Auth(key string, secret string) *RestClient {
// pragma: allowlist nextline secret
c.APIKey = key c.APIKey = key
// pragma: allowlist nextline secret
c.APISecret = secret c.APISecret = secret
return c return c
} }

View File

@ -44,7 +44,8 @@ func NewStream(key, secret string) *Stream {
stream := &Stream{ stream := &Stream{
StandardStream: types.NewStandardStream(), StandardStream: types.NewStandardStream(),
key: key, key: key,
secret: secret, // pragma: allowlist nextline secret
secret: secret,
} }
stream.SetEndpointCreator(stream.getEndpoint) stream.SetEndpointCreator(stream.getEndpoint)
stream.SetParser(max.ParseMessage) stream.SetParser(max.ParseMessage)
@ -116,7 +117,9 @@ func (s *Stream) handleConnect() {
nonce := time.Now().UnixNano() / int64(time.Millisecond) nonce := time.Now().UnixNano() / int64(time.Millisecond)
auth := &max.AuthMessage{ auth := &max.AuthMessage{
Action: "auth", // pragma: allowlist nextline secret
Action: "auth",
// pragma: allowlist nextline secret
APIKey: s.key, APIKey: s.key,
Nonce: nonce, Nonce: nonce,
Signature: signPayload(fmt.Sprintf("%d", nonce), s.secret), Signature: signPayload(fmt.Sprintf("%d", nonce), s.secret),

View File

@ -38,7 +38,8 @@ func New(key, secret, passphrase string) *Exchange {
} }
return &Exchange{ return &Exchange{
key: key, key: key,
// pragma: allowlist nextline secret
secret: secret, secret: secret,
passphrase: passphrase, passphrase: passphrase,
client: client, client: client,

View File

@ -91,6 +91,7 @@ func NewClient() *RestClient {
func (c *RestClient) Auth(key, secret, passphrase string) { func (c *RestClient) Auth(key, secret, passphrase string) {
c.Key = key c.Key = key
// pragma: allowlist nextline secret
c.Secret = secret c.Secret = secret
c.Passphrase = passphrase c.Passphrase = passphrase
} }

View File

@ -349,11 +349,11 @@ func NewFromString(input string) (Value, error) {
} }
} }
if hasDecimal { if hasDecimal {
after := input[dotIndex+1 : len(input)] after := input[dotIndex+1:]
if decimalCount >= 8 { if decimalCount >= 8 {
after = after[0:8] + "." + after[8:len(after)] after = after[0:8] + "." + after[8:]
} else { } else {
after = after[0:decimalCount] + strings.Repeat("0", 8-decimalCount) + after[decimalCount:len(after)] after = after[0:decimalCount] + strings.Repeat("0", 8-decimalCount) + after[decimalCount:]
} }
input = input[0:dotIndex] + after input = input[0:dotIndex] + after
v, err := strconv.ParseFloat(input, 64) v, err := strconv.ParseFloat(input, 64)
@ -368,7 +368,7 @@ func NewFromString(input string) (Value, error) {
return Value(int64(math.Trunc(v))), nil return Value(int64(math.Trunc(v))), nil
} else if hasScientificNotion { } else if hasScientificNotion {
exp, err := strconv.ParseInt(input[scIndex+1:len(input)], 10, 32) exp, err := strconv.ParseInt(input[scIndex+1:], 10, 32)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -37,6 +37,7 @@ type AuthInteract struct {
func (it *AuthInteract) Commands(interact *Interact) { func (it *AuthInteract) Commands(interact *Interact) {
if it.Strict { if it.Strict {
// generate a one-time-use otp // generate a one-time-use otp
// pragma: allowlist nextline secret
if it.OneTimePasswordKey == nil { if it.OneTimePasswordKey == nil {
opts := totp.GenerateOpts{ opts := totp.GenerateOpts{
Issuer: "interact", Issuer: "interact",
@ -48,7 +49,7 @@ func (it *AuthInteract) Commands(interact *Interact) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
// pragma: allowlist nextline secret
it.OneTimePasswordKey = key it.OneTimePasswordKey = key
} }
interact.Command("/auth", "authorize", func(reply Reply, session Session) error { interact.Command("/auth", "authorize", func(reply Reply, session Session) error {

View File

@ -14,7 +14,7 @@ func TestGetMigrationsMap(t *testing.T) {
func TestMergeMigrationsMap(t *testing.T) { func TestMergeMigrationsMap(t *testing.T) {
MergeMigrationsMap(map[int64]*rockhopper.Migration{ MergeMigrationsMap(map[int64]*rockhopper.Migration{
2: &rockhopper.Migration{}, 2: {},
3: &rockhopper.Migration{}, 3: {},
}) })
} }

View File

@ -14,7 +14,7 @@ func TestGetMigrationsMap(t *testing.T) {
func TestMergeMigrationsMap(t *testing.T) { func TestMergeMigrationsMap(t *testing.T) {
MergeMigrationsMap(map[int64]*rockhopper.Migration{ MergeMigrationsMap(map[int64]*rockhopper.Migration{
2: &rockhopper.Migration{}, 2: {},
3: &rockhopper.Migration{}, 3: {},
}) })
} }

View File

@ -17,11 +17,15 @@ func collectSessionEnvVars(sessions map[string]*bbgo.ExchangeSession) (envVars m
} }
if len(session.EnvVarPrefix) > 0 { if len(session.EnvVarPrefix) > 0 {
// pragma: allowlist nextline secret
envVars[session.EnvVarPrefix+"_API_KEY"] = session.Key envVars[session.EnvVarPrefix+"_API_KEY"] = session.Key
// pragma: allowlist nextline secret
envVars[session.EnvVarPrefix+"_API_SECRET"] = session.Secret envVars[session.EnvVarPrefix+"_API_SECRET"] = session.Secret
} else if len(session.Name) > 0 { } else if len(session.Name) > 0 {
sn := strings.ToUpper(session.Name) sn := strings.ToUpper(session.Name)
// pragma: allowlist nextline secret
envVars[sn+"_API_KEY"] = session.Key envVars[sn+"_API_KEY"] = session.Key
// pragma: allowlist nextline secret
envVars[sn+"_API_SECRET"] = session.Secret envVars[sn+"_API_SECRET"] = session.Secret
} else { } else {
err = fmt.Errorf("session %s name or env var prefix is not defined", session.Name) err = fmt.Errorf("session %s name or env var prefix is not defined", session.Name)

View File

@ -19,6 +19,7 @@ func NewRedisPersistenceService(config *RedisPersistenceConfig) *RedisPersistenc
client := redis.NewClient(&redis.Options{ client := redis.NewClient(&redis.Options{
Addr: net.JoinHostPort(config.Host, config.Port), Addr: net.JoinHostPort(config.Host, config.Port),
// Username: "", // username is only for redis 6.0 // Username: "", // username is only for redis 6.0
// pragma: allowlist nextline secret
Password: config.Password, // no password set Password: config.Password, // no password set
DB: config.DB, // use default DB DB: config.DB, // use default DB
}) })