mirror of
https://github.com/c9s/bbgo.git
synced 2024-09-20 00:01:09 +00:00
Compare commits
236 Commits
822abfa3da
...
ac31fd0da2
Author | SHA1 | Date | |
---|---|---|---|
|
ac31fd0da2 | ||
|
37106c35b7 | ||
|
8265ada5a0 | ||
|
744ca57c71 | ||
|
17d3097e06 | ||
|
a0c41f89f2 | ||
|
35a6639530 | ||
|
26b1fd2ae7 | ||
|
80430fec46 | ||
|
1f8b2b3710 | ||
|
25a2203000 | ||
|
aca2c32442 | ||
|
df915d6ee8 | ||
|
0d6b7b29d5 | ||
|
2784ef4687 | ||
|
ea8f3a5485 | ||
|
52f32e0ad0 | ||
|
50cdf617f2 | ||
|
de0d11b511 | ||
|
789bb1e53e | ||
|
a9b71adce9 | ||
|
f83491af26 | ||
|
82d07a0098 | ||
|
619cce53f6 | ||
|
643ecde2e9 | ||
|
d7ddc9c462 | ||
|
bd19b63c7b | ||
|
83ed9b0811 | ||
|
34ef50d889 | ||
|
52925c5643 | ||
|
b4f2748892 | ||
|
ceda1e06b9 | ||
|
bc1715f8ad | ||
|
f361b19564 | ||
|
f44486447e | ||
|
a2eca66af5 | ||
|
129e2c438e | ||
|
90749f4873 | ||
|
77dfe213e5 | ||
|
960ea89d8c | ||
|
f24a96c8c3 | ||
|
6ad16b7488 | ||
|
e14f09a914 | ||
|
3cc96ff6ad | ||
|
6ea996bec4 | ||
|
ef935f8ca0 | ||
|
a282654c02 | ||
|
336dd7a108 | ||
|
f2a443a499 | ||
|
63a58e1b12 | ||
|
1b40118bba | ||
|
c75a685cc0 | ||
|
50262f2a84 | ||
|
9fc3a1b44a | ||
|
656112de45 | ||
|
ba73eeaad1 | ||
|
2527c0c7b7 | ||
|
a2f8fe5f72 | ||
|
ed51eff242 | ||
|
f6865f664c | ||
|
24de49860f | ||
|
83dc981c92 | ||
|
ec68e3c5f6 | ||
|
699164484b | ||
|
f27afac77b | ||
|
d404b20bd1 | ||
|
1b8d7bd805 | ||
|
7d034d1ba8 | ||
|
7135895006 | ||
|
ba913ce4de | ||
|
f12ba1adb9 | ||
|
294e529a98 | ||
|
f30aca1b5a | ||
|
f9b9832fff | ||
|
2bf1072977 | ||
|
01f8b78008 | ||
|
4d1c357c3d | ||
|
a4833524cf | ||
|
ed073264f1 | ||
|
ad6056834e | ||
|
8b1306a6a6 | ||
|
d85da78e17 | ||
|
9d581adc04 | ||
|
cff7103ece | ||
|
d501e8ff4d | ||
|
b87213827e | ||
|
ec80cbfd9f | ||
|
04bed165d0 | ||
|
7c4b3e81df | ||
|
cc820d3df0 | ||
|
371db8e7d1 | ||
|
b8abc065de | ||
|
9ebab4f4f7 | ||
|
d9fb9ff3e0 | ||
|
88d7783843 | ||
|
86e464b1bc | ||
|
8e91a023ca | ||
|
8de0c67503 | ||
|
e187614179 | ||
|
bda66040e7 | ||
|
d36bbe5fb5 | ||
|
77b7b29739 | ||
|
1d6282a10b | ||
|
108fb6138a | ||
|
1e2f086643 | ||
|
6011fd5157 | ||
|
652c9b62e8 | ||
|
9939b5ce68 | ||
|
a740ef10c2 | ||
|
f81ce5ce95 | ||
|
4ae8ad77b3 | ||
|
3819feacf3 | ||
|
4af12a7e5d | ||
|
3d4ccd1344 | ||
|
b3c8739983 | ||
|
5edf5a763f | ||
|
7e65aca62e | ||
|
199b86df86 | ||
|
6fb6467d59 | ||
|
2479f2e9fc | ||
|
e77f0cbacb | ||
|
be386d085a | ||
|
ea93bf959a | ||
|
e3079c134c | ||
|
7367ea73b8 | ||
|
f96e0d6552 | ||
|
68e56d76e6 | ||
|
ec51eb7cdd | ||
|
3ec509b379 | ||
|
84edcb0e02 | ||
|
866751cc3d | ||
|
80949bf0e1 | ||
|
86cc821626 | ||
|
90bcd25bef | ||
|
f59ff90c5e | ||
|
3013e37a38 | ||
|
349a3040f3 | ||
|
7fdb3f671f | ||
|
321eb23514 | ||
|
77e185ffa7 | ||
|
e7fd90ed59 | ||
|
9a7517a72a | ||
|
9a1d9ae27b | ||
|
c76a80da6a | ||
|
afac81a3e8 | ||
|
b0cc009d67 | ||
|
0df56ad6e7 | ||
|
1c1959b8a8 | ||
|
9f01dc28c8 | ||
|
e8bd370aa2 | ||
|
5ca1c4fb62 | ||
|
f7dc07327e | ||
|
6ef8aa62e5 | ||
|
5f65e87e89 | ||
|
14fff8dbad | ||
|
92ad80f2f8 | ||
|
0a2d442ceb | ||
|
f7f8ecfd15 | ||
|
40a0585187 | ||
|
9f510da78b | ||
|
c2679b4ae1 | ||
|
106a01e508 | ||
|
02ff04cfb5 | ||
|
80ad5c32b5 | ||
|
e3c4314aed | ||
|
c7f62cf1e5 | ||
|
4bbb9c5e38 | ||
|
b2f1f7d735 | ||
|
1b06fcc961 | ||
|
b820fccce1 | ||
|
bb06a6a046 | ||
|
72575e3cd8 | ||
|
a900c72032 | ||
|
5635e31487 | ||
|
e2d68f2a86 | ||
|
6718087cb1 | ||
|
9136877207 | ||
|
c063df6467 | ||
|
80fc10a1fd | ||
|
fead99aaa6 | ||
|
40d3a40277 | ||
|
055cfbb3ff | ||
|
d18adfcbc7 | ||
|
48eeae0a10 | ||
|
d855d9bbc0 | ||
|
1a1f700122 | ||
|
3a2e4dfd26 | ||
|
6f0cf8f3e0 | ||
|
731fa9af7e | ||
|
a06b63c897 | ||
|
ebaf3a330f | ||
|
a0cbf82d97 | ||
|
9a2b792ed1 | ||
|
2392fddc3c | ||
|
c92c395f67 | ||
|
48029f95cc | ||
|
8911f88ed6 | ||
|
a1c77e42f1 | ||
|
c03f5f47c2 | ||
|
40260a1204 | ||
|
baffefac07 | ||
|
6d3a18ad55 | ||
|
d1617b6a0b | ||
|
b9c41b7ad7 | ||
|
0530809834 | ||
|
d8fad8250c | ||
|
c8aea81505 | ||
|
cec078f4bf | ||
|
648e10fd7c | ||
|
b7d18e687e | ||
|
51c1b995c2 | ||
|
f845918d80 | ||
|
47c7714d33 | ||
|
408a41342b | ||
|
f7ad141b04 | ||
|
90712aff29 | ||
|
0a83c26fd5 | ||
|
1294cd95be | ||
|
9dd85623b9 | ||
|
621a2b86cf | ||
|
714275fedb | ||
|
b27fc896f9 | ||
|
cf42d9d0b0 | ||
|
8773c220f5 | ||
|
b4bb7b6716 | ||
|
57a68f8063 | ||
|
c35805916a | ||
|
df5838dc9e | ||
|
a4f220749a | ||
|
ee4455fa85 | ||
|
dae140aacc | ||
|
4d84308b99 | ||
|
29301cbe7f | ||
|
af1e63f345 | ||
|
e23e8fde1d | ||
|
a484211aa5 |
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v3
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
# list of Docker images to use as base name for tags
|
# list of Docker images to use as base name for tags
|
||||||
images: |
|
images: |
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
|
|
10
.github/workflows/go.yml
vendored
10
.github/workflows/go.yml
vendored
|
@ -2,8 +2,10 @@ name: Go
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches:
|
||||||
|
- "main"
|
||||||
pull_request:
|
pull_request:
|
||||||
|
types: [synchronize, opened, reopened]
|
||||||
branches:
|
branches:
|
||||||
- "main"
|
- "main"
|
||||||
- "v*"
|
- "v*"
|
||||||
|
@ -27,7 +29,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# lfs: 'true'
|
# lfs: 'true'
|
||||||
ssh-key: ${{ secrets.git_ssh_key }}
|
ssh-key: ${{ secrets.git_ssh_key }}
|
||||||
|
@ -88,13 +90,13 @@ jobs:
|
||||||
sed -i -e '/_requestgen.go/d' coverage_dnum.txt
|
sed -i -e '/_requestgen.go/d' coverage_dnum.txt
|
||||||
|
|
||||||
- name: Revive Check
|
- name: Revive Check
|
||||||
uses: morphy2k/revive-action@v2.5.9 # https://github.com/mgechev/revive/issues/956
|
uses: morphy2k/revive-action@v2.5.10 # https://github.com/mgechev/revive/issues/956
|
||||||
with:
|
with:
|
||||||
reporter: github-pr-review
|
reporter: github-pr-review
|
||||||
fail_on_error: true
|
fail_on_error: true
|
||||||
|
|
||||||
- name: Upload Coverage Report
|
- name: Upload Coverage Report
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
files: ./coverage.txt,./coverage_dnum.txt
|
files: ./coverage.txt,./coverage_dnum.txt
|
||||||
|
|
||||||
|
|
4
.github/workflows/node.yml
vendored
4
.github/workflows/node.yml
vendored
|
@ -21,9 +21,9 @@ jobs:
|
||||||
node-version: [ 16.x ]
|
node-version: [ 16.x ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm install -g yarn
|
- run: npm install -g yarn
|
||||||
|
|
48
.github/workflows/python.yml
vendored
48
.github/workflows/python.yml
vendored
|
@ -1,48 +0,0 @@
|
||||||
name: Python
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
paths:
|
|
||||||
- python
|
|
||||||
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
paths:
|
|
||||||
- python
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: [ 3.8 ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Install poetry
|
|
||||||
run: pip install poetry==1.1.13
|
|
||||||
|
|
||||||
- name: Install package
|
|
||||||
run: |
|
|
||||||
cd python
|
|
||||||
poetry install
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
cd python
|
|
||||||
poetry run pytest -v -s tests
|
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
run: |
|
|
||||||
cd python
|
|
||||||
poetry run flake8 .
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -16,13 +16,13 @@ jobs:
|
||||||
id: get_version
|
id: get_version
|
||||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21"
|
go-version: "1.21"
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "16"
|
node-version: "16"
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -14,6 +14,7 @@
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
@ -48,6 +49,7 @@ testoutput
|
||||||
|
|
||||||
*.swp
|
*.swp
|
||||||
/pkg/backtest/assets.go
|
/pkg/backtest/assets.go
|
||||||
|
/data/backtest
|
||||||
|
|
||||||
coverage.txt
|
coverage.txt
|
||||||
coverage_dum.txt
|
coverage_dum.txt
|
||||||
|
|
25
.travis.yml
25
.travis.yml
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.14
|
|
||||||
- 1.15
|
|
||||||
|
|
||||||
services:
|
|
||||||
- redis-server
|
|
||||||
- mysql
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- mysql -e 'CREATE DATABASE bbgo;'
|
|
||||||
- mysql -e 'CREATE DATABASE bbgo_dev;'
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get github.com/c9s/rockhopper/cmd/rockhopper
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- go mod download
|
|
||||||
- make migrations
|
|
||||||
|
|
||||||
script:
|
|
||||||
- bash scripts/test-sqlite3-migrations.sh
|
|
||||||
- bash scripts/test-mysql-migrations.sh
|
|
||||||
- go test -v ./pkg/...
|
|
|
@ -15,9 +15,9 @@ type: application
|
||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.3.5
|
version: 0.4.3
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
appVersion: 1.21.0
|
appVersion: 1.60.0
|
||||||
|
|
55
charts/bbgo/templates/cronjob_sync.yaml
Normal file
55
charts/bbgo/templates/cronjob_sync.yaml
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{{- if .Values.sync.enabled }}
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: {{ include "bbgo.fullname" . }}-sync
|
||||||
|
{{- with .Values.deploymentAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "bbgo.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
schedule: {{ .Values.sync.schedule | quote }}
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: bbgo
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext | nindent 14 }}
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
args:
|
||||||
|
{{- if .Values.metrics.enabled }}
|
||||||
|
- "--metrics"
|
||||||
|
- "--metrics-port"
|
||||||
|
- {{ .Values.metrics.port | quote }}
|
||||||
|
{{- end }}
|
||||||
|
- "sync"
|
||||||
|
- "--config"
|
||||||
|
- "/config/bbgo.yaml"
|
||||||
|
{{- if .Values.sync.since }}
|
||||||
|
- "--since"
|
||||||
|
- {{ .Values.sync.since | quote }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources | nindent 14 }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: config-volume
|
||||||
|
mountPath: /config
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: {{ .Values.dotenv.secret | default .Release.Name }}
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
{{- if .Values.existingConfigMap }}
|
||||||
|
name: {{ .Values.existingConfigMap }}
|
||||||
|
{{- else }}
|
||||||
|
name: {{ include "bbgo.fullname" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end -}}
|
|
@ -98,6 +98,18 @@ spec:
|
||||||
mountPath: /config
|
mountPath: /config
|
||||||
|
|
||||||
# the "env" entries will override the environment variables from envFrom.
|
# the "env" entries will override the environment variables from envFrom.
|
||||||
|
env:
|
||||||
|
- name: USE_MARKETS_CACHE_IN_MEMORY
|
||||||
|
value: "true"
|
||||||
|
{{- if .Values.environment }}
|
||||||
|
- name: BBGO_ENV
|
||||||
|
value: {{ .Values.environment | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if .Values.rbTreeOrderbook.enabled }}
|
||||||
|
- name: ENABLE_RBT_ORDERBOOK
|
||||||
|
value: "true"
|
||||||
|
{{- end }}
|
||||||
envFrom:
|
envFrom:
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ .Values.dotenv.secret | default .Release.Name }}
|
name: {{ .Values.dotenv.secret | default .Release.Name }}
|
||||||
|
|
|
@ -75,11 +75,11 @@ webserver:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
metrics:
|
metrics:
|
||||||
enabled: false
|
enabled: true
|
||||||
port: 9090
|
port: 9090
|
||||||
|
|
||||||
logFormatter:
|
logFormatter:
|
||||||
enabled: false
|
enabled: true
|
||||||
format: json
|
format: json
|
||||||
|
|
||||||
grpc:
|
grpc:
|
||||||
|
@ -89,6 +89,18 @@ grpc:
|
||||||
debug:
|
debug:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
environment: dev
|
||||||
|
|
||||||
|
# this set ENABLE_RBT_ORDERBOOK env
|
||||||
|
rbTreeOrderbook:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
sync:
|
||||||
|
enabled: false
|
||||||
|
schedule: "0 * * * *"
|
||||||
|
## since: sync from time
|
||||||
|
since: false
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||||
# choice for the user. This also increases chances charts run on environments with little
|
# choice for the user. This also increases chances charts run on environments with little
|
||||||
|
|
|
@ -21,7 +21,7 @@ logging:
|
||||||
trade: true
|
trade: true
|
||||||
order: true
|
order: true
|
||||||
fields:
|
fields:
|
||||||
env: staging
|
env: local
|
||||||
|
|
||||||
sessions:
|
sessions:
|
||||||
max:
|
max:
|
||||||
|
@ -59,6 +59,17 @@ crossExchangeStrategies:
|
||||||
# 0.1 pip is 0.01, here we use 10, so we will get 18000.00, 18001.00 and
|
# 0.1 pip is 0.01, here we use 10, so we will get 18000.00, 18001.00 and
|
||||||
# 18002.00
|
# 18002.00
|
||||||
pips: 10
|
pips: 10
|
||||||
persistence:
|
## profitFixer is used for fixing the profit stats and the position
|
||||||
type: redis
|
# profitFixer:
|
||||||
|
# tradesSince: "2024-08-01T15:00:00.000+08:00"
|
||||||
|
circuitBreaker:
|
||||||
|
enabled: true
|
||||||
|
maximumConsecutiveTotalLoss: 36.0
|
||||||
|
maximumConsecutiveLossTimes: 10
|
||||||
|
maximumLossPerRound: 15.0
|
||||||
|
maximumTotalLoss: 80.0
|
||||||
|
ignoreConsecutiveDustLoss: true
|
||||||
|
consecutiveDustLossThreshold: 0.003
|
||||||
|
haltDuration: "30m"
|
||||||
|
maximumHaltTimes: 2
|
||||||
|
maximumHaltTimesExceededPanic: true
|
||||||
|
|
|
@ -58,4 +58,4 @@ bbgo [flags]
|
||||||
* [bbgo userdatastream](bbgo_userdatastream.md) - Listen to session events (orderUpdate, tradeUpdate, balanceUpdate, balanceSnapshot)
|
* [bbgo userdatastream](bbgo_userdatastream.md) - Listen to session events (orderUpdate, tradeUpdate, balanceUpdate, balanceSnapshot)
|
||||||
* [bbgo version](bbgo_version.md) - show version name
|
* [bbgo version](bbgo_version.md) - show version name
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -41,4 +41,4 @@ bbgo account [--session SESSION] [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -50,4 +50,4 @@ bbgo backtest [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -40,4 +40,4 @@ bbgo balances [--session SESSION] [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -39,4 +39,4 @@ bbgo build [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -49,4 +49,4 @@ bbgo cancel-order [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -41,4 +41,4 @@ bbgo deposits [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -9,16 +9,18 @@ bbgo execute-order --session SESSION --symbol SYMBOL --side SIDE --target-quanti
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--deadline duration deadline of the order execution
|
--deadline duration deadline duration of the order execution, e.g. 1h
|
||||||
-h, --help help for execute-order
|
--delay-interval duration order delay time after filled (default 3s)
|
||||||
--price-ticks int the number of price tick for the jump spread, default to 0
|
-h, --help help for execute-order
|
||||||
--session string the exchange session name for sync
|
--order-update-rate-limit string order update rate limit, syntax: 1+1/1m (default "1s")
|
||||||
--side string the trading side: buy or sell
|
--price-ticks int the number of price tick for the jump spread, default to 0
|
||||||
--slice-quantity string slice quantity
|
--session string the exchange session name for sync
|
||||||
--stop-price string stop price (default "0")
|
--side string the trading side: buy or sell
|
||||||
--symbol string the trading pair, like btcusdt
|
--slice-quantity string slice quantity
|
||||||
--target-quantity string target quantity
|
--stop-price string stop price (default "0")
|
||||||
--update-interval duration order update time (default 10s)
|
--symbol string the trading pair, like btcusdt
|
||||||
|
--target-quantity string target quantity
|
||||||
|
--update-interval duration order update time (default 10s)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
@ -48,4 +50,4 @@ bbgo execute-order --session SESSION --symbol SYMBOL --side SIDE --target-quanti
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -42,4 +42,4 @@ bbgo get-order --session SESSION --order-id ORDER_ID [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -45,4 +45,4 @@ bbgo hoptimize [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -42,4 +42,4 @@ bbgo kline [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -41,4 +41,4 @@ bbgo list-orders open|closed --session SESSION --symbol SYMBOL [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -38,4 +38,4 @@ margin related history
|
||||||
* [bbgo margin loans](bbgo_margin_loans.md) - query loans history
|
* [bbgo margin loans](bbgo_margin_loans.md) - query loans history
|
||||||
* [bbgo margin repays](bbgo_margin_repays.md) - query repay history
|
* [bbgo margin repays](bbgo_margin_repays.md) - query repay history
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -41,4 +41,4 @@ bbgo margin interests --session=SESSION_NAME --asset=ASSET [flags]
|
||||||
|
|
||||||
* [bbgo margin](bbgo_margin.md) - margin related history
|
* [bbgo margin](bbgo_margin.md) - margin related history
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -41,4 +41,4 @@ bbgo margin loans --session=SESSION_NAME --asset=ASSET [flags]
|
||||||
|
|
||||||
* [bbgo margin](bbgo_margin.md) - margin related history
|
* [bbgo margin](bbgo_margin.md) - margin related history
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -41,4 +41,4 @@ bbgo margin repays --session=SESSION_NAME --asset=ASSET [flags]
|
||||||
|
|
||||||
* [bbgo margin](bbgo_margin.md) - margin related history
|
* [bbgo margin](bbgo_margin.md) - margin related history
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -40,4 +40,4 @@ bbgo market [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -44,4 +44,4 @@ bbgo optimize [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -42,4 +42,4 @@ bbgo orderbook --session=[exchange_name] --symbol=[pair_name] [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -40,4 +40,4 @@ bbgo orderupdate [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -49,4 +49,4 @@ bbgo pnl [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -51,4 +51,4 @@ bbgo run [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -46,4 +46,4 @@ bbgo submit-order --session SESSION --symbol SYMBOL --side SIDE --quantity QUANT
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -42,4 +42,4 @@ bbgo sync [--session=[exchange_name]] [--symbol=[pair_name]] [[--since=yyyy/mm/d
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -42,4 +42,4 @@ bbgo trades --session=[exchange_name] --symbol=[pair_name] [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -40,4 +40,4 @@ bbgo tradeupdate --session=[exchange_name] [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -42,4 +42,4 @@ bbgo transfer-history [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -40,4 +40,4 @@ bbgo userdatastream [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -39,4 +39,4 @@ bbgo version [flags]
|
||||||
|
|
||||||
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
* [bbgo](bbgo.md) - bbgo is a crypto trading bot
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 20-May-2024
|
###### Auto generated by spf13/cobra on 16-Sep-2024
|
||||||
|
|
|
@ -17,7 +17,7 @@ TELEGRAM_BOT_TOKEN=347374838:ABFTjfiweajfiawoejfiaojfeijoaef
|
||||||
```
|
```
|
||||||
|
|
||||||
For the telegram chat authentication (your bot needs to verify it's you), if you only need a fixed authentication token,
|
For the telegram chat authentication (your bot needs to verify it's you), if you only need a fixed authentication token,
|
||||||
you can set `TELEGRAM_AUTH_TOKEN` in the `.env.local` file, e.g.,
|
you can set `TELEGRAM_BOT_AUTH_TOKEN` in the `.env.local` file, e.g.,
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
TELEGRAM_BOT_AUTH_TOKEN=itsme55667788
|
TELEGRAM_BOT_AUTH_TOKEN=itsme55667788
|
||||||
|
|
62
doc/release/v1.60.0.md
Normal file
62
doc/release/v1.60.0.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
[Full Changelog](https://github.com/c9s/bbgo/compare/v1.59.2...main)
|
||||||
|
|
||||||
|
- [#1683](https://github.com/c9s/bbgo/pull/1683): dep: bump codecov/codecov-action from 3 to 4
|
||||||
|
- [#1680](https://github.com/c9s/bbgo/pull/1680): dep: bump docker/setup-buildx-action from 1 to 3
|
||||||
|
- [#1684](https://github.com/c9s/bbgo/pull/1684): dep: bump docker/metadata-action from 3 to 5
|
||||||
|
- [#1681](https://github.com/c9s/bbgo/pull/1681): dep: bump actions/checkout from 2 to 4
|
||||||
|
- [#1689](https://github.com/c9s/bbgo/pull/1689): FIX: fix float64 series use mean or stdev function result is zero
|
||||||
|
- [#1697](https://github.com/c9s/bbgo/pull/1697): FEATURE: redesign and refactor twap order executor
|
||||||
|
- [#1698](https://github.com/c9s/bbgo/pull/1698): FEATURE: update get trades api
|
||||||
|
- [#1692](https://github.com/c9s/bbgo/pull/1692): DELETE: delete python
|
||||||
|
- [#1693](https://github.com/c9s/bbgo/pull/1693): FIX: fix binance exchange query futures order
|
||||||
|
- [#1696](https://github.com/c9s/bbgo/pull/1696): FIX: [core] setting.InitializeConverter could return a nil converter object
|
||||||
|
- [#1695](https://github.com/c9s/bbgo/pull/1695): FIX: [max] fix GetDepositHistoryRequest
|
||||||
|
- [#1694](https://github.com/c9s/bbgo/pull/1694): FIX: [max] fix max withdrawal api parameters
|
||||||
|
- [#1691](https://github.com/c9s/bbgo/pull/1691): FEATURE: [bitget] upgrade public websocket to v2
|
||||||
|
- [#1690](https://github.com/c9s/bbgo/pull/1690): FEATURE: improve trade/order converter
|
||||||
|
- [#1688](https://github.com/c9s/bbgo/pull/1688): FEATURE: [xdepthmaker] separate hedge symbol
|
||||||
|
- [#1685](https://github.com/c9s/bbgo/pull/1685): IMPROVE: [xalign] improve notification
|
||||||
|
- [#1679](https://github.com/c9s/bbgo/pull/1679): FIX: [xalign] fix max withdraw history api query
|
||||||
|
- [#1671](https://github.com/c9s/bbgo/pull/1671): dep: bump morphy2k/revive-action from 2.5.4 to 2.5.9
|
||||||
|
- [#1672](https://github.com/c9s/bbgo/pull/1672): dep: bump docker/login-action from 1 to 3
|
||||||
|
- [#1674](https://github.com/c9s/bbgo/pull/1674): dep: bump docker/setup-qemu-action from 1 to 3
|
||||||
|
- [#1676](https://github.com/c9s/bbgo/pull/1676): dep: bump docker/build-push-action from 2 to 6
|
||||||
|
- [#1677](https://github.com/c9s/bbgo/pull/1677): dep: bump golangci/golangci-lint-action from 4 to 6
|
||||||
|
- [#1678](https://github.com/c9s/bbgo/pull/1678): FEATURE: [xalign] add withdraw detection
|
||||||
|
- [#1673](https://github.com/c9s/bbgo/pull/1673): dep: bump actions/setup-go from 4 to 5
|
||||||
|
- [#1675](https://github.com/c9s/bbgo/pull/1675): dep: bump actions/cache from 2 to 4
|
||||||
|
- [#1670](https://github.com/c9s/bbgo/pull/1670): CI: Create .github/dependabot.yml
|
||||||
|
- [#1669](https://github.com/c9s/bbgo/pull/1669): FEATURE: [max] update max api url
|
||||||
|
- [#1668](https://github.com/c9s/bbgo/pull/1668): REFACTOR: support custom order by column
|
||||||
|
- [#1663](https://github.com/c9s/bbgo/pull/1663): FIX: [rebalance] round down quantity
|
||||||
|
- [#1667](https://github.com/c9s/bbgo/pull/1667): FIX: [common] fix profit fixer batch query
|
||||||
|
- [#1666](https://github.com/c9s/bbgo/pull/1666): FEATURE: [liqmaker] add profit fixer support
|
||||||
|
- [#1665](https://github.com/c9s/bbgo/pull/1665): IMPROVE: improve price volume slice parsing
|
||||||
|
- [#1664](https://github.com/c9s/bbgo/pull/1664): FEATURE: update max api to latest version
|
||||||
|
- [#1383](https://github.com/c9s/bbgo/pull/1383): FEATURE: merge recover logic and run periodically
|
||||||
|
- [#1656](https://github.com/c9s/bbgo/pull/1656): REFACTOR: [autobuy] replace threshold with minBaseBalance
|
||||||
|
- [#1644](https://github.com/c9s/bbgo/pull/1644): REFACTOR: Extract and move FeeBudget from xgap
|
||||||
|
- [#1662](https://github.com/c9s/bbgo/pull/1662): FIX: [atrpin] fix position quantity
|
||||||
|
- [#1661](https://github.com/c9s/bbgo/pull/1661): IMPROVE: [batch] improve trade batch query
|
||||||
|
- [#1648](https://github.com/c9s/bbgo/pull/1648): FEATURE: [atrpin] take profit by expected base balance
|
||||||
|
- [#1660](https://github.com/c9s/bbgo/pull/1660): FIX: fix trade insertion for inserted_at field
|
||||||
|
- [#1659](https://github.com/c9s/bbgo/pull/1659): FEATURE: [core] add syncBufferPeriod config and set default to -30 mins
|
||||||
|
- [#1657](https://github.com/c9s/bbgo/pull/1657): MINOR: compile and update migration package for trades.inserted_at
|
||||||
|
- [#1646](https://github.com/c9s/bbgo/pull/1646): MINOR: add inserted_at column to trades
|
||||||
|
- [#1655](https://github.com/c9s/bbgo/pull/1655): FEATURE: [xgap] add dailyTargetVolume option
|
||||||
|
- [#1645](https://github.com/c9s/bbgo/pull/1645): FEATURE: [dca2] make the take-profit order of round from order to orders
|
||||||
|
- [#1654](https://github.com/c9s/bbgo/pull/1654): FIX: [types] improve AdjustQuantityByMinNotional
|
||||||
|
- [#1653](https://github.com/c9s/bbgo/pull/1653): FIX: [xgap] make sourceBook optional
|
||||||
|
- [#1652](https://github.com/c9s/bbgo/pull/1652): FIX: [xgap] fix empty source book pricing issue
|
||||||
|
- [#1649](https://github.com/c9s/bbgo/pull/1649): FEATURE: add BasicCircuitBreaker
|
||||||
|
- [#1650](https://github.com/c9s/bbgo/pull/1650): FIX: [okex] fix order book subscription channels
|
||||||
|
- [#1651](https://github.com/c9s/bbgo/pull/1651): FEATURE: [okx] add conn info event
|
||||||
|
- [#1647](https://github.com/c9s/bbgo/pull/1647): FIX: [retry] add initialAttempts to the order trades query backoff
|
||||||
|
- [#1637](https://github.com/c9s/bbgo/pull/1637): CHORE: [atrpin] add symbol and window log fields
|
||||||
|
- [#1643](https://github.com/c9s/bbgo/pull/1643): FIX: [binance] implement query trade for binance margin trading
|
||||||
|
- [#1640](https://github.com/c9s/bbgo/pull/1640): FEATURE: [dca2] change state recovery logic
|
||||||
|
- [#1642](https://github.com/c9s/bbgo/pull/1642): Refactor: add average depth price method
|
||||||
|
- [#1641](https://github.com/c9s/bbgo/pull/1641): FEATURE: [dca2] new flag UniversalCancelAllOrdersWhenClose to decide …
|
||||||
|
- [#1638](https://github.com/c9s/bbgo/pull/1638): FEATURE: [dca2] store price quantity pairs of the open-position order…
|
||||||
|
- [#1639](https://github.com/c9s/bbgo/pull/1639): REFACTOR: move maker tools
|
||||||
|
- [#1625](https://github.com/c9s/bbgo/pull/1625): CHORE: fix function names in comment
|
35
doc/release/v1.60.1.md
Normal file
35
doc/release/v1.60.1.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- fixed xmaker bugs
|
||||||
|
- updated helm chart for sync cronjob
|
||||||
|
- fixed max deposits api
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/c9s/bbgo/compare/v1.60.0...main)
|
||||||
|
|
||||||
|
- [#1727](https://github.com/c9s/bbgo/pull/1727): FIX: update timeInForce for binance margin order
|
||||||
|
- [#1729](https://github.com/c9s/bbgo/pull/1729): FIX: [max] fix v3 deposit state conversion
|
||||||
|
- [#1723](https://github.com/c9s/bbgo/pull/1723): FIX: [xmaker] avoid calculate margin from 0.0 signal
|
||||||
|
- [#1721](https://github.com/c9s/bbgo/pull/1721): FIX: [xmaker] fix aggregatePrice method
|
||||||
|
- [#1725](https://github.com/c9s/bbgo/pull/1725): IMPROVE: [xmaker] improve hedge margin account leverage calculation
|
||||||
|
- [#1722](https://github.com/c9s/bbgo/pull/1722): FEATURE: [xmaker] add signals
|
||||||
|
- [#1720](https://github.com/c9s/bbgo/pull/1720): FEATURE: [xmaker] margin credit improvement
|
||||||
|
- [#1718](https://github.com/c9s/bbgo/pull/1718): FEATURE: [xmaker] add more config metrics
|
||||||
|
- [#1719](https://github.com/c9s/bbgo/pull/1719): IMPROVE: [xmaker] fix bollinger band price calculation
|
||||||
|
- [#1709](https://github.com/c9s/bbgo/pull/1709): IMPROVE: [xmaker] improve profit stats ticker and integrate rate limiter
|
||||||
|
- [#1708](https://github.com/c9s/bbgo/pull/1708): FEATURE: [xmaker] integrate circuit breaker
|
||||||
|
- [#1712](https://github.com/c9s/bbgo/pull/1712): FEATURE: [xmaker] add profit fixer
|
||||||
|
- [#1710](https://github.com/c9s/bbgo/pull/1710): IMPROVE: [xmaker] improve stability
|
||||||
|
- [#1717](https://github.com/c9s/bbgo/pull/1717): REFACTOR: [xmaker] refactor hedge worker and quote worker
|
||||||
|
- [#1716](https://github.com/c9s/bbgo/pull/1716): FIX: [xmaker] profit object can be nil
|
||||||
|
- [#1707](https://github.com/c9s/bbgo/pull/1707): FIX: [xmaker] position metrics missing label
|
||||||
|
- [#1715](https://github.com/c9s/bbgo/pull/1715): UPGRADE: [go] upgrade packages that are too old
|
||||||
|
- [#1713](https://github.com/c9s/bbgo/pull/1713): FEATURE: [chart] add env vars section
|
||||||
|
- [#1711](https://github.com/c9s/bbgo/pull/1711): FEATURE: [binance] add new margin order side effect AUTO_BORROW_REPAY
|
||||||
|
- [#1705](https://github.com/c9s/bbgo/pull/1705): FIX: [k8s] fix sync.enabled option
|
||||||
|
- [#1704](https://github.com/c9s/bbgo/pull/1704): FEATURE: [k8s] add cronjob for sync
|
||||||
|
- [#1700](https://github.com/c9s/bbgo/pull/1700): Fix: [autobuy] fix error when bollinger settings is not set
|
||||||
|
- [#1703](https://github.com/c9s/bbgo/pull/1703): FEATURE: [core] add position metrics
|
||||||
|
- [#1702](https://github.com/c9s/bbgo/pull/1702): IMPROVE: improve balance related metrics
|
||||||
|
- [#1699](https://github.com/c9s/bbgo/pull/1699): REFACTOR: [twap] upgrade twap command and add optional order update rate limiter
|
||||||
|
- [#1701](https://github.com/c9s/bbgo/pull/1701): RELEASE: v1.60.0
|
||||||
|
- [#1714](https://github.com/c9s/bbgo/pull/1714): dep: bump actions/setup-node from 2 to 4
|
18
doc/release/v1.60.2.md
Normal file
18
doc/release/v1.60.2.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[Full Changelog](https://github.com/c9s/bbgo/compare/v1.60.1...main)
|
||||||
|
|
||||||
|
- [#1739](https://github.com/c9s/bbgo/pull/1739): FEATURE: [dca2] set exchange fee rate for round position
|
||||||
|
- [#1738](https://github.com/c9s/bbgo/pull/1738): FEATURE: [okx] update symbols to latest
|
||||||
|
- [#1737](https://github.com/c9s/bbgo/pull/1737): FEATURE: [xmaker] implement tryArbitrage
|
||||||
|
- [#1730](https://github.com/c9s/bbgo/pull/1730): FEATURE: [xmaker] add market trade signal
|
||||||
|
- [#1734](https://github.com/c9s/bbgo/pull/1734): REFACTOR: [xmaker] refactor for supporting ioc arb [part1]
|
||||||
|
- [#1736](https://github.com/c9s/bbgo/pull/1736): MINOR: [session] remove environment nil validation log
|
||||||
|
- [#1742](https://github.com/c9s/bbgo/pull/1742): FIX: types/stream: change errorf to warnf
|
||||||
|
- [#1741](https://github.com/c9s/bbgo/pull/1741): FIX: upgrade github.com/c9s/requestgen to 1.4.3
|
||||||
|
- [#1740](https://github.com/c9s/bbgo/pull/1740): FIX: upgrade requestgen and re-generate max cancel order request files
|
||||||
|
- [#1726](https://github.com/c9s/bbgo/pull/1726): dep: bump morphy2k/revive-action from 2.5.9 to 2.5.10
|
||||||
|
- [#1735](https://github.com/c9s/bbgo/pull/1735): FIX: configure environment
|
||||||
|
- [#1724](https://github.com/c9s/bbgo/pull/1724): FIX: fix slice init length
|
||||||
|
- [#1733](https://github.com/c9s/bbgo/pull/1733): FIX: [bbgo] fix the defaults / initialize steps
|
||||||
|
- [#1732](https://github.com/c9s/bbgo/pull/1732): FIX: fix env name
|
||||||
|
- [#1728](https://github.com/c9s/bbgo/pull/1728): FIX: [core] fix memory leak
|
||||||
|
- [#1731](https://github.com/c9s/bbgo/pull/1731): FIX: fix json tag
|
4
doc/release/v1.60.3.md
Normal file
4
doc/release/v1.60.3.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[Full Changelog](https://github.com/c9s/bbgo/compare/v1.60.2...main)
|
||||||
|
|
||||||
|
- FIX: fix xmaker default price
|
||||||
|
- [#1744](https://github.com/c9s/bbgo/pull/1744): call b.EmitNew() when new order is added into activeorderbook
|
|
@ -1,9 +1,13 @@
|
||||||
## Back-testing
|
## Back-testing
|
||||||
|
|
||||||
*Before you start back-testing, you need to setup [MySQL](../../README.md#configure-mysql-database) or [SQLite3
|
Currently bbgo supports two ways to run backtests:
|
||||||
|
|
||||||
|
1: Through csv data source (supported right now are binance, bybit and OkEx)
|
||||||
|
|
||||||
|
2: Alternatively run backtests through [MySQL](../../README.md#configure-mysql-database) or [SQLite3
|
||||||
](../../README.md#configure-sqlite3-database). Using MySQL is highly recommended.*
|
](../../README.md#configure-sqlite3-database). Using MySQL is highly recommended.*
|
||||||
|
|
||||||
First, you need to add the back-testing config to your `bbgo.yaml`:
|
Let's start by adding the back-testing section to your config eg: `bbgo.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
backtest:
|
backtest:
|
||||||
|
@ -41,8 +45,11 @@ Note on date formats, the following date formats are supported:
|
||||||
* RFC822, which looks like `02 Jan 06 15:04 MST`
|
* RFC822, which looks like `02 Jan 06 15:04 MST`
|
||||||
* You can also use `2021-11-26T15:04:56`
|
* You can also use `2021-11-26T15:04:56`
|
||||||
|
|
||||||
And then, you can sync remote exchange k-lines (candle bars) data for back-testing:
|
And then, you can sync remote exchange k-lines (candle bars) data for back-testing through csv data source:
|
||||||
|
```sh
|
||||||
|
bbgo backtest -v --csv --verify --config config/grid.yaml
|
||||||
|
```
|
||||||
|
or use the sql data source like so:
|
||||||
```sh
|
```sh
|
||||||
bbgo backtest -v --sync --config config/grid.yaml
|
bbgo backtest -v --sync --config config/grid.yaml
|
||||||
```
|
```
|
||||||
|
@ -67,6 +74,11 @@ Run back-test:
|
||||||
```sh
|
```sh
|
||||||
bbgo backtest --base-asset-baseline --config config/grid.yaml
|
bbgo backtest --base-asset-baseline --config config/grid.yaml
|
||||||
```
|
```
|
||||||
|
or through csv data source
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bbgo backtest -v --csv --base-asset-baseline --config config/grid.yaml --output data/backtest
|
||||||
|
```
|
||||||
|
|
||||||
If you're developing a strategy, you might want to start with a command like this:
|
If you're developing a strategy, you might want to start with a command like this:
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ var rootCmd = &cobra.Command{
|
||||||
stream.SetPublicOnly()
|
stream.SetPublicOnly()
|
||||||
stream.Subscribe(types.BookChannel, symbol, types.SubscribeOptions{})
|
stream.Subscribe(types.BookChannel, symbol, types.SubscribeOptions{})
|
||||||
|
|
||||||
streamBook := types.NewStreamBook(symbol)
|
streamBook := types.NewStreamBook(symbol, exchange.Name())
|
||||||
streamBook.BindStream(stream)
|
streamBook.BindStream(stream)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|
96
go.mod
96
go.mod
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
module github.com/c9s/bbgo
|
module github.com/c9s/bbgo
|
||||||
|
|
||||||
go 1.21
|
go 1.21.0
|
||||||
|
|
||||||
toolchain go1.21.6
|
toolchain go1.21.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||||
github.com/Masterminds/squirrel v1.5.3
|
github.com/Masterminds/squirrel v1.5.3
|
||||||
github.com/adshao/go-binance/v2 v2.5.0
|
github.com/adshao/go-binance/v2 v2.6.0
|
||||||
github.com/c-bata/goptuna v0.8.1
|
github.com/c-bata/goptuna v0.8.1
|
||||||
github.com/c9s/requestgen v1.3.6
|
github.com/c9s/requestgen v1.4.3
|
||||||
github.com/c9s/rockhopper/v2 v2.0.4
|
github.com/c9s/rockhopper/v2 v2.0.4
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0
|
github.com/cenkalti/backoff/v4 v4.2.0
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8
|
github.com/cheggaaa/pb/v3 v3.0.8
|
||||||
|
@ -20,13 +20,13 @@ require (
|
||||||
github.com/fatih/camelcase v1.0.0
|
github.com/fatih/camelcase v1.0.0
|
||||||
github.com/fatih/color v1.14.1
|
github.com/fatih/color v1.14.1
|
||||||
github.com/gertd/go-pluralize v0.2.1
|
github.com/gertd/go-pluralize v0.2.1
|
||||||
github.com/gin-contrib/cors v1.3.1
|
github.com/gin-contrib/cors v1.7.2
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/go-redis/redis/v8 v8.8.0
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/gofrs/flock v0.8.1
|
github.com/gofrs/flock v0.8.1
|
||||||
github.com/google/uuid v1.4.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/heroku/rollrus v0.2.0
|
github.com/heroku/rollrus v0.2.0
|
||||||
github.com/jedib0t/go-pretty/v6 v6.5.8
|
github.com/jedib0t/go-pretty/v6 v6.5.8
|
||||||
github.com/jmoiron/sqlx v1.3.4
|
github.com/jmoiron/sqlx v1.3.4
|
||||||
|
@ -55,72 +55,77 @@ require (
|
||||||
github.com/zserge/lorca v0.1.9
|
github.com/zserge/lorca v0.1.9
|
||||||
go.uber.org/mock v0.4.0
|
go.uber.org/mock v0.4.0
|
||||||
go.uber.org/multierr v1.11.0
|
go.uber.org/multierr v1.11.0
|
||||||
golang.org/x/oauth2 v0.15.0
|
golang.org/x/oauth2 v0.22.0
|
||||||
golang.org/x/sync v0.7.0
|
golang.org/x/sync v0.8.0
|
||||||
golang.org/x/time v0.5.0
|
golang.org/x/time v0.6.0
|
||||||
gonum.org/v1/gonum v0.8.2
|
gonum.org/v1/gonum v0.8.2
|
||||||
google.golang.org/api v0.153.0
|
google.golang.org/api v0.194.0
|
||||||
google.golang.org/grpc v1.59.0
|
google.golang.org/grpc v1.65.0
|
||||||
google.golang.org/protobuf v1.31.0
|
google.golang.org/protobuf v1.34.2
|
||||||
gopkg.in/tucnak/telebot.v2 v2.5.0
|
gopkg.in/tucnak/telebot.v2 v2.5.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.23.3 // indirect
|
cloud.google.com/go/auth v0.9.1 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/VividCortex/ewma v1.1.1 // indirect
|
github.com/VividCortex/ewma v1.1.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bitly/go-simplejson v0.5.1 // indirect
|
github.com/bitly/go-simplejson v0.5.1 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.12.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/cockroachdb/apd v1.1.0 // indirect
|
github.com/cockroachdb/apd v1.1.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/denisenkom/go-mssqldb v0.12.3 // indirect
|
github.com/denisenkom/go-mssqldb v0.12.3 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||||
github.com/go-test/deep v1.0.6 // indirect
|
github.com/go-test/deep v1.0.6 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
github.com/google/s2a-go v0.1.8 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lestrrat-go/strftime v1.0.0 // indirect
|
github.com/lestrrat-go/strftime v1.0.0 // indirect
|
||||||
github.com/lib/pq v1.10.9 // indirect
|
github.com/lib/pq v1.10.9 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
github.com/mattn/go-sqlite3 v1.14.23 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/prometheus/common v0.32.1 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
|
@ -137,23 +142,24 @@ require (
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tebeka/strftime v0.1.3 // indirect
|
github.com/tebeka/strftime v0.1.3 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/otel v0.19.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v0.19.0 // indirect
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v0.19.0 // indirect
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||||
golang.org/x/crypto v0.22.0 // indirect
|
golang.org/x/arch v0.9.0 // indirect
|
||||||
|
golang.org/x/crypto v0.26.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
|
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
|
||||||
golang.org/x/image v0.5.0 // indirect
|
golang.org/x/image v0.5.0 // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mod v0.20.0 // indirect
|
||||||
golang.org/x/net v0.24.0 // indirect
|
golang.org/x/net v0.28.0 // indirect
|
||||||
golang.org/x/sys v0.19.0 // indirect
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
golang.org/x/term v0.19.0 // indirect
|
golang.org/x/term v0.23.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.17.0 // indirect
|
||||||
golang.org/x/tools v0.20.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
245
go.sum
245
go.sum
|
@ -13,16 +13,18 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go/auth v0.9.1 h1:+pMtLEV2k0AXKvs/tGZojuj6QaioxfUjOpMsG5Gtx+w=
|
||||||
|
cloud.google.com/go/auth v0.9.1/go.mod h1:Sw8ocT5mhhXxFklyhT12Eiy0ed6tTrPMCJjSI8KhYLk=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||||
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
|
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
|
||||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
@ -44,16 +46,16 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
|
github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
|
||||||
github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||||
github.com/adshao/go-binance/v2 v2.5.0 h1:mk8ylSjIzDYVBF9Wf2KXu6GWD/Ws4LLzD9q2R2mqZB0=
|
github.com/adshao/go-binance/v2 v2.6.0 h1:sXPkfix+SgBojJmkt+sNJbJBQZOJK5GFP/WtAu+B5r0=
|
||||||
github.com/adshao/go-binance/v2 v2.5.0/go.mod h1:41Up2dG4NfMXpCldrDPETEtiOq+pHoGsFZ73xGgaumo=
|
github.com/adshao/go-binance/v2 v2.6.0/go.mod h1:41Up2dG4NfMXpCldrDPETEtiOq+pHoGsFZ73xGgaumo=
|
||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
@ -77,13 +79,15 @@ github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
||||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/c-bata/goptuna v0.8.1 h1:25+n1MLv0yvCsD56xv4nqIus3oLHL9GuPAZDLIqmX1U=
|
github.com/c-bata/goptuna v0.8.1 h1:25+n1MLv0yvCsD56xv4nqIus3oLHL9GuPAZDLIqmX1U=
|
||||||
github.com/c-bata/goptuna v0.8.1/go.mod h1:knmS8+Iyq5PPy1YUeIEq0pMFR4Y6x7z/CySc9HlZTCY=
|
github.com/c-bata/goptuna v0.8.1/go.mod h1:knmS8+Iyq5PPy1YUeIEq0pMFR4Y6x7z/CySc9HlZTCY=
|
||||||
github.com/c9s/requestgen v1.3.6 h1:ul7dZ2uwGYjNBjreooRfSY10WTXvQmQSjZsHebz6QfE=
|
github.com/c9s/requestgen v1.4.3 h1:0QZ27RVBLb9QuBKfiSBTOB5zSUuasrJm2p6/GZZHZZw=
|
||||||
github.com/c9s/requestgen v1.3.6/go.mod h1:QwkZudcv84kJ8g9+E0RDTj+13btFXbTvv2aI+zbuLbc=
|
github.com/c9s/requestgen v1.4.3/go.mod h1:3gk1M2ihvNU2wWl7WLUc09myp7XpHMP33Dx96+Vr8A0=
|
||||||
github.com/c9s/rockhopper/v2 v2.0.4 h1:1cQEzU7rzCSz09B2RYdyPWwBW9gZ/DoFqD1b2xLLmAk=
|
github.com/c9s/rockhopper/v2 v2.0.4 h1:1cQEzU7rzCSz09B2RYdyPWwBW9gZ/DoFqD1b2xLLmAk=
|
||||||
github.com/c9s/rockhopper/v2 v2.0.4/go.mod h1:x0XuYI2Su3kS/74UYu/3Cqc9m5Dtzqh7j7JZarczfss=
|
github.com/c9s/rockhopper/v2 v2.0.4/go.mod h1:x0XuYI2Su3kS/74UYu/3Cqc9m5Dtzqh7j7JZarczfss=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
||||||
|
@ -91,13 +95,10 @@ github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA=
|
github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA=
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA=
|
github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
|
||||||
github.com/chewxy/hm v1.0.0/go.mod h1:qg9YI4q6Fkj/whwHR1D+bOGeF7SniIP40VweVepLjg0=
|
github.com/chewxy/hm v1.0.0/go.mod h1:qg9YI4q6Fkj/whwHR1D+bOGeF7SniIP40VweVepLjg0=
|
||||||
github.com/chewxy/math32 v1.0.0/go.mod h1:Miac6hA1ohdDUTagnvJy/q+aNnEk16qWUdb8ZVhvCN0=
|
github.com/chewxy/math32 v1.0.0/go.mod h1:Miac6hA1ohdDUTagnvJy/q+aNnEk16qWUdb8ZVhvCN0=
|
||||||
github.com/chewxy/math32 v1.0.6/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs=
|
github.com/chewxy/math32 v1.0.6/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs=
|
||||||
|
@ -106,6 +107,10 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cfssl v0.0.0-20190808011637-b1ec8c586c2a/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
github.com/cloudflare/cfssl v0.0.0-20190808011637-b1ec8c586c2a/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
@ -153,25 +158,25 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||||
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
|
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
|
||||||
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
|
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
|
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||||
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
|
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
@ -182,18 +187,21 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-redis/redis/v8 v8.8.0 h1:fDZP58UN/1RD3DjtTXP/fFZ04TFohSYhjZDkcDe2dnw=
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
github.com/go-redis/redis/v8 v8.8.0/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
|
@ -201,8 +209,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
||||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
|
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
|
||||||
github.com/go-test/deep v1.0.6/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
github.com/go-test/deep v1.0.6/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
@ -245,9 +253,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
|
github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
@ -277,24 +284,24 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorgonia/bindgen v0.0.0-20180812032444-09626750019e/go.mod h1:YzKk63P9jQHkwAo2rXHBv02yPxDzoQT2cBV0x5bGV/8=
|
github.com/gorgonia/bindgen v0.0.0-20180812032444-09626750019e/go.mod h1:YzKk63P9jQHkwAo2rXHBv02yPxDzoQT2cBV0x5bGV/8=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
@ -321,7 +328,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/heroku/rollrus v0.2.0 h1:b3AgcXJKFJNUwbQOC2S69/+mxuTpe4laznem9VJdPEo=
|
github.com/heroku/rollrus v0.2.0 h1:b3AgcXJKFJNUwbQOC2S69/+mxuTpe4laznem9VJdPEo=
|
||||||
github.com/heroku/rollrus v0.2.0/go.mod h1:B3MwEcr9nmf4xj0Sr5l9eSht7wLKMa1C+9ajgAU79ek=
|
github.com/heroku/rollrus v0.2.0/go.mod h1:B3MwEcr9nmf4xj0Sr5l9eSht7wLKMa1C+9ajgAU79ek=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
@ -384,7 +390,6 @@ github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
@ -398,9 +403,11 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
@ -421,9 +428,8 @@ github.com/leekchan/accounting v0.0.0-20191218023648-17a4ce5f94d4 h1:KZzDAtJ7ZLm
|
||||||
github.com/leekchan/accounting v0.0.0-20191218023648-17a4ce5f94d4/go.mod h1:3timm6YPhY3YDaGxl0q3eaflX0eoSx3FXn7ckHe4tO0=
|
github.com/leekchan/accounting v0.0.0-20191218023648-17a4ce5f94d4/go.mod h1:3timm6YPhY3YDaGxl0q3eaflX0eoSx3FXn7ckHe4tO0=
|
||||||
github.com/leesper/go_rng v0.0.0-20171009123644-5344a9259b21/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U=
|
github.com/leesper/go_rng v0.0.0-20171009123644-5344a9259b21/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U=
|
||||||
github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U=
|
github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U=
|
||||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
|
||||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
||||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.2.0+incompatible h1:eXEwY0f2h6mcobdAxm4VRSWds4tqmlLdUqxu8ybiEEA=
|
github.com/lestrrat-go/file-rotatelogs v2.2.0+incompatible h1:eXEwY0f2h6mcobdAxm4VRSWds4tqmlLdUqxu8ybiEEA=
|
||||||
|
@ -466,8 +472,8 @@ github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lL
|
||||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||||
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
|
@ -497,22 +503,17 @@ github.com/muesli/kmeans v0.3.0 h1:cI2cpeS8m3pm+gTOdzl+7SlzZYSe+x0XoqXUyUvb1ro=
|
||||||
github.com/muesli/kmeans v0.3.0/go.mod h1:eNyybq0tX9/iBEP6EMU4Y7dpmGK0uEhODdZpnG1a/iQ=
|
github.com/muesli/kmeans v0.3.0/go.mod h1:eNyybq0tX9/iBEP6EMU4Y7dpmGK0uEhODdZpnG1a/iQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
|
|
||||||
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
|
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -637,8 +638,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
|
@ -649,10 +648,8 @@ github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIU
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
|
||||||
github.com/valyala/fastjson v1.5.1 h1:SXaQZVSwLjZOVhDEhjiCcDtnX0Feu7Z7A1+C5atpoHM=
|
github.com/valyala/fastjson v1.5.1 h1:SXaQZVSwLjZOVhDEhjiCcDtnX0Feu7Z7A1+C5atpoHM=
|
||||||
github.com/valyala/fastjson v1.5.1/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
github.com/valyala/fastjson v1.5.1/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||||
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
|
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
|
||||||
|
@ -666,7 +663,6 @@ github.com/xtgo/set v1.0.0/go.mod h1:d3NHzGzSa0NmB2NhFyECA+QdRp29oEn2xbT+TpeFoM8
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
|
@ -681,14 +677,14 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||||
go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||||
go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
|
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||||
go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
|
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||||
go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
|
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||||
go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
|
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||||
go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
|
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||||
go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
@ -702,9 +698,8 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
@ -721,8 +716,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -764,8 +759,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -796,30 +791,27 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -828,15 +820,13 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -855,10 +845,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -881,24 +868,23 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -907,13 +893,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -964,10 +950,9 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -1001,16 +986,14 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4=
|
google.golang.org/api v0.194.0 h1:dztZKG9HgtIpbI35FhfuSNR/zmaMVdxNlntHj1sIS4s=
|
||||||
google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
|
google.golang.org/api v0.194.0/go.mod h1:AgvUFdojGANh3vI+P7EVnxj3AISHllxGCJSFmggmnd0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
@ -1042,12 +1025,10 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8=
|
||||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -1062,8 +1043,8 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200910201057-6591123024b3/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200910201057-6591123024b3/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
@ -1076,19 +1057,16 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
|
||||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
@ -1142,6 +1120,7 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||||
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
|
10
migrations/mysql/20240918132534_add_position_index.sql
Normal file
10
migrations/mysql/20240918132534_add_position_index.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-- +up
|
||||||
|
-- +begin
|
||||||
|
CREATE INDEX positions_traded_at ON positions (traded_at, profit);
|
||||||
|
-- +end
|
||||||
|
|
||||||
|
-- +down
|
||||||
|
|
||||||
|
-- +begin
|
||||||
|
DROP INDEX positions_traded_at ON positions;
|
||||||
|
-- +end
|
10
migrations/sqlite3/20240918132534_add_position_index.sql
Normal file
10
migrations/sqlite3/20240918132534_add_position_index.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-- +up
|
||||||
|
-- +begin
|
||||||
|
CREATE INDEX positions_traded_at ON positions (traded_at, profit);
|
||||||
|
-- +end
|
||||||
|
|
||||||
|
-- +down
|
||||||
|
|
||||||
|
-- +begin
|
||||||
|
DROP INDEX positions_traded_at;
|
||||||
|
-- +end
|
|
@ -55,7 +55,7 @@ var ErrEmptyOrderType = errors.New("order type can not be empty string")
|
||||||
type Exchange struct {
|
type Exchange struct {
|
||||||
sourceName types.ExchangeName
|
sourceName types.ExchangeName
|
||||||
publicExchange types.Exchange
|
publicExchange types.Exchange
|
||||||
srv *service.BacktestService
|
srv service.BackTestable
|
||||||
currentTime time.Time
|
currentTime time.Time
|
||||||
|
|
||||||
account *types.Account
|
account *types.Account
|
||||||
|
@ -78,7 +78,7 @@ type Exchange struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExchange(
|
func NewExchange(
|
||||||
sourceName types.ExchangeName, sourceExchange types.Exchange, srv *service.BacktestService, config *bbgo.Backtest,
|
sourceName types.ExchangeName, sourceExchange types.Exchange, srv service.BackTestable, config *bbgo.Backtest,
|
||||||
) (*Exchange, error) {
|
) (*Exchange, error) {
|
||||||
ex := sourceExchange
|
ex := sourceExchange
|
||||||
|
|
||||||
|
@ -366,6 +366,7 @@ func (e *Exchange) SubscribeMarketData(
|
||||||
loadedIntervals[sub.Options.Interval] = struct{}{}
|
loadedIntervals[sub.Options.Interval] = struct{}{}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// todo support stream back test with csv tick source
|
||||||
// Since Environment is not yet been injected at this point, no hard error
|
// Since Environment is not yet been injected at this point, no hard error
|
||||||
log.Errorf("stream channel %s is not supported in backtest", sub.Channel)
|
log.Errorf("stream channel %s is not supported in backtest", sub.Channel)
|
||||||
}
|
}
|
||||||
|
@ -394,6 +395,7 @@ func (e *Exchange) SubscribeMarketData(
|
||||||
log.Infof("querying klines from database with exchange: %v symbols: %v and intervals: %v for back-testing", e.Name(), symbols, intervals)
|
log.Infof("querying klines from database with exchange: %v symbols: %v and intervals: %v for back-testing", e.Name(), symbols, intervals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("querying klines from database with exchange: %v symbols: %v and intervals: %v for back-testing", e.Name(), symbols, intervals)
|
||||||
if len(symbols) == 0 {
|
if len(symbols) == 0 {
|
||||||
log.Warnf("empty symbols, will not query kline data from the database")
|
log.Warnf("empty symbols, will not query kline data from the database")
|
||||||
|
|
||||||
|
|
|
@ -359,6 +359,7 @@ func (b *ActiveOrderBook) Add(orders ...types.Order) {
|
||||||
}
|
}
|
||||||
|
|
||||||
b.add(order)
|
b.add(order)
|
||||||
|
b.EmitNew(order)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +467,12 @@ func (b *ActiveOrderBook) Lookup(f func(o types.Order) bool) *types.Order {
|
||||||
|
|
||||||
func (b *ActiveOrderBook) filterExistingOrders(orders []types.Order) (existingOrders types.OrderSlice) {
|
func (b *ActiveOrderBook) filterExistingOrders(orders []types.Order) (existingOrders types.OrderSlice) {
|
||||||
for _, o := range orders {
|
for _, o := range orders {
|
||||||
|
// skip market order
|
||||||
|
// this prevents if someone added a market order to the active order book
|
||||||
|
if o.Type == types.OrderTypeMarket {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if b.Exists(o) {
|
if b.Exists(o) {
|
||||||
existingOrders.Add(o)
|
existingOrders.Add(o)
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,8 @@ type Backtest struct {
|
||||||
Sessions []string `json:"sessions" yaml:"sessions"`
|
Sessions []string `json:"sessions" yaml:"sessions"`
|
||||||
|
|
||||||
// sync 1 second interval KLines
|
// sync 1 second interval KLines
|
||||||
SyncSecKLines bool `json:"syncSecKLines,omitempty" yaml:"syncSecKLines,omitempty"`
|
SyncSecKLines bool `json:"syncSecKLines,omitempty" yaml:"syncSecKLines,omitempty"`
|
||||||
|
CsvSource *CsvSourceConfig `json:"csvConfig,omitempty" yaml:"csvConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backtest) GetAccount(n string) BacktestAccount {
|
func (b *Backtest) GetAccount(n string) BacktestAccount {
|
||||||
|
@ -706,3 +707,8 @@ func reUnmarshal(conf interface{}, tpe interface{}) (interface{}, error) {
|
||||||
|
|
||||||
return val.Elem().Interface(), nil
|
return val.Elem().Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CsvSourceConfig struct {
|
||||||
|
Market types.MarketType `json:"market"`
|
||||||
|
Granularity types.MarketDataType `json:"granularity"`
|
||||||
|
}
|
||||||
|
|
|
@ -42,9 +42,9 @@ var defaultSyncBufferPeriod = 30 * time.Minute
|
||||||
// IsBackTesting is a global variable that indicates the current environment is back-test or not.
|
// IsBackTesting is a global variable that indicates the current environment is back-test or not.
|
||||||
var IsBackTesting = false
|
var IsBackTesting = false
|
||||||
|
|
||||||
var BackTestService *service.BacktestService
|
var BackTestService service.BackTestable
|
||||||
|
|
||||||
func SetBackTesting(s *service.BacktestService) {
|
func SetBackTesting(s service.BackTestable) {
|
||||||
BackTestService = s
|
BackTestService = s
|
||||||
IsBackTesting = s != nil
|
IsBackTesting = s != nil
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ type Environment struct {
|
||||||
TradeService *service.TradeService
|
TradeService *service.TradeService
|
||||||
ProfitService *service.ProfitService
|
ProfitService *service.ProfitService
|
||||||
PositionService *service.PositionService
|
PositionService *service.PositionService
|
||||||
BacktestService *service.BacktestService
|
BacktestService service.BackTestable
|
||||||
RewardService *service.RewardService
|
RewardService *service.RewardService
|
||||||
MarginService *service.MarginService
|
MarginService *service.MarginService
|
||||||
SyncService *service.SyncService
|
SyncService *service.SyncService
|
||||||
|
|
|
@ -5,6 +5,8 @@ import "github.com/c9s/bbgo/pkg/types"
|
||||||
const MaxNumOfKLines = 5_000
|
const MaxNumOfKLines = 5_000
|
||||||
const MaxNumOfKLinesTruncate = 100
|
const MaxNumOfKLinesTruncate = 100
|
||||||
|
|
||||||
|
const CapacityOfKLineWindowLimit = 5_000
|
||||||
|
|
||||||
// MarketDataStore receives and maintain the public market data of a single symbol
|
// MarketDataStore receives and maintain the public market data of a single symbol
|
||||||
//go:generate callbackgen -type MarketDataStore
|
//go:generate callbackgen -type MarketDataStore
|
||||||
type MarketDataStore struct {
|
type MarketDataStore struct {
|
||||||
|
@ -57,10 +59,20 @@ func (store *MarketDataStore) AddKLine(k types.KLine) {
|
||||||
}
|
}
|
||||||
window.Add(k)
|
window.Add(k)
|
||||||
|
|
||||||
if len(*window) > MaxNumOfKLines {
|
truncateKLineWindowIfNeeded(window)
|
||||||
*window = (*window)[MaxNumOfKLinesTruncate-1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
store.EmitKLineClosed(k)
|
store.EmitKLineClosed(k)
|
||||||
store.EmitKLineWindowUpdate(k.Interval, *window)
|
store.EmitKLineWindowUpdate(k.Interval, *window)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func truncateKLineWindowIfNeeded(window *types.KLineWindow) {
|
||||||
|
lenOfWindow := len(*window)
|
||||||
|
capOfWindow := cap(*window)
|
||||||
|
|
||||||
|
if lenOfWindow == capOfWindow && capOfWindow > CapacityOfKLineWindowLimit {
|
||||||
|
size := CapacityOfKLineWindowLimit / 2
|
||||||
|
start := lenOfWindow - size
|
||||||
|
copy(*window, (*window)[start:])
|
||||||
|
*window = (*window)[:size]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
45
pkg/bbgo/marketdatastore_test.go
Normal file
45
pkg/bbgo/marketdatastore_test.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package bbgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarketDataStore_AddKLineAndTruncateWindow(t *testing.T) {
|
||||||
|
store := NewMarketDataStore("BTCUSD")
|
||||||
|
|
||||||
|
interval := types.Interval1s
|
||||||
|
|
||||||
|
var maxCap int = 0
|
||||||
|
capFixed := false
|
||||||
|
|
||||||
|
var gid uint64 = 0
|
||||||
|
// insert 1.5 * CapacityOfKLineWindowLimit KLine into window
|
||||||
|
for ; gid < CapacityOfKLineWindowLimit+(CapacityOfKLineWindowLimit/2); gid++ {
|
||||||
|
store.AddKLine(types.KLine{
|
||||||
|
Interval: interval,
|
||||||
|
GID: gid,
|
||||||
|
})
|
||||||
|
|
||||||
|
// if the capacity is > CapacityOfKLineWindowLimit, the capacity should be fixed. We use this if expression to verify it then.
|
||||||
|
if !capFixed && cap(*store.KLineWindows[interval]) > CapacityOfKLineWindowLimit {
|
||||||
|
maxCap = cap(*store.KLineWindows[interval])
|
||||||
|
capFixed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window := store.KLineWindows[interval]
|
||||||
|
|
||||||
|
// make sure the capacity is fixed
|
||||||
|
assert.Equal(t, maxCap, cap(*window))
|
||||||
|
|
||||||
|
// after truncate, it will remain (CapacityOfKLineWindowLimit / 2) KLine in the window
|
||||||
|
// so the first GIC will be the maxCap - (CapacityOfKLineWindowLimit / 2)
|
||||||
|
truncatedGID := uint64(maxCap - (CapacityOfKLineWindowLimit / 2))
|
||||||
|
for _, kline := range *window {
|
||||||
|
assert.Equal(t, truncatedGID, kline.GID)
|
||||||
|
truncatedGID++
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,35 +9,94 @@ var (
|
||||||
Help: "bbgo exchange session connection status",
|
Help: "bbgo exchange session connection status",
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
"exchange", // exchange name
|
"session",
|
||||||
"channel", // channel: user or market
|
"exchange", // exchange name
|
||||||
"margin", // margin type: none, margin or isolated
|
"channel", // channel: user or market
|
||||||
"symbol", // margin symbol of the connection.
|
"margin_type", // margin type: none, margin or isolated
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
metricsLockedBalances = prometheus.NewGaugeVec(
|
metricsBalanceLockedMetrics = prometheus.NewGaugeVec(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Name: "bbgo_balances_locked",
|
Name: "bbgo_balances_locked",
|
||||||
Help: "bbgo exchange locked balances",
|
Help: "bbgo exchange locked balances",
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
"exchange", // exchange name
|
"session",
|
||||||
"margin", // margin of connection. 1 or 0
|
"exchange", // exchange name
|
||||||
"symbol", // margin symbol of the connection.
|
"margin_type", // margin of connection. 1 or 0
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
"currency",
|
"currency",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
metricsAvailableBalances = prometheus.NewGaugeVec(
|
metricsBalanceAvailableMetrics = prometheus.NewGaugeVec(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Name: "bbgo_balances_available",
|
Name: "bbgo_balances_available",
|
||||||
Help: "bbgo exchange available balances",
|
Help: "bbgo exchange available balances",
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
"exchange", // exchange name
|
"session",
|
||||||
"margin", // margin of connection. none, margin or isolated
|
"exchange", // exchange name
|
||||||
"symbol", // margin symbol of the connection.
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
|
"currency",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
metricsBalanceDebtMetrics = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_balances_debt",
|
||||||
|
Help: "bbgo exchange balance debt",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"session",
|
||||||
|
"exchange", // exchange name
|
||||||
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
|
"currency",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
metricsBalanceBorrowedMetrics = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_balances_borrowed",
|
||||||
|
Help: "bbgo exchange balance borrowed",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"session",
|
||||||
|
"exchange", // exchange name
|
||||||
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
|
"currency",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
metricsBalanceInterestMetrics = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_balances_interest",
|
||||||
|
Help: "bbgo exchange balance interest",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"session",
|
||||||
|
"exchange", // exchange name
|
||||||
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
|
"currency",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
metricsBalanceNetMetrics = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_balances_net",
|
||||||
|
Help: "bbgo exchange session total net balances",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"session",
|
||||||
|
"exchange", // exchange name
|
||||||
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
"currency",
|
"currency",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -48,9 +107,10 @@ var (
|
||||||
Help: "bbgo exchange session total balances",
|
Help: "bbgo exchange session total balances",
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
"exchange", // exchange name
|
"session",
|
||||||
"margin", // margin of connection. none, margin or isolated
|
"exchange", // exchange name
|
||||||
"symbol", // margin symbol of the connection.
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
|
"symbol", // margin symbol of the connection.
|
||||||
"currency",
|
"currency",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -61,11 +121,12 @@ var (
|
||||||
Help: "bbgo exchange session trades",
|
Help: "bbgo exchange session trades",
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
"exchange", // exchange name
|
"session",
|
||||||
"margin", // margin of connection. none, margin or isolated
|
"exchange", // exchange name
|
||||||
"symbol", // margin symbol of the connection.
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
"side", // side: buy or sell
|
"symbol", // margin symbol of the connection.
|
||||||
"liquidity", // maker or taker
|
"side", // side: buy or sell
|
||||||
|
"liquidity", // maker or taker
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -75,26 +136,28 @@ var (
|
||||||
Help: "bbgo trading volume",
|
Help: "bbgo trading volume",
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
"exchange", // exchange name
|
"session",
|
||||||
"margin", // margin of connection. none, margin or isolated
|
"exchange", // exchange name
|
||||||
"symbol", // margin symbol of the connection.
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
"side", // side: buy or sell
|
"symbol", // margin symbol of the connection.
|
||||||
"liquidity", // maker or taker
|
"side", // side: buy or sell
|
||||||
|
"liquidity", // maker or taker
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
metricsLastUpdateTimeBalance = prometheus.NewGaugeVec(
|
metricsLastUpdateTimeMetrics = prometheus.NewGaugeVec(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Name: "bbgo_last_update_time",
|
Name: "bbgo_last_update_time",
|
||||||
Help: "bbgo last update time of different channel",
|
Help: "bbgo last update time of different channel",
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
"exchange", // exchange name
|
"session",
|
||||||
"margin", // margin of connection. none, margin or isolated
|
"exchange", // exchange name
|
||||||
"channel", // channel: user, market
|
"margin_type", // margin of connection. none, margin or isolated
|
||||||
"data_type", // type: balance, ticker, kline, orderbook, trade, order
|
"channel", // channel: user, market
|
||||||
"symbol", // for market data, trade and order
|
"data_type", // type: balance, ticker, kline, orderbook, trade, order
|
||||||
"currency", // for balance
|
"symbol", // for market data, trade and order
|
||||||
|
"currency", // for balance
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -103,10 +166,14 @@ func init() {
|
||||||
prometheus.MustRegister(
|
prometheus.MustRegister(
|
||||||
metricsConnectionStatus,
|
metricsConnectionStatus,
|
||||||
metricsTotalBalances,
|
metricsTotalBalances,
|
||||||
metricsLockedBalances,
|
metricsBalanceNetMetrics,
|
||||||
metricsAvailableBalances,
|
metricsBalanceLockedMetrics,
|
||||||
|
metricsBalanceAvailableMetrics,
|
||||||
|
metricsBalanceDebtMetrics,
|
||||||
|
metricsBalanceBorrowedMetrics,
|
||||||
|
metricsBalanceInterestMetrics,
|
||||||
metricsTradesTotal,
|
metricsTradesTotal,
|
||||||
metricsTradingVolume,
|
metricsTradingVolume,
|
||||||
metricsLastUpdateTimeBalance,
|
metricsLastUpdateTimeMetrics,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,9 @@ var quantityReduceDelta = fixedpoint.NewFromFloat(0.005)
|
||||||
// This is for the maximum retries
|
// This is for the maximum retries
|
||||||
const submitOrderRetryLimit = 5
|
const submitOrderRetryLimit = 5
|
||||||
|
|
||||||
|
// BaseOrderExecutor provides the common accessors for order executor
|
||||||
type BaseOrderExecutor struct {
|
type BaseOrderExecutor struct {
|
||||||
|
exchange types.Exchange
|
||||||
session *ExchangeSession
|
session *ExchangeSession
|
||||||
activeMakerOrders *ActiveOrderBook
|
activeMakerOrders *ActiveOrderBook
|
||||||
orderStore *core.OrderStore
|
orderStore *core.OrderStore
|
||||||
|
@ -43,8 +45,8 @@ func (e *BaseOrderExecutor) ActiveMakerOrders() *ActiveOrderBook {
|
||||||
|
|
||||||
// GracefulCancel cancels all active maker orders if orders are not given, otherwise cancel all the given orders
|
// GracefulCancel cancels all active maker orders if orders are not given, otherwise cancel all the given orders
|
||||||
func (e *BaseOrderExecutor) GracefulCancel(ctx context.Context, orders ...types.Order) error {
|
func (e *BaseOrderExecutor) GracefulCancel(ctx context.Context, orders ...types.Order) error {
|
||||||
if err := e.activeMakerOrders.GracefulCancel(ctx, e.session.Exchange, orders...); err != nil {
|
if err := e.activeMakerOrders.GracefulCancel(ctx, e.exchange, orders...); err != nil {
|
||||||
return errors.Wrap(err, "graceful cancel error")
|
return errors.Wrap(err, "graceful cancel order error")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -84,6 +86,7 @@ func NewGeneralOrderExecutor(
|
||||||
executor := &GeneralOrderExecutor{
|
executor := &GeneralOrderExecutor{
|
||||||
BaseOrderExecutor: BaseOrderExecutor{
|
BaseOrderExecutor: BaseOrderExecutor{
|
||||||
session: session,
|
session: session,
|
||||||
|
exchange: session.Exchange,
|
||||||
activeMakerOrders: NewActiveOrderBook(symbol),
|
activeMakerOrders: NewActiveOrderBook(symbol),
|
||||||
orderStore: orderStore,
|
orderStore: orderStore,
|
||||||
},
|
},
|
||||||
|
@ -111,7 +114,7 @@ func (e *GeneralOrderExecutor) SetMaxRetries(maxRetries uint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *GeneralOrderExecutor) startMarginAssetUpdater(ctx context.Context) {
|
func (e *GeneralOrderExecutor) startMarginAssetUpdater(ctx context.Context) {
|
||||||
marginService, ok := e.session.Exchange.(types.MarginBorrowRepayService)
|
marginService, ok := e.exchange.(types.MarginBorrowRepayService)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warnf("session %s (%T) exchange does not support MarginBorrowRepayService", e.session.Name, e.session.Exchange)
|
log.Warnf("session %s (%T) exchange does not support MarginBorrowRepayService", e.session.Name, e.session.Exchange)
|
||||||
return
|
return
|
||||||
|
|
|
@ -22,6 +22,7 @@ func NewSimpleOrderExecutor(session *ExchangeSession) *SimpleOrderExecutor {
|
||||||
return &SimpleOrderExecutor{
|
return &SimpleOrderExecutor{
|
||||||
BaseOrderExecutor: BaseOrderExecutor{
|
BaseOrderExecutor: BaseOrderExecutor{
|
||||||
session: session,
|
session: session,
|
||||||
|
exchange: session.Exchange,
|
||||||
activeMakerOrders: NewActiveOrderBook(""),
|
activeMakerOrders: NewActiveOrderBook(""),
|
||||||
orderStore: core.NewOrderStore(""),
|
orderStore: core.NewOrderStore(""),
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,12 +12,16 @@ type Quota struct {
|
||||||
Locked fixedpoint.Value
|
Locked fixedpoint.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add adds the fund to the available quota
|
||||||
func (q *Quota) Add(fund fixedpoint.Value) {
|
func (q *Quota) Add(fund fixedpoint.Value) {
|
||||||
q.mu.Lock()
|
q.mu.Lock()
|
||||||
q.Available = q.Available.Add(fund)
|
q.Available = q.Available.Add(fund)
|
||||||
q.mu.Unlock()
|
q.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lock locks the fund from the available quota
|
||||||
|
// returns true if the fund is locked successfully
|
||||||
|
// returns false if the fund is not enough
|
||||||
func (q *Quota) Lock(fund fixedpoint.Value) bool {
|
func (q *Quota) Lock(fund fixedpoint.Value) bool {
|
||||||
if fund.Compare(q.Available) > 0 {
|
if fund.Compare(q.Available) > 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -31,12 +35,15 @@ func (q *Quota) Lock(fund fixedpoint.Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commit commits the locked fund
|
||||||
func (q *Quota) Commit() {
|
func (q *Quota) Commit() {
|
||||||
q.mu.Lock()
|
q.mu.Lock()
|
||||||
q.Locked = fixedpoint.Zero
|
q.Locked = fixedpoint.Zero
|
||||||
q.mu.Unlock()
|
q.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rollback rolls back the locked fund
|
||||||
|
// this will move the locked fund to the available quota
|
||||||
func (q *Quota) Rollback() {
|
func (q *Quota) Rollback() {
|
||||||
q.mu.Lock()
|
q.mu.Lock()
|
||||||
q.Available = q.Available.Add(q.Locked)
|
q.Available = q.Available.Add(q.Locked)
|
||||||
|
@ -44,12 +51,21 @@ func (q *Quota) Rollback() {
|
||||||
q.mu.Unlock()
|
q.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Quota) String() string {
|
||||||
|
q.mu.Lock()
|
||||||
|
defer q.mu.Unlock()
|
||||||
|
|
||||||
|
return q.Locked.String() + "/" + q.Available.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuotaTransaction is a transactional quota manager
|
||||||
type QuotaTransaction struct {
|
type QuotaTransaction struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
BaseAsset Quota
|
BaseAsset Quota
|
||||||
QuoteAsset Quota
|
QuoteAsset Quota
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commit commits the transaction
|
||||||
func (m *QuotaTransaction) Commit() bool {
|
func (m *QuotaTransaction) Commit() bool {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
m.BaseAsset.Commit()
|
m.BaseAsset.Commit()
|
||||||
|
@ -58,6 +74,7 @@ func (m *QuotaTransaction) Commit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rollback rolls back the transaction
|
||||||
func (m *QuotaTransaction) Rollback() bool {
|
func (m *QuotaTransaction) Rollback() bool {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
m.BaseAsset.Rollback()
|
m.BaseAsset.Rollback()
|
||||||
|
|
|
@ -54,11 +54,11 @@ type ExchangeSession struct {
|
||||||
PublicOnly bool `json:"publicOnly,omitempty" yaml:"publicOnly"`
|
PublicOnly bool `json:"publicOnly,omitempty" yaml:"publicOnly"`
|
||||||
|
|
||||||
// PrivateChannels is used for filtering the private user data channel, .e.g, orders, trades, balances.. etc
|
// PrivateChannels is used for filtering the private user data channel, .e.g, orders, trades, balances.. etc
|
||||||
// This option is exchange specific
|
// This option is exchange-specific, currently only MAX exchange reads this option
|
||||||
PrivateChannels []string `json:"privateChannels,omitempty" yaml:"privateChannels,omitempty"`
|
PrivateChannels []string `json:"privateChannels,omitempty" yaml:"privateChannels,omitempty"`
|
||||||
|
|
||||||
// PrivateChannelSymbols is used for filtering the private user data channel, .e.g, order symbol subscription.
|
// PrivateChannelSymbols is used for filtering the private user data channel, .e.g, order symbol subscription.
|
||||||
// This option is exchange specific
|
// This option is exchange-specific, currently only Bitget exchange reads this option
|
||||||
PrivateChannelSymbols []string `json:"privateChannelSymbols,omitempty" yaml:"privateChannelSymbols,omitempty"`
|
PrivateChannelSymbols []string `json:"privateChannelSymbols,omitempty" yaml:"privateChannelSymbols,omitempty"`
|
||||||
|
|
||||||
Margin bool `json:"margin,omitempty" yaml:"margin"`
|
Margin bool `json:"margin,omitempty" yaml:"margin"`
|
||||||
|
@ -405,6 +405,8 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
|
||||||
return fmt.Errorf("market %s is not defined", symbol)
|
return fmt.Errorf("market %s is not defined", symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session.logger.Infof("environment config: %+v", environ.environmentConfig)
|
||||||
|
|
||||||
disableMarketDataStore := environ.environmentConfig != nil && environ.environmentConfig.DisableMarketDataStore
|
disableMarketDataStore := environ.environmentConfig != nil && environ.environmentConfig.DisableMarketDataStore
|
||||||
disableSessionTradeBuffer := environ.environmentConfig != nil && environ.environmentConfig.DisableSessionTradeBuffer
|
disableSessionTradeBuffer := environ.environmentConfig != nil && environ.environmentConfig.DisableSessionTradeBuffer
|
||||||
maxSessionTradeBufferSize := 0
|
maxSessionTradeBufferSize := 0
|
||||||
|
@ -464,7 +466,7 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
|
||||||
for _, sub := range session.Subscriptions {
|
for _, sub := range session.Subscriptions {
|
||||||
switch sub.Channel {
|
switch sub.Channel {
|
||||||
case types.BookChannel:
|
case types.BookChannel:
|
||||||
book := types.NewStreamBook(sub.Symbol)
|
book := types.NewStreamBook(sub.Symbol, session.ExchangeName)
|
||||||
book.BindStream(session.MarketDataStream)
|
book.BindStream(session.MarketDataStream)
|
||||||
session.orderBooks[sub.Symbol] = book
|
session.orderBooks[sub.Symbol] = book
|
||||||
|
|
||||||
|
@ -886,91 +888,106 @@ func (session *ExchangeSession) InitExchange(name string, ex types.Exchange) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *ExchangeSession) MarginType() string {
|
func (session *ExchangeSession) MarginType() types.MarginType {
|
||||||
margin := "none"
|
|
||||||
if session.Margin {
|
if session.Margin {
|
||||||
margin = "margin"
|
|
||||||
if session.IsolatedMargin {
|
if session.IsolatedMargin {
|
||||||
margin = "isolated"
|
return types.MarginTypeIsolatedMargin
|
||||||
|
} else {
|
||||||
|
return types.MarginTypeCrossMargin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return margin
|
|
||||||
|
return types.MarginTypeSpot
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *ExchangeSession) metricsBalancesUpdater(balances types.BalanceMap) {
|
func (session *ExchangeSession) metricsBalancesUpdater(balances types.BalanceMap) {
|
||||||
for currency, balance := range balances {
|
for currency, balance := range balances {
|
||||||
labels := prometheus.Labels{
|
labels := prometheus.Labels{
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"symbol": session.IsolatedMarginSymbol,
|
"margin_type": string(session.MarginType()),
|
||||||
"currency": currency,
|
"symbol": session.IsolatedMarginSymbol,
|
||||||
|
"currency": currency,
|
||||||
}
|
}
|
||||||
|
|
||||||
metricsTotalBalances.With(labels).Set(balance.Total().Float64())
|
metricsTotalBalances.With(labels).Set(balance.Total().Float64())
|
||||||
metricsLockedBalances.With(labels).Set(balance.Locked.Float64())
|
metricsBalanceNetMetrics.With(labels).Set(balance.Net().Float64())
|
||||||
metricsAvailableBalances.With(labels).Set(balance.Available.Float64())
|
metricsBalanceAvailableMetrics.With(labels).Set(balance.Available.Float64())
|
||||||
metricsLastUpdateTimeBalance.With(prometheus.Labels{
|
metricsBalanceLockedMetrics.With(labels).Set(balance.Locked.Float64())
|
||||||
"exchange": session.ExchangeName.String(),
|
|
||||||
"margin": session.MarginType(),
|
// margin metrics
|
||||||
"channel": "user",
|
metricsBalanceDebtMetrics.With(labels).Set(balance.Debt().Float64())
|
||||||
"data_type": "balance",
|
metricsBalanceBorrowedMetrics.With(labels).Set(balance.Borrowed.Float64())
|
||||||
"symbol": "",
|
metricsBalanceInterestMetrics.With(labels).Set(balance.Interest.Float64())
|
||||||
"currency": currency,
|
|
||||||
|
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
|
||||||
|
"session": session.Name,
|
||||||
|
"exchange": session.ExchangeName.String(),
|
||||||
|
"margin_type": string(session.MarginType()),
|
||||||
|
"channel": "user",
|
||||||
|
"data_type": "balance",
|
||||||
|
"symbol": "",
|
||||||
|
"currency": currency,
|
||||||
}).SetToCurrentTime()
|
}).SetToCurrentTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *ExchangeSession) metricsOrderUpdater(order types.Order) {
|
func (session *ExchangeSession) metricsOrderUpdater(order types.Order) {
|
||||||
metricsLastUpdateTimeBalance.With(prometheus.Labels{
|
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"channel": "user",
|
"margin_type": string(session.MarginType()),
|
||||||
"data_type": "order",
|
"channel": "user",
|
||||||
"symbol": order.Symbol,
|
"data_type": "order",
|
||||||
"currency": "",
|
"symbol": order.Symbol,
|
||||||
|
"currency": "",
|
||||||
}).SetToCurrentTime()
|
}).SetToCurrentTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *ExchangeSession) metricsTradeUpdater(trade types.Trade) {
|
func (session *ExchangeSession) metricsTradeUpdater(trade types.Trade) {
|
||||||
labels := prometheus.Labels{
|
labels := prometheus.Labels{
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"side": trade.Side.String(),
|
"margin_type": string(session.MarginType()),
|
||||||
"symbol": trade.Symbol,
|
"side": trade.Side.String(),
|
||||||
"liquidity": trade.Liquidity(),
|
"symbol": trade.Symbol,
|
||||||
|
"liquidity": trade.Liquidity(),
|
||||||
}
|
}
|
||||||
metricsTradingVolume.With(labels).Add(trade.Quantity.Mul(trade.Price).Float64())
|
metricsTradingVolume.With(labels).Add(trade.Quantity.Mul(trade.Price).Float64())
|
||||||
metricsTradesTotal.With(labels).Inc()
|
metricsTradesTotal.With(labels).Inc()
|
||||||
metricsLastUpdateTimeBalance.With(prometheus.Labels{
|
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"channel": "user",
|
"margin_type": string(session.MarginType()),
|
||||||
"data_type": "trade",
|
"channel": "user",
|
||||||
"symbol": trade.Symbol,
|
"data_type": "trade",
|
||||||
"currency": "",
|
"symbol": trade.Symbol,
|
||||||
|
"currency": "",
|
||||||
}).SetToCurrentTime()
|
}).SetToCurrentTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *ExchangeSession) bindMarketDataStreamMetrics(stream types.Stream) {
|
func (session *ExchangeSession) bindMarketDataStreamMetrics(stream types.Stream) {
|
||||||
stream.OnBookUpdate(func(book types.SliceOrderBook) {
|
stream.OnBookUpdate(func(book types.SliceOrderBook) {
|
||||||
metricsLastUpdateTimeBalance.With(prometheus.Labels{
|
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"channel": "market",
|
"margin_type": string(session.MarginType()),
|
||||||
"data_type": "book",
|
"channel": "market",
|
||||||
"symbol": book.Symbol,
|
"data_type": "book",
|
||||||
"currency": "",
|
"symbol": book.Symbol,
|
||||||
|
"currency": "",
|
||||||
}).SetToCurrentTime()
|
}).SetToCurrentTime()
|
||||||
})
|
})
|
||||||
stream.OnKLineClosed(func(kline types.KLine) {
|
stream.OnKLineClosed(func(kline types.KLine) {
|
||||||
metricsLastUpdateTimeBalance.With(prometheus.Labels{
|
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"channel": "market",
|
"margin_type": string(session.MarginType()),
|
||||||
"data_type": "kline",
|
"channel": "market",
|
||||||
"symbol": kline.Symbol,
|
"data_type": "kline",
|
||||||
"currency": "",
|
"symbol": kline.Symbol,
|
||||||
|
"currency": "",
|
||||||
}).SetToCurrentTime()
|
}).SetToCurrentTime()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -982,18 +999,20 @@ func (session *ExchangeSession) bindUserDataStreamMetrics(stream types.Stream) {
|
||||||
stream.OnOrderUpdate(session.metricsOrderUpdater)
|
stream.OnOrderUpdate(session.metricsOrderUpdater)
|
||||||
stream.OnDisconnect(func() {
|
stream.OnDisconnect(func() {
|
||||||
metricsConnectionStatus.With(prometheus.Labels{
|
metricsConnectionStatus.With(prometheus.Labels{
|
||||||
"channel": "user",
|
"channel": "user",
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"symbol": session.IsolatedMarginSymbol,
|
"margin_type": string(session.MarginType()),
|
||||||
|
"symbol": session.IsolatedMarginSymbol,
|
||||||
}).Set(0.0)
|
}).Set(0.0)
|
||||||
})
|
})
|
||||||
stream.OnConnect(func() {
|
stream.OnConnect(func() {
|
||||||
metricsConnectionStatus.With(prometheus.Labels{
|
metricsConnectionStatus.With(prometheus.Labels{
|
||||||
"channel": "user",
|
"channel": "user",
|
||||||
"exchange": session.ExchangeName.String(),
|
"session": session.Name,
|
||||||
"margin": session.MarginType(),
|
"exchange": session.ExchangeName.String(),
|
||||||
"symbol": session.IsolatedMarginSymbol,
|
"margin_type": string(session.MarginType()),
|
||||||
|
"symbol": session.IsolatedMarginSymbol,
|
||||||
}).Set(1.0)
|
}).Set(1.0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Strategy method calls:
|
// Strategy method calls:
|
||||||
// -> Initialize() (optional method)
|
|
||||||
// -> Defaults() (optional method)
|
// -> Defaults() (optional method)
|
||||||
|
//
|
||||||
|
// setup default static values from constants
|
||||||
|
//
|
||||||
|
// -> Initialize() (optional method)
|
||||||
|
//
|
||||||
|
// initialize dynamic runtime objects
|
||||||
|
//
|
||||||
|
// -> Subscribe()
|
||||||
|
//
|
||||||
|
// register the subscriptions
|
||||||
|
//
|
||||||
// -> Validate() (optional method)
|
// -> Validate() (optional method)
|
||||||
// -> Run() (optional method)
|
// -> Run() (optional method)
|
||||||
// -> Shutdown(shutdownCtx context.Context, wg *sync.WaitGroup)
|
// -> Shutdown(shutdownCtx context.Context, wg *sync.WaitGroup)
|
||||||
|
@ -112,6 +122,12 @@ func (trader *Trader) DisableLogging() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (trader *Trader) Configure(userConfig *Config) error {
|
func (trader *Trader) Configure(userConfig *Config) error {
|
||||||
|
// config environment
|
||||||
|
if userConfig.Environment != nil && trader.environment != nil {
|
||||||
|
trader.environment.environmentConfig = userConfig.Environment
|
||||||
|
}
|
||||||
|
|
||||||
|
// config risk control
|
||||||
if userConfig.RiskControls != nil {
|
if userConfig.RiskControls != nil {
|
||||||
trader.SetRiskControls(userConfig.RiskControls)
|
trader.SetRiskControls(userConfig.RiskControls)
|
||||||
}
|
}
|
||||||
|
@ -171,12 +187,6 @@ func (trader *Trader) SetRiskControls(riskControls *RiskControls) {
|
||||||
func (trader *Trader) RunSingleExchangeStrategy(
|
func (trader *Trader) RunSingleExchangeStrategy(
|
||||||
ctx context.Context, strategy SingleExchangeStrategy, session *ExchangeSession, orderExecutor OrderExecutor,
|
ctx context.Context, strategy SingleExchangeStrategy, session *ExchangeSession, orderExecutor OrderExecutor,
|
||||||
) error {
|
) error {
|
||||||
if v, ok := strategy.(StrategyValidator); ok {
|
|
||||||
if err := v.Validate(); err != nil {
|
|
||||||
return fmt.Errorf("failed to validate the config: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if shutdown, ok := strategy.(StrategyShutdown); ok {
|
if shutdown, ok := strategy.(StrategyShutdown); ok {
|
||||||
trader.gracefulShutdown.OnShutdown(shutdown.Shutdown)
|
trader.gracefulShutdown.OnShutdown(shutdown.Shutdown)
|
||||||
}
|
}
|
||||||
|
@ -238,12 +248,6 @@ func (trader *Trader) injectFieldsAndSubscribe(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaulter, ok := strategy.(StrategyDefaulter); ok {
|
|
||||||
if err := defaulter.Defaults(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if subscriber, ok := strategy.(ExchangeSessionSubscriber); ok {
|
if subscriber, ok := strategy.(ExchangeSessionSubscriber); ok {
|
||||||
subscriber.Subscribe(session)
|
subscriber.Subscribe(session)
|
||||||
} else {
|
} else {
|
||||||
|
@ -304,12 +308,6 @@ func (trader *Trader) injectFieldsAndSubscribe(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if initializer, ok := strategy.(StrategyInitializer); ok {
|
|
||||||
if err := initializer.Initialize(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if subscriber, ok := strategy.(CrossExchangeSessionSubscriber); ok {
|
if subscriber, ok := strategy.(CrossExchangeSessionSubscriber); ok {
|
||||||
subscriber.CrossSubscribe(trader.environment.sessions)
|
subscriber.CrossSubscribe(trader.environment.sessions)
|
||||||
} else {
|
} else {
|
||||||
|
@ -356,8 +354,23 @@ func (trader *Trader) Run(ctx context.Context) error {
|
||||||
return trader.environment.Connect(ctx)
|
return trader.environment.Connect(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize initializes the strategies, this method is called before the Run method.
|
||||||
|
// It sets the default values and validates the strategy configurations.
|
||||||
|
// And calls the Initialize method if the strategy implements the Initialize method.
|
||||||
func (trader *Trader) Initialize(ctx context.Context) error {
|
func (trader *Trader) Initialize(ctx context.Context) error {
|
||||||
return trader.IterateStrategies(func(strategy StrategyID) error {
|
return trader.IterateStrategies(func(strategy StrategyID) error {
|
||||||
|
if defaulter, ok := strategy.(StrategyDefaulter); ok {
|
||||||
|
if err := defaulter.Defaults(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := strategy.(StrategyValidator); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("found invalid strategy config: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if initializer, ok := strategy.(StrategyInitializer); ok {
|
if initializer, ok := strategy.(StrategyInitializer); ok {
|
||||||
return initializer.Initialize()
|
return initializer.Initialize()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,6 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/cmd/cmdutil"
|
|
||||||
"github.com/c9s/bbgo/pkg/core"
|
|
||||||
"github.com/c9s/bbgo/pkg/data/tsv"
|
|
||||||
"github.com/c9s/bbgo/pkg/util"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -26,13 +20,18 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/accounting/pnl"
|
"github.com/c9s/bbgo/pkg/accounting/pnl"
|
||||||
"github.com/c9s/bbgo/pkg/backtest"
|
"github.com/c9s/bbgo/pkg/backtest"
|
||||||
"github.com/c9s/bbgo/pkg/bbgo"
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/cmd/cmdutil"
|
||||||
|
"github.com/c9s/bbgo/pkg/core"
|
||||||
|
"github.com/c9s/bbgo/pkg/data/tsv"
|
||||||
"github.com/c9s/bbgo/pkg/exchange"
|
"github.com/c9s/bbgo/pkg/exchange"
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
"github.com/c9s/bbgo/pkg/service"
|
"github.com/c9s/bbgo/pkg/service"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/c9s/bbgo/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
BacktestCmd.Flags().Bool("csv", false, "use csv data source for exchange (if supported)")
|
||||||
BacktestCmd.Flags().Bool("sync", false, "sync backtest data")
|
BacktestCmd.Flags().Bool("sync", false, "sync backtest data")
|
||||||
BacktestCmd.Flags().Bool("sync-only", false, "sync backtest data only, do not run backtest")
|
BacktestCmd.Flags().Bool("sync-only", false, "sync backtest data only, do not run backtest")
|
||||||
BacktestCmd.Flags().String("sync-from", "", "sync backtest data from the given time, which will override the time range in the backtest config")
|
BacktestCmd.Flags().String("sync-from", "", "sync backtest data from the given time, which will override the time range in the backtest config")
|
||||||
|
@ -77,6 +76,11 @@ var BacktestCmd = &cobra.Command{
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modeCsv, err := cmd.Flags().GetBool("csv")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
wantSync, err := cmd.Flags().GetBool("sync")
|
wantSync, err := cmd.Flags().GetBool("sync")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -156,15 +160,29 @@ var BacktestCmd = &cobra.Command{
|
||||||
log.Infof("starting backtest with startTime %s", startTime.Format(time.RFC3339))
|
log.Infof("starting backtest with startTime %s", startTime.Format(time.RFC3339))
|
||||||
|
|
||||||
environ := bbgo.NewEnvironment()
|
environ := bbgo.NewEnvironment()
|
||||||
if err := bbgo.BootstrapBacktestEnvironment(ctx, environ); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if environ.DatabaseService == nil {
|
if userConfig.Backtest.CsvSource == nil {
|
||||||
return errors.New("database service is not enabled, please check your environment variables DB_DRIVER and DB_DSN")
|
return fmt.Errorf("user config backtest section needs csvsource config")
|
||||||
}
|
}
|
||||||
|
backtestService := service.NewBacktestServiceCSV(
|
||||||
|
outputDirectory,
|
||||||
|
userConfig.Backtest.CsvSource.Market,
|
||||||
|
userConfig.Backtest.CsvSource.Granularity,
|
||||||
|
)
|
||||||
|
if modeCsv {
|
||||||
|
if err := bbgo.BootstrapEnvironmentLightweight(ctx, environ, userConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backtestService = service.NewBacktestService(environ.DatabaseService.DB)
|
||||||
|
if err := bbgo.BootstrapBacktestEnvironment(ctx, environ); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB}
|
if environ.DatabaseService == nil {
|
||||||
|
return errors.New("database service is not enabled, please check your environment variables DB_DRIVER and DB_DSN")
|
||||||
|
}
|
||||||
|
}
|
||||||
environ.BacktestService = backtestService
|
environ.BacktestService = backtestService
|
||||||
bbgo.SetBackTesting(backtestService)
|
bbgo.SetBackTesting(backtestService)
|
||||||
|
|
||||||
|
@ -692,7 +710,7 @@ func createSymbolReport(
|
||||||
}
|
}
|
||||||
|
|
||||||
func verify(
|
func verify(
|
||||||
userConfig *bbgo.Config, backtestService *service.BacktestService,
|
userConfig *bbgo.Config, backtestService service.BackTestable,
|
||||||
sourceExchanges map[types.ExchangeName]types.Exchange, startTime, endTime time.Time,
|
sourceExchanges map[types.ExchangeName]types.Exchange, startTime, endTime time.Time,
|
||||||
) error {
|
) error {
|
||||||
for _, sourceExchange := range sourceExchanges {
|
for _, sourceExchange := range sourceExchanges {
|
||||||
|
@ -735,7 +753,7 @@ func getExchangeIntervals(ex types.Exchange) types.IntervalMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sync(
|
func sync(
|
||||||
ctx context.Context, userConfig *bbgo.Config, backtestService *service.BacktestService,
|
ctx context.Context, userConfig *bbgo.Config, backtestService service.BackTestable,
|
||||||
sourceExchanges map[types.ExchangeName]types.Exchange, syncFrom, syncTo time.Time,
|
sourceExchanges map[types.ExchangeName]types.Exchange, syncFrom, syncTo time.Time,
|
||||||
) error {
|
) error {
|
||||||
for _, symbol := range userConfig.Backtest.Symbols {
|
for _, symbol := range userConfig.Backtest.Symbols {
|
||||||
|
@ -750,10 +768,8 @@ func sync(
|
||||||
var intervals = supportIntervals.Slice()
|
var intervals = supportIntervals.Slice()
|
||||||
intervals.Sort()
|
intervals.Sort()
|
||||||
|
|
||||||
for _, interval := range intervals {
|
if err := backtestService.Sync(ctx, sourceExchange, symbol, intervals, syncFrom, syncTo); err != nil {
|
||||||
if err := backtestService.Sync(ctx, sourceExchange, symbol, interval, syncFrom, syncTo); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
221
pkg/cmd/execute_order.go
Normal file
221
pkg/cmd/execute_order.go
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
_ "github.com/c9s/bbgo/pkg/twap"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/c9s/bbgo/pkg/util"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/twap/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
executeOrderCmd.Flags().String("session", "", "the exchange session name for sync")
|
||||||
|
executeOrderCmd.Flags().String("symbol", "", "the trading pair, like btcusdt")
|
||||||
|
executeOrderCmd.Flags().String("side", "", "the trading side: buy or sell")
|
||||||
|
executeOrderCmd.Flags().String("target-quantity", "", "target quantity")
|
||||||
|
executeOrderCmd.Flags().String("slice-quantity", "", "slice quantity")
|
||||||
|
executeOrderCmd.Flags().String("stop-price", "0", "stop price")
|
||||||
|
executeOrderCmd.Flags().String("order-update-rate-limit", "1s", "order update rate limit, syntax: 1+1/1m")
|
||||||
|
executeOrderCmd.Flags().Duration("update-interval", time.Second*10, "order update time")
|
||||||
|
executeOrderCmd.Flags().Duration("delay-interval", time.Second*3, "order delay time after filled")
|
||||||
|
executeOrderCmd.Flags().Duration("deadline", 0, "deadline duration of the order execution, e.g. 1h")
|
||||||
|
executeOrderCmd.Flags().Int("price-ticks", 0, "the number of price tick for the jump spread, default to 0")
|
||||||
|
RootCmd.AddCommand(executeOrderCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var executeOrderCmd = &cobra.Command{
|
||||||
|
Use: "execute-order --session SESSION --symbol SYMBOL --side SIDE --target-quantity TOTAL_QUANTITY --slice-quantity SLICE_QUANTITY",
|
||||||
|
Short: "execute buy/sell on the balance/position you have on specific symbol",
|
||||||
|
SilenceUsage: true,
|
||||||
|
PreRunE: cobraInitRequired([]string{
|
||||||
|
"symbol",
|
||||||
|
"side",
|
||||||
|
"target-quantity",
|
||||||
|
"slice-quantity",
|
||||||
|
}),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
sessionName, err := cmd.Flags().GetString("session")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol, err := cmd.Flags().GetString("symbol")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can not get the symbol from flags: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if symbol == "" {
|
||||||
|
return fmt.Errorf("symbol not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
sideS, err := cmd.Flags().GetString("side")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't get side: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
side, err := types.StrToSideType(sideS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetQuantityS, err := cmd.Flags().GetString("target-quantity")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(targetQuantityS) == 0 {
|
||||||
|
return errors.New("--target-quantity can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetQuantity, err := fixedpoint.NewFromString(targetQuantityS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceQuantityS, err := cmd.Flags().GetString("slice-quantity")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(sliceQuantityS) == 0 {
|
||||||
|
return errors.New("--slice-quantity can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceQuantity, err := fixedpoint.NewFromString(sliceQuantityS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
numOfPriceTicks, err := cmd.Flags().GetInt("price-ticks")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stopPriceS, err := cmd.Flags().GetString("stop-price")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stopPrice, err := fixedpoint.NewFromString(stopPriceS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
orderUpdateRateLimitStr, err := cmd.Flags().GetString("order-update-rate-limit")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInterval, err := cmd.Flags().GetDuration("update-interval")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
delayInterval, err := cmd.Flags().GetDuration("delay-interval")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
deadlineDuration, err := cmd.Flags().GetDuration("deadline")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var deadlineTime time.Time
|
||||||
|
if deadlineDuration > 0 {
|
||||||
|
deadlineTime = time.Now().Add(deadlineDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
environ := bbgo.NewEnvironment()
|
||||||
|
if err := environ.ConfigureExchangeSessions(userConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := environ.Init(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, ok := environ.Session(sessionName)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("session %s not found", sessionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
executionCtx, cancelExecution := context.WithCancel(ctx)
|
||||||
|
defer cancelExecution()
|
||||||
|
|
||||||
|
market, ok := session.Market(symbol)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("market %s not found", symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
executor := twap.NewFixedQuantityExecutor(session.Exchange, symbol, market, side, targetQuantity, sliceQuantity)
|
||||||
|
|
||||||
|
if updateInterval > 0 {
|
||||||
|
executor.SetUpdateInterval(updateInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(orderUpdateRateLimitStr) > 0 {
|
||||||
|
rateLimit, err := util.ParseRateLimitSyntax(orderUpdateRateLimitStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.SetOrderUpdateRateLimit(rateLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if delayInterval > 0 {
|
||||||
|
executor.SetDelayInterval(delayInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stopPrice.Sign() > 0 {
|
||||||
|
executor.SetStopPrice(stopPrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumOfTicks: numOfPriceTicks,
|
||||||
|
if !deadlineTime.IsZero() {
|
||||||
|
executor.SetDeadlineTime(deadlineTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if numOfPriceTicks > 0 {
|
||||||
|
executor.SetNumOfTicks(numOfPriceTicks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := executor.Start(executionCtx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var sigC = make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigC, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
defer signal.Stop(sigC)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
|
||||||
|
case sig := <-sigC:
|
||||||
|
logrus.Warnf("signal %v", sig)
|
||||||
|
logrus.Infof("shutting down order executor...")
|
||||||
|
shutdownCtx, cancelShutdown := context.WithDeadline(ctx, time.Now().Add(10*time.Second))
|
||||||
|
executor.Shutdown(shutdownCtx)
|
||||||
|
cancelShutdown()
|
||||||
|
|
||||||
|
case <-executor.Done():
|
||||||
|
logrus.Infof("the order execution is completed")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
|
@ -54,7 +54,7 @@ var orderbookCmd = &cobra.Command{
|
||||||
return fmt.Errorf("session %s not found", sessionName)
|
return fmt.Errorf("session %s not found", sessionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
orderBook := types.NewMutexOrderBook(symbol)
|
orderBook := types.NewMutexOrderBook(symbol, session.Exchange.Name())
|
||||||
|
|
||||||
s := session.Exchange.NewStream()
|
s := session.Exchange.NewStream()
|
||||||
s.SetPublicOnly()
|
s.SetPublicOnly()
|
||||||
|
|
|
@ -3,19 +3,14 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/bbgo"
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -145,155 +140,6 @@ var listOrdersCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var executeOrderCmd = &cobra.Command{
|
|
||||||
Use: "execute-order --session SESSION --symbol SYMBOL --side SIDE --target-quantity TOTAL_QUANTITY --slice-quantity SLICE_QUANTITY",
|
|
||||||
Short: "execute buy/sell on the balance/position you have on specific symbol",
|
|
||||||
SilenceUsage: true,
|
|
||||||
PreRunE: cobraInitRequired([]string{
|
|
||||||
"symbol",
|
|
||||||
"side",
|
|
||||||
"target-quantity",
|
|
||||||
"slice-quantity",
|
|
||||||
}),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
sessionName, err := cmd.Flags().GetString("session")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol, err := cmd.Flags().GetString("symbol")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can not get the symbol from flags: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if symbol == "" {
|
|
||||||
return fmt.Errorf("symbol not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
sideS, err := cmd.Flags().GetString("side")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't get side: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
side, err := types.StrToSideType(sideS)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
targetQuantityS, err := cmd.Flags().GetString("target-quantity")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(targetQuantityS) == 0 {
|
|
||||||
return errors.New("--target-quantity can not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
targetQuantity, err := fixedpoint.NewFromString(targetQuantityS)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sliceQuantityS, err := cmd.Flags().GetString("slice-quantity")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(sliceQuantityS) == 0 {
|
|
||||||
return errors.New("--slice-quantity can not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
sliceQuantity, err := fixedpoint.NewFromString(sliceQuantityS)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
numOfPriceTicks, err := cmd.Flags().GetInt("price-ticks")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stopPriceS, err := cmd.Flags().GetString("stop-price")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stopPrice, err := fixedpoint.NewFromString(stopPriceS)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
updateInterval, err := cmd.Flags().GetDuration("update-interval")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
deadlineDuration, err := cmd.Flags().GetDuration("deadline")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var deadlineTime time.Time
|
|
||||||
if deadlineDuration > 0 {
|
|
||||||
deadlineTime = time.Now().Add(deadlineDuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
environ := bbgo.NewEnvironment()
|
|
||||||
if err := environ.ConfigureExchangeSessions(userConfig); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := environ.Init(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
session, ok := environ.Session(sessionName)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("session %s not found", sessionName)
|
|
||||||
}
|
|
||||||
|
|
||||||
executionCtx, cancelExecution := context.WithCancel(ctx)
|
|
||||||
defer cancelExecution()
|
|
||||||
|
|
||||||
execution := &bbgo.TwapExecution{
|
|
||||||
Session: session,
|
|
||||||
Symbol: symbol,
|
|
||||||
Side: side,
|
|
||||||
TargetQuantity: targetQuantity,
|
|
||||||
SliceQuantity: sliceQuantity,
|
|
||||||
StopPrice: stopPrice,
|
|
||||||
NumOfTicks: numOfPriceTicks,
|
|
||||||
UpdateInterval: updateInterval,
|
|
||||||
DeadlineTime: deadlineTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := execution.Run(executionCtx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var sigC = make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigC, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
defer signal.Stop(sigC)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case sig := <-sigC:
|
|
||||||
log.Warnf("signal %v", sig)
|
|
||||||
log.Infof("shutting down order executor...")
|
|
||||||
shutdownCtx, cancelShutdown := context.WithDeadline(ctx, time.Now().Add(10*time.Second))
|
|
||||||
execution.Shutdown(shutdownCtx)
|
|
||||||
cancelShutdown()
|
|
||||||
|
|
||||||
case <-execution.Done():
|
|
||||||
log.Infof("the order execution is completed")
|
|
||||||
|
|
||||||
case <-ctx.Done():
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// go run ./cmd/bbgo submit-order --session=ftx --symbol=BTCUSDT --side=buy --price=18000 --quantity=0.001
|
// go run ./cmd/bbgo submit-order --session=ftx --symbol=BTCUSDT --side=buy --price=18000 --quantity=0.001
|
||||||
var submitOrderCmd = &cobra.Command{
|
var submitOrderCmd = &cobra.Command{
|
||||||
Use: "submit-order --session SESSION --symbol SYMBOL --side SIDE --quantity QUANTITY [--price PRICE]",
|
Use: "submit-order --session SESSION --symbol SYMBOL --side SIDE --quantity QUANTITY [--price PRICE]",
|
||||||
|
@ -413,18 +259,7 @@ func init() {
|
||||||
submitOrderCmd.Flags().Bool("market", false, "submit order as a market order")
|
submitOrderCmd.Flags().Bool("market", false, "submit order as a market order")
|
||||||
submitOrderCmd.Flags().String("margin-side-effect", "", "margin order side effect")
|
submitOrderCmd.Flags().String("margin-side-effect", "", "margin order side effect")
|
||||||
|
|
||||||
executeOrderCmd.Flags().String("session", "", "the exchange session name for sync")
|
|
||||||
executeOrderCmd.Flags().String("symbol", "", "the trading pair, like btcusdt")
|
|
||||||
executeOrderCmd.Flags().String("side", "", "the trading side: buy or sell")
|
|
||||||
executeOrderCmd.Flags().String("target-quantity", "", "target quantity")
|
|
||||||
executeOrderCmd.Flags().String("slice-quantity", "", "slice quantity")
|
|
||||||
executeOrderCmd.Flags().String("stop-price", "0", "stop price")
|
|
||||||
executeOrderCmd.Flags().Duration("update-interval", time.Second*10, "order update time")
|
|
||||||
executeOrderCmd.Flags().Duration("deadline", 0, "deadline of the order execution")
|
|
||||||
executeOrderCmd.Flags().Int("price-ticks", 0, "the number of price tick for the jump spread, default to 0")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(listOrdersCmd)
|
RootCmd.AddCommand(listOrdersCmd)
|
||||||
RootCmd.AddCommand(getOrderCmd)
|
RootCmd.AddCommand(getOrderCmd)
|
||||||
RootCmd.AddCommand(submitOrderCmd)
|
RootCmd.AddCommand(submitOrderCmd)
|
||||||
RootCmd.AddCommand(executeOrderCmd)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,8 @@ var PnLCmd = &cobra.Command{
|
||||||
|
|
||||||
// we need the backtest klines for the daily prices
|
// we need the backtest klines for the daily prices
|
||||||
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB}
|
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB}
|
||||||
if err := backtestService.Sync(ctx, exchange, symbol, types.Interval1d, since, until); err != nil {
|
intervals := []types.Interval{types.Interval1d}
|
||||||
|
if err := backtestService.Sync(ctx, exchange, symbol, intervals, since, until); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
161
pkg/datasource/csvsource/csv_kline_decoder.go
Normal file
161
pkg/datasource/csvsource/csv_kline_decoder.go
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetaTraderTimeFormat is the time format expected by the MetaTrader decoder when cols [0] and [1] are used.
|
||||||
|
const MetaTraderTimeFormat = "02/01/2006 15:04"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNotEnoughColumns is returned when the CSV price record does not have enough columns.
|
||||||
|
ErrNotEnoughColumns = errors.New("not enough columns")
|
||||||
|
|
||||||
|
// ErrInvalidTimeFormat is returned when the CSV price record does not have a valid time unix milli format.
|
||||||
|
ErrInvalidIDFormat = errors.New("cannot parse trade id string")
|
||||||
|
|
||||||
|
// ErrInvalidBoolFormat is returned when the CSV isBuyerMaker record does not have a valid bool representation.
|
||||||
|
ErrInvalidBoolFormat = errors.New("cannot parse bool to string")
|
||||||
|
|
||||||
|
// ErrInvalidTimeFormat is returned when the CSV price record does not have a valid time unix milli format.
|
||||||
|
ErrInvalidTimeFormat = errors.New("cannot parse time string")
|
||||||
|
|
||||||
|
// ErrInvalidOrderSideFormat is returned when the CSV side record does not have a valid buy or sell string.
|
||||||
|
ErrInvalidOrderSideFormat = errors.New("cannot parse order side string")
|
||||||
|
|
||||||
|
// ErrInvalidPriceFormat is returned when the CSV price record does not prices in expected format.
|
||||||
|
ErrInvalidPriceFormat = errors.New("OHLC prices must be valid number format")
|
||||||
|
|
||||||
|
// ErrInvalidVolumeFormat is returned when the CSV price record does not have a valid volume format.
|
||||||
|
ErrInvalidVolumeFormat = errors.New("volume must be valid number format")
|
||||||
|
)
|
||||||
|
|
||||||
|
// CSVKLineDecoder is an extension point for CSVKLineReader to support custom file formats.
|
||||||
|
type CSVKLineDecoder func(record []string, interval time.Duration) (types.KLine, error)
|
||||||
|
|
||||||
|
// NewBinanceCSVKLineReader creates a new CSVKLineReader for Binance CSV files.
|
||||||
|
func NewBinanceCSVKLineReader(csv *csv.Reader) *CSVKLineReader {
|
||||||
|
return &CSVKLineReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: BinanceCSVKLineDecoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinanceCSVKLineDecoder decodes a CSV record from Binance or Bybit into a KLine.
|
||||||
|
func BinanceCSVKLineDecoder(record []string, interval time.Duration) (types.KLine, error) {
|
||||||
|
var (
|
||||||
|
k, empty types.KLine
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(record) < 5 {
|
||||||
|
return k, ErrNotEnoughColumns
|
||||||
|
}
|
||||||
|
|
||||||
|
ts, err := strconv.ParseFloat(record[0], 64) // check for e numbers "1.70027E+12"
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidTimeFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
open, err := fixedpoint.NewFromString(record[1])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
high, err := fixedpoint.NewFromString(record[2])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
low, err := fixedpoint.NewFromString(record[3])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
closing, err := fixedpoint.NewFromString(record[4])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
volume := fixedpoint.Zero
|
||||||
|
if len(record) == 6 {
|
||||||
|
volume, err = fixedpoint.NewFromString(record[5])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k.StartTime = types.Time(time.UnixMilli(int64(ts)))
|
||||||
|
k.EndTime = types.Time(k.StartTime.Time().Add(interval))
|
||||||
|
k.Open = open
|
||||||
|
k.High = high
|
||||||
|
k.Low = low
|
||||||
|
k.Close = closing
|
||||||
|
k.Volume = volume
|
||||||
|
|
||||||
|
return k, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetaTraderCSVKLineReader creates a new CSVKLineReader for MetaTrader CSV files.
|
||||||
|
func NewMetaTraderCSVKLineReader(csv *csv.Reader) *CSVKLineReader {
|
||||||
|
csv.Comma = ';'
|
||||||
|
return &CSVKLineReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: MetaTraderCSVKLineDecoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetaTraderCSVKLineDecoder decodes a CSV record from MetaTrader into a KLine.
|
||||||
|
func MetaTraderCSVKLineDecoder(record []string, interval time.Duration) (types.KLine, error) {
|
||||||
|
var (
|
||||||
|
k, empty types.KLine
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(record) < 6 {
|
||||||
|
return k, ErrNotEnoughColumns
|
||||||
|
}
|
||||||
|
|
||||||
|
tStr := fmt.Sprintf("%s %s", record[0], record[1])
|
||||||
|
t, err := time.Parse(MetaTraderTimeFormat, tStr)
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidTimeFormat
|
||||||
|
}
|
||||||
|
open, err := fixedpoint.NewFromString(record[2])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
high, err := fixedpoint.NewFromString(record[3])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
low, err := fixedpoint.NewFromString(record[4])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
closing, err := fixedpoint.NewFromString(record[5])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
volume, err := fixedpoint.NewFromString(record[6])
|
||||||
|
if err != nil {
|
||||||
|
return empty, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
k.StartTime = types.NewTimeFromUnix(t.Unix(), 0)
|
||||||
|
k.EndTime = types.NewTimeFromUnix(t.Add(interval).Unix(), 0)
|
||||||
|
k.Open = open
|
||||||
|
k.High = high
|
||||||
|
k.Low = low
|
||||||
|
k.Close = closing
|
||||||
|
k.Volume = volume
|
||||||
|
|
||||||
|
return k, nil
|
||||||
|
}
|
65
pkg/datasource/csvsource/csv_kline_reader.go
Normal file
65
pkg/datasource/csvsource/csv_kline_reader.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ KLineReader = (*CSVKLineReader)(nil)
|
||||||
|
|
||||||
|
// CSVKLineReader is a KLineReader that reads from a CSV file.
|
||||||
|
type CSVKLineReader struct {
|
||||||
|
csv *csv.Reader
|
||||||
|
decoder CSVKLineDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCSVKLineReader is a factory method type that creates a new CSVKLineReader.
|
||||||
|
type MakeCSVKLineReader func(csv *csv.Reader) *CSVKLineReader
|
||||||
|
|
||||||
|
// NewCSVKLineReader creates a new CSVKLineReader with the default Binance decoder.
|
||||||
|
func NewCSVKLineReader(csv *csv.Reader) *CSVKLineReader {
|
||||||
|
return &CSVKLineReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: BinanceCSVKLineDecoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCSVKLineReaderWithDecoder creates a new CSVKLineReader with the given decoder.
|
||||||
|
func NewCSVKLineReaderWithDecoder(csv *csv.Reader, decoder CSVKLineDecoder) *CSVKLineReader {
|
||||||
|
return &CSVKLineReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: decoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads the next KLine from the underlying CSV data.
|
||||||
|
func (r *CSVKLineReader) Read(interval time.Duration) (types.KLine, error) {
|
||||||
|
var k types.KLine
|
||||||
|
|
||||||
|
rec, err := r.csv.Read()
|
||||||
|
if err != nil {
|
||||||
|
return k, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.decoder(rec, interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAll reads all the KLines from the underlying CSV data.
|
||||||
|
func (r *CSVKLineReader) ReadAll(interval time.Duration) ([]types.KLine, error) {
|
||||||
|
var ks []types.KLine
|
||||||
|
for {
|
||||||
|
k, err := r.Read(interval)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ks = append(ks, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ks, nil
|
||||||
|
}
|
163
pkg/datasource/csvsource/csv_kline_reader_test.go
Normal file
163
pkg/datasource/csvsource/csv_kline_reader_test.go
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
. "github.com/c9s/bbgo/pkg/testing/testhelper"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func assertKLineEq(t *testing.T, exp, act types.KLine, name string) {
|
||||||
|
assert.True(t, exp.StartTime.Equal(act.StartTime.Time()), name)
|
||||||
|
assert.Equal(t, 0, exp.Open.Compare(act.Open), name)
|
||||||
|
assert.Equal(t, 0, exp.High.Compare(act.High), name)
|
||||||
|
assert.Equal(t, 0, exp.Low.Compare(act.Low), name)
|
||||||
|
assert.Equal(t, 0, exp.Close.Compare(act.Close), name)
|
||||||
|
assert.Equal(t, 0, exp.Volume.Compare(act.Volume), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCSVKLineReader_ReadWithBinanceDecoder(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
give string
|
||||||
|
want types.KLine
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Read DOHLCV",
|
||||||
|
give: "1609459200000,28923.63000000,29031.34000000,28690.17000000,28995.13000000,2311.81144500",
|
||||||
|
want: types.KLine{
|
||||||
|
StartTime: types.NewTimeFromUnix(1609459200, 0),
|
||||||
|
Open: Number(28923.63),
|
||||||
|
High: Number(29031.34),
|
||||||
|
Low: Number(28690.17),
|
||||||
|
Close: Number(28995.13),
|
||||||
|
// todo this should never happen >>
|
||||||
|
// mustNewFromString and NewFromFloat have different values after parse
|
||||||
|
Volume: fixedpoint.MustNewFromString("2311.81144500")},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Read DOHLC",
|
||||||
|
give: "1609459200000,28923.63000000,29031.34000000,28690.17000000,28995.13000000",
|
||||||
|
want: types.KLine{
|
||||||
|
StartTime: types.NewTimeFromUnix(1609459200, 0),
|
||||||
|
Open: Number(28923.63),
|
||||||
|
High: Number(29031.34),
|
||||||
|
Low: Number(28690.17),
|
||||||
|
Close: Number(28995.13),
|
||||||
|
Volume: Number(0)},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Not enough columns",
|
||||||
|
give: "1609459200000,28923.63000000,29031.34000000",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrNotEnoughColumns,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid time format",
|
||||||
|
give: "23/12/2021,28923.63000000,29031.34000000,28690.17000000,28995.13000000",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrInvalidTimeFormat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid price format",
|
||||||
|
give: "1609459200000,sixty,29031.34000000,28690.17000000,28995.13000000",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrInvalidPriceFormat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid volume format",
|
||||||
|
give: "1609459200000,28923.63000000,29031.34000000,28690.17000000,28995.13000000,vol",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrInvalidVolumeFormat,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
reader := NewBinanceCSVKLineReader(csv.NewReader(strings.NewReader(tt.give)))
|
||||||
|
kline, err := reader.Read(time.Hour)
|
||||||
|
assert.Equal(t, tt.err, err)
|
||||||
|
if err == nil {
|
||||||
|
spew.Dump(tt.want)
|
||||||
|
spew.Dump(kline)
|
||||||
|
assertKLineEq(t, tt.want, kline, tt.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCSVKLineReader_ReadAllWithDefaultDecoder(t *testing.T) {
|
||||||
|
records := []string{
|
||||||
|
"1609459200000,28923.63000000,29031.34000000,28690.17000000,28995.13000000,2311.81144500",
|
||||||
|
"1609459300000,28928.63000000,30031.34000000,22690.17000000,28495.13000000,3000.00",
|
||||||
|
}
|
||||||
|
reader := NewCSVKLineReader(csv.NewReader(strings.NewReader(strings.Join(records, "\n"))))
|
||||||
|
klines, err := reader.ReadAll(time.Hour)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, klines, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCSVKLineReader_ReadWithMetaTraderDecoder(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
give string
|
||||||
|
want types.KLine
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Read DOHLCV",
|
||||||
|
give: "11/12/2008;16:00;779.527679;780.964756;777.527679;779.964756;5",
|
||||||
|
want: types.KLine{
|
||||||
|
StartTime: types.NewTimeFromUnix(time.Date(2008, 12, 11, 16, 0, 0, 0, time.UTC).Unix(), 0),
|
||||||
|
Open: Number(779.527679),
|
||||||
|
High: Number(780.964756),
|
||||||
|
Low: Number(777.527679),
|
||||||
|
Close: Number(779.964756),
|
||||||
|
Volume: Number(5)},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Not enough columns",
|
||||||
|
give: "1609459200000;28923.63000000;29031.34000000",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrNotEnoughColumns,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid time format",
|
||||||
|
give: "23/12/2021;t;28923.63000000;29031.34000000;28690.17000000;28995.13000000",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrInvalidTimeFormat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid price format",
|
||||||
|
give: "11/12/2008;00:00;sixty;29031.34000000;28690.17000000;28995.13000000",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrInvalidPriceFormat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid volume format",
|
||||||
|
give: "11/12/2008;00:00;779.527679;780.964756;777.527679;779.964756;vol",
|
||||||
|
want: types.KLine{},
|
||||||
|
err: ErrInvalidVolumeFormat,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
reader := NewMetaTraderCSVKLineReader(csv.NewReader(strings.NewReader(tt.give)))
|
||||||
|
kline, err := reader.Read(time.Hour)
|
||||||
|
assert.Equal(t, tt.err, err)
|
||||||
|
assertKLineEq(t, tt.want, kline, tt.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
167
pkg/datasource/csvsource/csv_tick_converter.go
Normal file
167
pkg/datasource/csvsource/csv_tick_converter.go
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ICSVTickConverter interface {
|
||||||
|
CsvTickToKLine(tick *CsvTick) (closesKLine bool)
|
||||||
|
GetTicks() []*CsvTick
|
||||||
|
LatestKLine(interval types.Interval) (k *types.KLine)
|
||||||
|
GetKLineResults() map[types.Interval][]types.KLine
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSVTickConverter takes a tick and internally converts it to a KLine slice
|
||||||
|
type CSVTickConverter struct {
|
||||||
|
ticks []*CsvTick
|
||||||
|
intervals []types.Interval
|
||||||
|
klines map[types.Interval][]types.KLine
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCSVTickConverter(intervals []types.Interval) ICSVTickConverter {
|
||||||
|
return &CSVTickConverter{
|
||||||
|
ticks: []*CsvTick{},
|
||||||
|
intervals: intervals,
|
||||||
|
klines: make(map[types.Interval][]types.KLine),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CSVTickConverter) GetTicks() []*CsvTick {
|
||||||
|
return c.ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CSVTickConverter) AddKLine(interval types.Interval, k types.KLine) {
|
||||||
|
c.klines[interval] = append(c.klines[interval], k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKLineResult returns the converted ticks as kLine of interval
|
||||||
|
func (c *CSVTickConverter) LatestKLine(interval types.Interval) (k *types.KLine) {
|
||||||
|
if _, ok := c.klines[interval]; !ok || len(c.klines[interval]) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &c.klines[interval][len(c.klines[interval])-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKLineResults returns the converted ticks as kLine of all constructed intervals
|
||||||
|
func (c *CSVTickConverter) GetKLineResults() map[types.Interval][]types.KLine {
|
||||||
|
if len(c.klines) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.klines
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert ticks to KLine with interval
|
||||||
|
func (c *CSVTickConverter) CsvTickToKLine(tick *CsvTick) (closesKLine bool) {
|
||||||
|
for _, interval := range c.intervals {
|
||||||
|
var (
|
||||||
|
currentCandle = types.KLine{}
|
||||||
|
high = fixedpoint.Zero
|
||||||
|
low = fixedpoint.Zero
|
||||||
|
)
|
||||||
|
isOpen, t := c.detCandleStart(tick.Timestamp.Time(), interval)
|
||||||
|
if isOpen {
|
||||||
|
latestKline := c.LatestKLine(interval)
|
||||||
|
if latestKline != nil {
|
||||||
|
latestKline.Closed = true // k is pointer
|
||||||
|
closesKLine = true
|
||||||
|
c.addMissingKLines(interval, t)
|
||||||
|
}
|
||||||
|
c.AddKLine(interval, types.KLine{
|
||||||
|
Exchange: tick.Exchange,
|
||||||
|
Symbol: tick.Symbol,
|
||||||
|
Interval: interval,
|
||||||
|
StartTime: types.NewTimeFromUnix(t.Unix(), 0),
|
||||||
|
EndTime: types.NewTimeFromUnix(t.Add(interval.Duration()).Unix(), 0),
|
||||||
|
Open: tick.Price,
|
||||||
|
High: tick.Price,
|
||||||
|
Low: tick.Price,
|
||||||
|
Close: tick.Price,
|
||||||
|
Volume: tick.HomeNotional,
|
||||||
|
QuoteVolume: tick.ForeignNotional,
|
||||||
|
Closed: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCandle = c.klines[interval][len(c.klines[interval])-1]
|
||||||
|
|
||||||
|
if tick.Price.Compare(currentCandle.High) > 0 {
|
||||||
|
high = tick.Price
|
||||||
|
} else {
|
||||||
|
high = currentCandle.High
|
||||||
|
}
|
||||||
|
|
||||||
|
if tick.Price.Compare(currentCandle.Low) < 0 {
|
||||||
|
low = tick.Price
|
||||||
|
} else {
|
||||||
|
low = currentCandle.Low
|
||||||
|
}
|
||||||
|
|
||||||
|
c.klines[interval][len(c.klines[interval])-1] = types.KLine{
|
||||||
|
StartTime: currentCandle.StartTime,
|
||||||
|
EndTime: currentCandle.EndTime,
|
||||||
|
Exchange: tick.Exchange,
|
||||||
|
Symbol: tick.Symbol,
|
||||||
|
Interval: interval,
|
||||||
|
Open: currentCandle.Open,
|
||||||
|
High: high,
|
||||||
|
Low: low,
|
||||||
|
Close: tick.Price,
|
||||||
|
Volume: currentCandle.Volume.Add(tick.HomeNotional),
|
||||||
|
QuoteVolume: currentCandle.QuoteVolume.Add(tick.ForeignNotional),
|
||||||
|
Closed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CSVTickConverter) detCandleStart(ts time.Time, interval types.Interval) (isOpen bool, t time.Time) {
|
||||||
|
if len(c.klines) == 0 {
|
||||||
|
return true, interval.Truncate(ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = c.LatestKLine(interval).EndTime.Time()
|
||||||
|
if ts.After(end) {
|
||||||
|
return true, end
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, t
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendMissingKLines appends an empty kline till startNext falls within a kline interval
|
||||||
|
func (c *CSVTickConverter) addMissingKLines(
|
||||||
|
interval types.Interval,
|
||||||
|
startNext time.Time,
|
||||||
|
) {
|
||||||
|
for {
|
||||||
|
last := c.LatestKLine(interval)
|
||||||
|
newEndTime := types.NewTimeFromUnix(
|
||||||
|
// one second is the smallest interval
|
||||||
|
last.EndTime.Time().Add(time.Duration(last.Interval.Seconds())*time.Second).Unix(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if last.EndTime.Time().Before(startNext) {
|
||||||
|
c.AddKLine(interval, types.KLine{
|
||||||
|
StartTime: last.EndTime,
|
||||||
|
EndTime: newEndTime,
|
||||||
|
Exchange: last.Exchange,
|
||||||
|
Symbol: last.Symbol,
|
||||||
|
Interval: last.Interval,
|
||||||
|
Open: last.Close,
|
||||||
|
High: last.Close,
|
||||||
|
Low: last.Close,
|
||||||
|
Close: last.Close,
|
||||||
|
Volume: 0,
|
||||||
|
QuoteVolume: 0,
|
||||||
|
Closed: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
193
pkg/datasource/csvsource/csv_tick_decoder.go
Normal file
193
pkg/datasource/csvsource/csv_tick_decoder.go
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CSVTickDecoder is an extension point for CSVTickReader to support custom file formats.
|
||||||
|
type CSVTickDecoder func(record []string, index int) (*CsvTick, error)
|
||||||
|
|
||||||
|
// NewBinanceCSVTickReader creates a new CSVTickReader for Binance CSV files.
|
||||||
|
func NewBinanceCSVTickReader(csv *csv.Reader) *CSVTickReader {
|
||||||
|
return &CSVTickReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: BinanceCSVTickDecoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinanceCSVKLineDecoder decodes a CSV record from Binance into a CsvTick.
|
||||||
|
func BinanceCSVTickDecoder(row []string, _ int) (*CsvTick, error) {
|
||||||
|
if len(row) < 5 {
|
||||||
|
return nil, ErrNotEnoughColumns
|
||||||
|
}
|
||||||
|
// example csv row for some reason some properties are duplicated in their csv
|
||||||
|
// id, price, qty, base_qty, base_qty, time, is_buyer_maker, is_buyer_maker,
|
||||||
|
// 11782578,6.00000000,1.00000000,14974844,14974844,1698623884463,True
|
||||||
|
id, err := strconv.ParseUint(row[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidIDFormat
|
||||||
|
}
|
||||||
|
price, err := fixedpoint.NewFromString(row[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
qty, err := fixedpoint.NewFromString(row[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
baseQty, err := fixedpoint.NewFromString(row[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
isBuyerMaker, err := strconv.ParseBool(row[6])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// isBuyerMaker=false trade will qualify as BUY.
|
||||||
|
side := types.SideTypeBuy
|
||||||
|
if isBuyerMaker {
|
||||||
|
side = types.SideTypeSell
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseFloat(row[5], 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidTimeFormat
|
||||||
|
}
|
||||||
|
ts := time.Unix(int64(n), 0)
|
||||||
|
return &CsvTick{
|
||||||
|
TradeID: id,
|
||||||
|
Exchange: types.ExchangeBinance,
|
||||||
|
Side: side,
|
||||||
|
Size: qty,
|
||||||
|
Price: price,
|
||||||
|
IsBuyerMaker: isBuyerMaker,
|
||||||
|
HomeNotional: price.Mul(qty),
|
||||||
|
ForeignNotional: price.Mul(baseQty),
|
||||||
|
Timestamp: types.NewMillisecondTimestampFromInt(ts.UnixMilli()),
|
||||||
|
// Symbol: must be overwritten - info not in csv,
|
||||||
|
// TickDirection: would need to keep last tick in memory to compare tick direction,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBinanceCSVTickReader creates a new CSVTickReader for Bybit CSV files.
|
||||||
|
func NewBybitCSVTickReader(csv *csv.Reader) *CSVTickReader {
|
||||||
|
return &CSVTickReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: BybitCSVTickDecoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BybitCSVTickDecoder decodes a CSV record from Bybit into a CsvTick.
|
||||||
|
func BybitCSVTickDecoder(row []string, index int) (*CsvTick, error) {
|
||||||
|
// example csv row
|
||||||
|
// timestamp,symbol,side,size,price,tickDirection,trdMatchID,grossValue,homeNotional,foreignNotional
|
||||||
|
// 1649054912,FXSUSDT,Buy,0.01,38.32,PlusTick,9c30abaf-80ae-5ebf-9850-58fe7ed4bac8,3.832e+07,0.01,0.3832
|
||||||
|
if len(row) < 9 {
|
||||||
|
return nil, ErrNotEnoughColumns
|
||||||
|
}
|
||||||
|
if index == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
side, err := types.StrToSideType(row[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidOrderSideFormat
|
||||||
|
}
|
||||||
|
size, err := fixedpoint.NewFromString(row[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
price, err := fixedpoint.NewFromString(row[4])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
hn, err := fixedpoint.NewFromString(row[8])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
fn, err := fixedpoint.NewFromString(row[9])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseFloat(row[0], 64) // startTime eg 1696982287.4922
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidTimeFormat
|
||||||
|
}
|
||||||
|
ts := time.Unix(int64(n), 0)
|
||||||
|
return &CsvTick{
|
||||||
|
TradeID: uint64(index),
|
||||||
|
Symbol: row[1],
|
||||||
|
Exchange: types.ExchangeBybit,
|
||||||
|
Side: side,
|
||||||
|
Size: size,
|
||||||
|
Price: price,
|
||||||
|
HomeNotional: hn,
|
||||||
|
ForeignNotional: fn,
|
||||||
|
TickDirection: row[5], // todo does this seem promising to define for other exchanges too?
|
||||||
|
Timestamp: types.NewMillisecondTimestampFromInt(ts.UnixMilli()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOKExCSVTickReader creates a new CSVTickReader for OKEx CSV files.
|
||||||
|
func NewOKExCSVTickReader(csv *csv.Reader) *CSVTickReader {
|
||||||
|
return &CSVTickReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: OKExCSVTickDecoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OKExCSVKLineDecoder decodes a CSV record from OKEx into a CsvTick.
|
||||||
|
func OKExCSVTickDecoder(row []string, index int) (*CsvTick, error) {
|
||||||
|
if len(row) < 5 {
|
||||||
|
return nil, ErrNotEnoughColumns
|
||||||
|
}
|
||||||
|
if index == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// example csv row for OKeX
|
||||||
|
// trade_id, side, size, price, created_time
|
||||||
|
// 134642, sell, 6.2638 6.507 1.69975E+12
|
||||||
|
id, err := strconv.ParseInt(row[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidIDFormat
|
||||||
|
}
|
||||||
|
price, err := fixedpoint.NewFromString(row[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidPriceFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
qty, err := fixedpoint.NewFromString(row[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidVolumeFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
side := types.SideTypeBuy
|
||||||
|
isBuyerMaker := false
|
||||||
|
if row[1] == "sell" {
|
||||||
|
side = types.SideTypeSell
|
||||||
|
isBuyerMaker = true
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := strconv.ParseFloat(row[4], 64) // startTime
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidTimeFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := time.UnixMilli(int64(n))
|
||||||
|
return &CsvTick{
|
||||||
|
TradeID: uint64(id),
|
||||||
|
Exchange: types.ExchangeOKEx,
|
||||||
|
Side: side,
|
||||||
|
Size: qty,
|
||||||
|
Price: price,
|
||||||
|
IsBuyerMaker: isBuyerMaker,
|
||||||
|
HomeNotional: price.Mul(qty),
|
||||||
|
Timestamp: types.NewMillisecondTimestampFromInt(ts.UnixMilli()),
|
||||||
|
// ForeignNotional: // info not in csv
|
||||||
|
// Symbol: must be overwritten - info not in csv
|
||||||
|
// TickDirection: would need to keep last tick in memory to compare tick direction,
|
||||||
|
}, nil
|
||||||
|
}
|
66
pkg/datasource/csvsource/csv_tick_reader.go
Normal file
66
pkg/datasource/csvsource/csv_tick_reader.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ TickReader = (*CSVTickReader)(nil)
|
||||||
|
|
||||||
|
// CSVTickReader is a CSVTickReader that reads from a CSV file.
|
||||||
|
type CSVTickReader struct {
|
||||||
|
csv *csv.Reader
|
||||||
|
decoder CSVTickDecoder
|
||||||
|
ticks []*CsvTick
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCSVTickReader is a factory method type that creates a new CSVTickReader.
|
||||||
|
type MakeCSVTickReader func(csv *csv.Reader) *CSVTickReader
|
||||||
|
|
||||||
|
// NewCSVKLineReader creates a new CSVKLineReader with the default Binance decoder.
|
||||||
|
func NewCSVTickReader(csv *csv.Reader) *CSVTickReader {
|
||||||
|
return &CSVTickReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: BinanceCSVTickDecoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCSVTickReaderWithDecoder creates a new CSVKLineReader with the given decoder.
|
||||||
|
func NewCSVTickReaderWithDecoder(csv *csv.Reader, decoder CSVTickDecoder) *CSVTickReader {
|
||||||
|
return &CSVTickReader{
|
||||||
|
csv: csv,
|
||||||
|
decoder: decoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAll reads all the KLines from the underlying CSV data.
|
||||||
|
func (r *CSVTickReader) ReadAll() (ticks []*CsvTick, err error) {
|
||||||
|
var i int
|
||||||
|
for {
|
||||||
|
tick, err := r.Read(i)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i++ // used as jump logic inside decoder to skip csv headers in case
|
||||||
|
if tick == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ticks = append(ticks, tick)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ticks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads the next KLine from the underlying CSV data.
|
||||||
|
func (r *CSVTickReader) Read(i int) (*CsvTick, error) {
|
||||||
|
rec, err := r.csv.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.decoder(rec, i)
|
||||||
|
}
|
75
pkg/datasource/csvsource/csv_tick_reader_test.go
Normal file
75
pkg/datasource/csvsource/csv_tick_reader_test.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
. "github.com/c9s/bbgo/pkg/testing/testhelper"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCSVTickReader_ReadWithBinanceDecoder(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
give string
|
||||||
|
want *CsvTick
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Read Tick",
|
||||||
|
give: "11782578,6.00000000,1.00000000,14974844,14974844,1698623884463,True,True",
|
||||||
|
want: &CsvTick{
|
||||||
|
Timestamp: types.NewMillisecondTimestampFromInt(1698623884463),
|
||||||
|
Size: Number(1),
|
||||||
|
Price: Number(6),
|
||||||
|
HomeNotional: Number(6),
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Not enough columns",
|
||||||
|
give: "1609459200000,28923.63000000,29031.34000000",
|
||||||
|
want: nil,
|
||||||
|
err: ErrNotEnoughColumns,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid time format",
|
||||||
|
give: "11782578,6.00000000,1.00000000,14974844,14974844,23/12/2021,True,True",
|
||||||
|
want: nil,
|
||||||
|
err: ErrInvalidTimeFormat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid price format",
|
||||||
|
give: "11782578,sixty,1.00000000,14974844,14974844,1698623884463,True,True",
|
||||||
|
want: nil,
|
||||||
|
err: ErrInvalidPriceFormat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid size format",
|
||||||
|
give: "11782578,1.00000000,one,14974844,14974844,1698623884463,True,True",
|
||||||
|
want: nil,
|
||||||
|
err: ErrInvalidVolumeFormat,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
reader := NewBinanceCSVTickReader(csv.NewReader(strings.NewReader(tt.give)))
|
||||||
|
tick, err := reader.Read(0)
|
||||||
|
if err == nil {
|
||||||
|
assertTickEqual(t, tt.want, tick)
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.err, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertTickEqual(t *testing.T, exp, act *CsvTick) {
|
||||||
|
assert.Equal(t, exp.Timestamp.Time(), act.Timestamp.Time())
|
||||||
|
assert.Equal(t, 0, exp.Price.Compare(act.Price))
|
||||||
|
assert.Equal(t, 0, exp.Size.Compare(act.Size))
|
||||||
|
assert.Equal(t, 0, exp.HomeNotional.Compare(act.HomeNotional))
|
||||||
|
}
|
59
pkg/datasource/csvsource/read_klines.go
Normal file
59
pkg/datasource/csvsource/read_klines.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KLineReader is an interface for reading candlesticks.
|
||||||
|
type KLineReader interface {
|
||||||
|
Read(interval time.Duration) (types.KLine, error)
|
||||||
|
ReadAll(interval time.Duration) ([]types.KLine, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadKLinesFromCSV reads all the .csv files in a given directory or a single file into a slice of KLines.
|
||||||
|
// Wraps a default CSVKLineReader with Binance decoder for convenience.
|
||||||
|
// For finer grained memory management use the base kline reader.
|
||||||
|
func ReadKLinesFromCSV(path string, interval time.Duration) ([]types.KLine, error) {
|
||||||
|
return ReadKLinesFromCSVWithDecoder(path, interval, MakeCSVKLineReader(NewBinanceCSVKLineReader))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadKLinesFromCSVWithDecoder permits using a custom CSVKLineReader.
|
||||||
|
func ReadKLinesFromCSVWithDecoder(path string, interval time.Duration, maker MakeCSVKLineReader) ([]types.KLine, error) {
|
||||||
|
var klines []types.KLine
|
||||||
|
|
||||||
|
err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if filepath.Ext(path) != ".csv" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//nolint:errcheck // Read ops only so safe to ignore err return
|
||||||
|
defer file.Close()
|
||||||
|
reader := maker(csv.NewReader(file))
|
||||||
|
newKlines, err := reader.ReadAll(interval)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
klines = append(klines, newKlines...)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return klines, nil
|
||||||
|
}
|
21
pkg/datasource/csvsource/read_klines_test.go
Normal file
21
pkg/datasource/csvsource/read_klines_test.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadKLinesFromCSV(t *testing.T) {
|
||||||
|
klines, err := ReadKLinesFromCSV("./testdata/binance/BTCUSDT-1h-2023-11-18.csv", time.Hour)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, klines, 24)
|
||||||
|
assert.Equal(t, int64(1700265600), klines[0].StartTime.Unix(), "StartTime")
|
||||||
|
assert.Equal(t, int64(1700269200), klines[0].EndTime.Unix(), "EndTime")
|
||||||
|
assert.Equal(t, 36613.91, klines[0].Open.Float64(), "Open")
|
||||||
|
assert.Equal(t, 36613.92, klines[0].High.Float64(), "High")
|
||||||
|
assert.Equal(t, 36388.12, klines[0].Low.Float64(), "Low")
|
||||||
|
assert.Equal(t, 36400.01, klines[0].Close.Float64(), "Close")
|
||||||
|
assert.Equal(t, 1005.75727, klines[0].Volume.Float64(), "Volume")
|
||||||
|
}
|
89
pkg/datasource/csvsource/read_ticks.go
Normal file
89
pkg/datasource/csvsource/read_ticks.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TickReader is an interface for reading candlesticks.
|
||||||
|
type TickReader interface {
|
||||||
|
Read(i int) (*CsvTick, error)
|
||||||
|
ReadAll() (ticks []*CsvTick, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadTicksFromCSV reads all the .csv files in a given directory or a single file into a slice of Ticks.
|
||||||
|
// Wraps a default CSVTickReader with Binance decoder for convenience.
|
||||||
|
// For finer grained memory management use the base kline reader.
|
||||||
|
func ReadTicksFromCSV(
|
||||||
|
path, symbol string,
|
||||||
|
intervals []types.Interval,
|
||||||
|
) (
|
||||||
|
klineMap map[types.Interval][]types.KLine,
|
||||||
|
err error,
|
||||||
|
) {
|
||||||
|
return ReadTicksFromCSVWithDecoder(
|
||||||
|
path,
|
||||||
|
symbol,
|
||||||
|
intervals,
|
||||||
|
MakeCSVTickReader(NewBinanceCSVTickReader),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadTicksFromCSVWithDecoder permits using a custom CSVTickReader.
|
||||||
|
func ReadTicksFromCSVWithDecoder(
|
||||||
|
path, symbol string,
|
||||||
|
intervals []types.Interval,
|
||||||
|
maker MakeCSVTickReader,
|
||||||
|
) (
|
||||||
|
klineMap map[types.Interval][]types.KLine,
|
||||||
|
err error,
|
||||||
|
) {
|
||||||
|
converter := NewCSVTickConverter(intervals)
|
||||||
|
ticks := []*CsvTick{}
|
||||||
|
// read all ticks into memory
|
||||||
|
err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if filepath.Ext(path) != ".csv" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//nolint:errcheck // Read ops only so safe to ignore err return
|
||||||
|
defer file.Close()
|
||||||
|
reader := maker(csv.NewReader(file))
|
||||||
|
newTicks, err := reader.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ticks = append(ticks, newTicks...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// sort ticks by timestamp (okex sorts csv by price ascending ;(
|
||||||
|
sort.Slice(ticks, func(i, j int) bool {
|
||||||
|
return ticks[i].Timestamp.Time().Before(ticks[j].Timestamp.Time())
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, tick := range ticks {
|
||||||
|
tick.Symbol = symbol
|
||||||
|
converter.CsvTickToKLine(tick)
|
||||||
|
}
|
||||||
|
|
||||||
|
return converter.GetKLineResults(), nil
|
||||||
|
}
|
67
pkg/datasource/csvsource/read_ticks_test.go
Normal file
67
pkg/datasource/csvsource/read_ticks_test.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadTicksFromBinanceCSV(t *testing.T) {
|
||||||
|
path := "./testdata/binance/FXSUSDT-ticks-2023-10-29.csv"
|
||||||
|
symbol := "FXSUSDT"
|
||||||
|
intervals := []types.Interval{types.Interval1h}
|
||||||
|
klineMap, err := ReadTicksFromCSVWithDecoder(
|
||||||
|
path, symbol, intervals, MakeCSVTickReader(NewBinanceCSVTickReader),
|
||||||
|
)
|
||||||
|
klines := klineMap[types.Interval1h]
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, klines, 1)
|
||||||
|
assert.Equal(t, int64(1698620400), klines[0].StartTime.Unix(), "StartTime")
|
||||||
|
assert.Equal(t, int64(1698624000), klines[0].EndTime.Unix(), "EndTime")
|
||||||
|
assert.Equal(t, 6.0, klines[0].Open.Float64(), "Open")
|
||||||
|
assert.Equal(t, 6.0, klines[0].High.Float64(), "High")
|
||||||
|
assert.Equal(t, 6.0, klines[0].Low.Float64(), "Low")
|
||||||
|
assert.Equal(t, 6.0, klines[0].Close.Float64(), "Close")
|
||||||
|
assert.Equal(t, 111.0, klines[0].Volume.Float64(), "Volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadTicksFromBybitCSV(t *testing.T) {
|
||||||
|
path := "./testdata/bybit/FXSUSDT2023-10-10.csv"
|
||||||
|
symbol := "FXSUSDT"
|
||||||
|
intervals := []types.Interval{types.Interval1h}
|
||||||
|
klineMap, err := ReadTicksFromCSVWithDecoder(
|
||||||
|
path, symbol, intervals, MakeCSVTickReader(NewBybitCSVTickReader),
|
||||||
|
)
|
||||||
|
klines := klineMap[types.Interval1h]
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, klines, 1)
|
||||||
|
assert.Equal(t, int64(1696978800), klines[0].StartTime.Unix(), "StartTime")
|
||||||
|
assert.Equal(t, int64(1696982400), klines[0].EndTime.Unix(), "EndTime")
|
||||||
|
assert.Equal(t, 5.239, klines[0].Open.Float64(), "Open")
|
||||||
|
assert.Equal(t, 5.2495, klines[0].High.Float64(), "High")
|
||||||
|
assert.Equal(t, 5.239, klines[0].Low.Float64(), "Low")
|
||||||
|
assert.Equal(t, 5.2495, klines[0].Close.Float64(), "Close")
|
||||||
|
assert.Equal(t, 147.05, klines[0].Volume.Float64(), "Volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadTicksFromOkexCSV(t *testing.T) {
|
||||||
|
path := "./testdata/okex/BTC-USDT-aggtrades-2023-11-18.csv"
|
||||||
|
symbol := "BTCUSDT"
|
||||||
|
intervals := []types.Interval{types.Interval1h}
|
||||||
|
klineMap, err := ReadTicksFromCSVWithDecoder(
|
||||||
|
path, symbol, intervals, MakeCSVTickReader(NewOKExCSVTickReader),
|
||||||
|
)
|
||||||
|
klines := klineMap[types.Interval1h]
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, klines, 1)
|
||||||
|
assert.Equal(t, int64(1700236800), klines[0].StartTime.Unix(), "StartTime")
|
||||||
|
assert.Equal(t, int64(1700240400), klines[0].EndTime.Unix(), "EndTime")
|
||||||
|
assert.Equal(t, 35910.6, klines[0].Open.Float64(), "Open")
|
||||||
|
assert.Equal(t, 35914.4, klines[0].High.Float64(), "High")
|
||||||
|
assert.Equal(t, 35910.6, klines[0].Low.Float64(), "Low")
|
||||||
|
assert.Equal(t, 35914.4, klines[0].Close.Float64(), "Close")
|
||||||
|
assert.Equal(t, 51525.38700081, klines[0].Volume.Float64(), "Volume")
|
||||||
|
|
||||||
|
}
|
24
pkg/datasource/csvsource/testdata/binance/BTCUSDT-1h-2023-11-18.csv
vendored
Normal file
24
pkg/datasource/csvsource/testdata/binance/BTCUSDT-1h-2023-11-18.csv
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
1.70027E+12,36613.91,36613.92,36388.12,36400.01,1005.75727,1.70027E+12,36712312.49831390,35985,440.81212,16088534.76584510,0
|
||||||
|
1.70027E+12,36400.01,36456.53,36377.88,36405.46,507.51514,1.70027E+12,18486014.80771630,25492,236.98883,8631509.902,0
|
||||||
|
1.70027E+12,36405.47,36447.75,36390.44,36408.09,341.53256,1.70028E+12,12438100.14362490,19727,163.41072,5951076.256,0
|
||||||
|
1.70028E+12,36408.1,36424.01,36360.22,36371.81,444.73045,1.70028E+12,16180477.67130620,25089,207.88416,7562956.27,0
|
||||||
|
1.70028E+12,36371.8,36426.51,36369.45,36369.45,378.50007,1.70028E+12,13775839.75999540,20728,197.99667,7205858.497,0
|
||||||
|
1.70028E+12,36369.45,36378.65,36303.98,36334,629.09574,1.70029E+12,22862757.37912180,33883,269.3097,9787132.913,0
|
||||||
|
1.70029E+12,36334.01,36361.1,36250.01,36252,615.52755,1.70029E+12,22350527.58640450,30392,238.90543,8675479.987,0
|
||||||
|
1.70029E+12,36251.99,36428,36178.58,36417.16,1191.24433,1.70029E+12,43265058.27238300,41466,628.67499,22834722.23764540,0
|
||||||
|
1.70029E+12,36417.15,36479.22,36375.28,36448.01,600.66262,1.7003E+12,21883116.44525150,29227,301.84047,10996141.34025750,0
|
||||||
|
1.7003E+12,36448,36453.09,36392,36397.45,398.07607,1.7003E+12,14499345.43090060,22193,159.60456,5813290.376,0
|
||||||
|
1.7003E+12,36397.44,36486.48,36397.44,36472.46,601.46574,1.70031E+12,21917527.53081410,24881,354.42545,12916946.80705190,0
|
||||||
|
1.70031E+12,36472.46,36538.61,36400,36402.8,549.76216,1.70031E+12,20053594.27145890,29706,248.4342,9062453.31,0
|
||||||
|
1.70031E+12,36402.79,36484.31,36393.44,36449.3,513.24545,1.70031E+12,18705069.10380380,26631,244.2024,8898609.715,0
|
||||||
|
1.70031E+12,36449.3,36483.13,36347.69,36430.21,887.7206,1.70032E+12,32327899.78688460,41973,391.19851,14246544.95513180,0
|
||||||
|
1.70032E+12,36430.21,36568.76,36421.1,36507.03,803.12819,1.70032E+12,29307346.75876810,36941,447.83113,16341815.04367800,0
|
||||||
|
1.70032E+12,36507.04,36682.2,36505.14,36664.16,1440.91018,1.70032E+12,52738306.52534310,50174,755.06676,27635771.88129150,0
|
||||||
|
1.70032E+12,36664.17,36845.49,36639.96,36674,1669.58835,1.70033E+12,61326939.61543430,61313,823.2455,30239331.84409360,0
|
||||||
|
1.70033E+12,36674,36701.76,36600.1,36620,933.50168,1.70033E+12,34203500.13911480,39514,402.07999,14731130.96536590,0
|
||||||
|
1.70033E+12,36620.01,36745.5,36611.47,36707.19,583.0753,1.70033E+12,21373512.11709470,29144,289.6688,10617175.15516390,0
|
||||||
|
1.70033E+12,36707.18,36768,36653.51,36679.1,598.67548,1.70034E+12,21974467.38315690,31067,277.3739,10180364.08398050,0
|
||||||
|
1.70034E+12,36679.09,36707.16,36536.08,36598,779.88183,1.70034E+12,28546655.20047820,43054,314.94592,11526766.07883920,0
|
||||||
|
1.70034E+12,36597.99,36611.78,36524.31,36542.01,581.49791,1.70034E+12,21265094.72761260,36319,230.4279,8425965.352,0
|
||||||
|
1.70034E+12,36542.01,36556.56,36443.1,36506,543.12589,1.70035E+12,19821336.79152930,27472,245.46068,8956420.024,0
|
||||||
|
1.70035E+12,36506,36571.33,36498.34,36568.1,504.0213,1.70035E+12,18419269.57915190,22127,231.75877,8469107.669,0
|
|
5
pkg/datasource/csvsource/testdata/binance/FXSUSDT-ticks-2023-10-29.csv
vendored
Normal file
5
pkg/datasource/csvsource/testdata/binance/FXSUSDT-ticks-2023-10-29.csv
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
11782578,6.00000000,1.00000000,14974844,14974844,1698623884463,True,True
|
||||||
|
11782579,6.00000000,1.00000000,14974845,14974845,1698623884666,True,True
|
||||||
|
11782580,6.00000000,1.00000000,14974846,14974846,1698623893793,True,True
|
||||||
|
11782581,6.00000000,5.00000000,14974847,14974847,1698623920955,True,True
|
||||||
|
11782582,6.00000000,10.50000000,14974848,14974848,1698623939783,False,True
|
|
16
pkg/datasource/csvsource/testdata/bybit/FXSUSDT-ticks-2023-10-10.csv
vendored
Normal file
16
pkg/datasource/csvsource/testdata/bybit/FXSUSDT-ticks-2023-10-10.csv
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
timestamp,symbol,side,size,price,tickDirection,trdMatchID,grossValue,homeNotional,foreignNotional
|
||||||
|
1696982287.4922,FXSUSDT,Sell,0.86,5.2390,ZeroMinusTick,f7496ecb-b174-51b9-ba56-150186ba6c27,4.50554e+08,0.86,4.50554
|
||||||
|
1696982322.0561,FXSUSDT,Buy,0.13,5.2395,PlusTick,2089f1f4-d890-5762-a652-49a743fab436,6.81135e+07,0.13,0.6811349999999999
|
||||||
|
1696982333.0308,FXSUSDT,Buy,48.9,5.2420,PlusTick,8e7d405a-0003-5aa1-972d-46b08fe520c0,2.563338e+10,48.9,256.3338
|
||||||
|
1696982333.0377,FXSUSDT,Buy,0.77,5.2425,PlusTick,9f250e94-da5b-5a94-9126-084e46c9c692,4.0367249999999994e+08,0.77,4.036725
|
||||||
|
1696982359.7441,FXSUSDT,Buy,0.12,5.2450,PlusTick,08a0c666-da06-53f6-8eec-9d3462582b4f,6.293999999999999e+07,0.12,0.6294
|
||||||
|
1696982359.7441,FXSUSDT,Buy,0.19,5.2450,ZeroMinusTick,8a61753b-2a8e-5881-8e9b-9ad66806ee23,9.9655e+07,0.19,0.99655
|
||||||
|
1696982359.7443,FXSUSDT,Buy,12.12,5.2450,ZeroMinusTick,34b2f272-2f68-5d0a-a4ad-6c02f5342ca1,6.356939999999999e+09,12.12,63.569399999999995
|
||||||
|
1696982359.7443,FXSUSDT,Buy,2.19,5.2450,ZeroMinusTick,0cae9717-0fe1-51dd-bb10-1a61d5e83d98,1.148655e+09,2.19,11.48655
|
||||||
|
1696982359.7449,FXSUSDT,Buy,35.66,5.2450,ZeroMinusTick,0a5f0734-af3a-5439-9f17-d98ce7ea4f24,1.870367e+10,35.66,187.0367
|
||||||
|
1696982359.7512,FXSUSDT,Buy,10.97,5.2450,ZeroMinusTick,d8529e38-d3f5-5a7a-97f7-55cf3335de77,5.753765000000001e+09,10.97,57.537650000000006
|
||||||
|
1696982359.7512,FXSUSDT,Buy,22.97,5.2450,ZeroMinusTick,44361b86-78e1-533b-a3d0-7dd12d538992,1.2047765e+10,22.97,120.47765
|
||||||
|
1696982369.5962,FXSUSDT,Buy,0.05,5.2470,PlusTick,6800a047-b6e5-520a-9817-eb4d463a3cce,2.6235000000000004e+07,0.05,0.26235
|
||||||
|
1696982389.6288,FXSUSDT,Buy,0.02,5.2495,PlusTick,a4bc238f-3e6a-58a3-a012-1342563c2ced,1.0499000000000002e+07,0.02,0.10499000000000001
|
||||||
|
1696982389.6288,FXSUSDT,Buy,6.06,5.2495,ZeroMinusTick,eb27200e-c34e-537a-a0a0-4636dab66f07,3.181197e+09,6.06,31.81197
|
||||||
|
1696982389.6297,FXSUSDT,Buy,6.04,5.2495,ZeroMinusTick,c6badf81-05c5-5b35-b932-3c71941340fb,3.170698e+09,6.04,31.70698
|
|
16
pkg/datasource/csvsource/testdata/okex/BTC-USDT-aggtrades-2023-11-18.csv
vendored
Normal file
16
pkg/datasource/csvsource/testdata/okex/BTC-USDT-aggtrades-2023-11-18.csv
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
trade_id/<2F><><EFBFBD>id,side/<2F><><EFBFBD><EFBFBD><D7B7><EFBFBD>,size/<2F><><EFBFBD><EFBFBD>,price/<2F>۸<EFBFBD>,created_time/<2F>ɽ<EFBFBD>ʱ<EFBFBD><CAB1>
|
||||||
|
450372093,buy,0.00418025,35910.6,1700239042832
|
||||||
|
450372094,buy,0.0104,35911.9,1700239043163
|
||||||
|
450372860,buy,0.17316796,35911.9,1700239133047
|
||||||
|
450372095,buy,0.2227,35912.3,1700239043283
|
||||||
|
450372874,buy,0.63393,35913.4,1700239135563
|
||||||
|
450372876,buy,0.01751154,35913.6,1700239135563
|
||||||
|
450372096,buy,0.0478082,35913.7,1700239043339
|
||||||
|
450372877,buy,0.00030629,35913.7,1700239135563
|
||||||
|
450372878,buy,0.00030629,35913.8,1700239135563
|
||||||
|
450372880,buy,0.32111425,35913.9,1700239135563
|
||||||
|
450372881,buy,0.00027844,35914.0,1700239135563
|
||||||
|
450372882,buy,0.00058473,35914.2,1700239135563
|
||||||
|
450372032,buy,0.00132007,35914.3,1700239040621
|
||||||
|
450372883,buy,0.00058473,35914.3,1700239135563
|
||||||
|
450372884,buy,0.00052904,35914.4,1700239135563
|
|
244
pkg/datasource/csvsource/tick_downloader.go
Normal file
244
pkg/datasource/csvsource/tick_downloader.go
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/exchange/kucoin"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Download(
|
||||||
|
path, symbol string,
|
||||||
|
exchange types.ExchangeName,
|
||||||
|
market types.MarketType,
|
||||||
|
granularity types.MarketDataType,
|
||||||
|
since, until time.Time,
|
||||||
|
) (err error) {
|
||||||
|
for {
|
||||||
|
var (
|
||||||
|
fileName = fmt.Sprintf("%s-%s.csv", symbol, since.Format("2006-01-02"))
|
||||||
|
)
|
||||||
|
|
||||||
|
if fileExists(filepath.Join(path, fileName)) {
|
||||||
|
since = since.AddDate(0, 0, 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var url, err = buildURL(exchange, symbol, market, granularity, fileName, since)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("fetching ", url)
|
||||||
|
|
||||||
|
csvContent, err := readCSVFromUrl(exchange, url)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
err = write(csvContent, fmt.Sprintf("%s/%s", path, granularity), fileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
since = since.AddDate(0, 0, 1)
|
||||||
|
if since.After(until) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildURL(
|
||||||
|
exchange types.ExchangeName,
|
||||||
|
symbol string,
|
||||||
|
market types.MarketType,
|
||||||
|
granularity types.MarketDataType,
|
||||||
|
fileName string,
|
||||||
|
start time.Time,
|
||||||
|
) (url string, err error) {
|
||||||
|
switch exchange {
|
||||||
|
case types.ExchangeBybit:
|
||||||
|
// bybit doesn't seem to differentiate between spot and futures market or trade type in their csv dumps ;(
|
||||||
|
url = fmt.Sprintf("https://public.bybit.com/trading/%s/%s%s.csv.gz",
|
||||||
|
symbol,
|
||||||
|
symbol,
|
||||||
|
start.Format("2006-01-02"),
|
||||||
|
)
|
||||||
|
case types.ExchangeBinance:
|
||||||
|
marketType := "spot"
|
||||||
|
if market == types.MarketTypeFutures {
|
||||||
|
marketType = "futures/um"
|
||||||
|
}
|
||||||
|
dataType := "aggTrades"
|
||||||
|
if granularity == types.MarketDataTypeTrades {
|
||||||
|
dataType = "trades"
|
||||||
|
}
|
||||||
|
url = fmt.Sprintf("https://data.binance.vision/data/%s/daily/%s/%s/%s-%s-%s.zip",
|
||||||
|
marketType,
|
||||||
|
dataType,
|
||||||
|
symbol,
|
||||||
|
symbol,
|
||||||
|
dataType,
|
||||||
|
start.Format("2006-01-02"))
|
||||||
|
|
||||||
|
case types.ExchangeOKEx:
|
||||||
|
// todo temporary find a better solution ?!
|
||||||
|
coins := strings.Split(kucoin.ToLocalSymbol(symbol), "-")
|
||||||
|
if len(coins) == 0 {
|
||||||
|
err = fmt.Errorf("%s not supported yet for OKEx.. care to fix it? PR's welcome ;)", symbol)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
baseCoin := coins[0]
|
||||||
|
quoteCoin := coins[1]
|
||||||
|
marketType := "" // for spot market
|
||||||
|
if market == types.MarketTypeFutures {
|
||||||
|
marketType = "-SWAP"
|
||||||
|
}
|
||||||
|
dataType := "aggtrades"
|
||||||
|
if granularity == types.MarketDataTypeTrades {
|
||||||
|
dataType = "trades"
|
||||||
|
}
|
||||||
|
url = fmt.Sprintf("https://static.okx.com/cdn/okex/traderecords/%s/daily/%s/%s-%s%s-%s-%s.zip",
|
||||||
|
dataType,
|
||||||
|
start.Format("20060102"),
|
||||||
|
baseCoin,
|
||||||
|
quoteCoin,
|
||||||
|
marketType,
|
||||||
|
dataType,
|
||||||
|
start.Format("2006-01-02"))
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("%s not supported yet as csv data source.. care to fix it? PR's welcome ;)", exchange.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return url, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCSVFromUrl(exchange types.ExchangeName, url string) (csvContent []byte, err error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("http get error, url %s: %w", url, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch exchange {
|
||||||
|
case types.ExchangeBybit:
|
||||||
|
csvContent, err = gunzip(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("gunzip data %s: %w", exchange, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.ExchangeBinance:
|
||||||
|
csvContent, err = unzip(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unzip data %s: %w", exchange, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.ExchangeOKEx:
|
||||||
|
csvContent, err = unzip(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unzip data %s: %w", exchange, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return csvContent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(content []byte, path, fileName string) error {
|
||||||
|
|
||||||
|
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||||
|
err := os.MkdirAll(path, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("mkdir %s: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dest := filepath.Join(path, fileName)
|
||||||
|
|
||||||
|
err := os.WriteFile(dest, content, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write %s: %w", dest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unzip(data []byte) (resData []byte, err error) {
|
||||||
|
zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if zipReader == nil || len(zipReader.File) == 0 {
|
||||||
|
return nil, errors.New("no data to unzip")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all the files from zip archive
|
||||||
|
for _, zipFile := range zipReader.File {
|
||||||
|
resData, err = readZipFile(zipFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readZipFile(zf *zip.File) ([]byte, error) {
|
||||||
|
f, err := zf.Open()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return io.ReadAll(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gunzip(data []byte) (resData []byte, err error) {
|
||||||
|
b := bytes.NewBuffer(data)
|
||||||
|
|
||||||
|
var r io.Reader
|
||||||
|
r, err = gzip.NewReader(b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var resB bytes.Buffer
|
||||||
|
_, err = resB.ReadFrom(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resData = resB.Bytes()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(fileName string) bool {
|
||||||
|
info, err := os.Stat(fileName)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !info.IsDir()
|
||||||
|
}
|
103
pkg/datasource/csvsource/tick_downloader_test.go
Normal file
103
pkg/datasource/csvsource/tick_downloader_test.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DownloadTester struct {
|
||||||
|
Exchange types.ExchangeName
|
||||||
|
Reader MakeCSVTickReader
|
||||||
|
Market types.MarketType
|
||||||
|
Granularity types.MarketDataType
|
||||||
|
Symbol string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
expectedCandles = []int{1440, 48, 24}
|
||||||
|
intervals = []types.Interval{types.Interval1m, types.Interval30m, types.Interval1h}
|
||||||
|
until = time.Now().Round(0)
|
||||||
|
since = until.Add(-24 * time.Hour)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_CSV_Download(t *testing.T) {
|
||||||
|
if _, ok := os.LookupEnv("TEST_CSV_DOWNLOADER"); !ok {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
var tests = []DownloadTester{
|
||||||
|
{
|
||||||
|
Exchange: types.ExchangeBinance,
|
||||||
|
Reader: NewBinanceCSVTickReader,
|
||||||
|
Market: types.MarketTypeSpot,
|
||||||
|
Granularity: types.MarketDataTypeAggTrades,
|
||||||
|
Symbol: "FXSUSDT",
|
||||||
|
Path: "testdata/binance/FXSUSDT",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Exchange: types.ExchangeBybit,
|
||||||
|
Reader: NewBybitCSVTickReader,
|
||||||
|
Market: types.MarketTypeFutures,
|
||||||
|
Granularity: types.MarketDataTypeAggTrades,
|
||||||
|
Symbol: "FXSUSDT",
|
||||||
|
Path: "testdata/bybit/FXSUSDT",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Exchange: types.ExchangeOKEx,
|
||||||
|
Reader: NewOKExCSVTickReader,
|
||||||
|
Market: types.MarketTypeSpot,
|
||||||
|
Granularity: types.MarketDataTypeAggTrades,
|
||||||
|
Symbol: "BTCUSDT",
|
||||||
|
Path: "testdata/okex/BTCUSDT",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
err := Download(
|
||||||
|
tt.Path,
|
||||||
|
tt.Symbol,
|
||||||
|
tt.Exchange,
|
||||||
|
tt.Market,
|
||||||
|
tt.Granularity,
|
||||||
|
since,
|
||||||
|
until,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
klineMap, err := ReadTicksFromCSVWithDecoder(
|
||||||
|
tt.Path,
|
||||||
|
tt.Symbol,
|
||||||
|
intervals,
|
||||||
|
MakeCSVTickReader(tt.Reader),
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
for i, interval := range intervals {
|
||||||
|
klines := klineMap[interval]
|
||||||
|
|
||||||
|
assert.Equal(
|
||||||
|
t,
|
||||||
|
expectedCandles[i],
|
||||||
|
len(klines),
|
||||||
|
fmt.Sprintf("%s: %s/%s should have %d kLines",
|
||||||
|
tt.Exchange.String(),
|
||||||
|
tt.Symbol,
|
||||||
|
interval.String(),
|
||||||
|
expectedCandles[i],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
err = WriteKLines(tt.Path, tt.Symbol, klines)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(tt.Path)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
46
pkg/datasource/csvsource/types.go
Normal file
46
pkg/datasource/csvsource/types.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CsvTick struct {
|
||||||
|
Exchange types.ExchangeName `json:"exchange"`
|
||||||
|
Market types.MarketType `json:"market"`
|
||||||
|
TradeID uint64 `json:"tradeID"`
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
TickDirection string `json:"tickDirection"`
|
||||||
|
Side types.SideType `json:"side"`
|
||||||
|
IsBuyerMaker bool
|
||||||
|
Size fixedpoint.Value `json:"size"`
|
||||||
|
Price fixedpoint.Value `json:"price"`
|
||||||
|
HomeNotional fixedpoint.Value `json:"homeNotional"`
|
||||||
|
ForeignNotional fixedpoint.Value `json:"foreignNotional"`
|
||||||
|
Timestamp types.MillisecondTimestamp `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CsvTick) ToGlobalTrade() (*types.Trade, error) {
|
||||||
|
var isFutures bool
|
||||||
|
if c.Market == types.MarketTypeFutures {
|
||||||
|
isFutures = true
|
||||||
|
}
|
||||||
|
return &types.Trade{
|
||||||
|
ID: c.TradeID,
|
||||||
|
// OrderID: // not implemented
|
||||||
|
Exchange: c.Exchange,
|
||||||
|
Price: c.Price,
|
||||||
|
Quantity: c.Size,
|
||||||
|
QuoteQuantity: c.Price.Mul(c.Size), // todo this does not seem right use of propert.. looses info on foreign notional
|
||||||
|
Symbol: c.Symbol,
|
||||||
|
Side: c.Side,
|
||||||
|
IsBuyer: c.Side == types.SideTypeBuy,
|
||||||
|
IsMaker: c.IsBuyerMaker,
|
||||||
|
Time: types.Time(c.Timestamp),
|
||||||
|
// Fee: trade.ExecFee, // info is overwritten by stream?
|
||||||
|
// FeeCurrency: trade.FeeTokenId,
|
||||||
|
IsFutures: isFutures,
|
||||||
|
IsMargin: false,
|
||||||
|
IsIsolated: false,
|
||||||
|
}, nil
|
||||||
|
}
|
77
pkg/datasource/csvsource/write_klines.go
Normal file
77
pkg/datasource/csvsource/write_klines.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package csvsource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteKLines writes csv to path.
|
||||||
|
func WriteKLines(path, symbol string, klines []types.KLine) (err error) {
|
||||||
|
if len(klines) == 0 {
|
||||||
|
return fmt.Errorf("no klines to write")
|
||||||
|
}
|
||||||
|
from := klines[0].StartTime.Time()
|
||||||
|
end := klines[len(klines)-1].EndTime.Time()
|
||||||
|
to := ""
|
||||||
|
if from.AddDate(0, 0, 1).After(end) {
|
||||||
|
to = "-" + end.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
|
path = fmt.Sprintf("%s/klines/%s",
|
||||||
|
path,
|
||||||
|
klines[0].Interval.String(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fileName := fmt.Sprintf("%s/%s-%s%s.csv",
|
||||||
|
path,
|
||||||
|
symbol,
|
||||||
|
from.Format("2006-01-02"),
|
||||||
|
to,
|
||||||
|
)
|
||||||
|
|
||||||
|
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||||
|
err := os.MkdirAll(path, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("mkdir %s: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to open file")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = file.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to close file")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
w := csv.NewWriter(file)
|
||||||
|
defer w.Flush()
|
||||||
|
|
||||||
|
// Using Write
|
||||||
|
for _, kline := range klines {
|
||||||
|
row := []string{
|
||||||
|
fmt.Sprintf("%d", kline.StartTime.Unix()),
|
||||||
|
kline.Open.String(),
|
||||||
|
kline.High.String(),
|
||||||
|
kline.Low.String(),
|
||||||
|
kline.Close.String(),
|
||||||
|
kline.Volume.String(),
|
||||||
|
}
|
||||||
|
if err := w.Write(row); err != nil {
|
||||||
|
return errors.Wrap(err, "writing record to file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -2,9 +2,10 @@ package binance
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/c9s/bbgo/pkg/exchange/binance/binanceapi"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/exchange/binance/binanceapi"
|
||||||
|
|
||||||
"github.com/adshao/go-binance/v2/futures"
|
"github.com/adshao/go-binance/v2/futures"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
@ -42,11 +43,10 @@ func toGlobalFuturesPositions(futuresPositions []*binanceapi.FuturesAccountPosit
|
||||||
retFuturesPositions := make(types.FuturesPositionMap)
|
retFuturesPositions := make(types.FuturesPositionMap)
|
||||||
for _, futuresPosition := range futuresPositions {
|
for _, futuresPosition := range futuresPositions {
|
||||||
retFuturesPositions[futuresPosition.Symbol] = types.FuturesPosition{ // TODO: types.FuturesPosition
|
retFuturesPositions[futuresPosition.Symbol] = types.FuturesPosition{ // TODO: types.FuturesPosition
|
||||||
Isolated: futuresPosition.Isolated,
|
Isolated: futuresPosition.Isolated,
|
||||||
AverageCost: fixedpoint.MustNewFromString(futuresPosition.EntryPrice),
|
AverageCost: fixedpoint.MustNewFromString(futuresPosition.EntryPrice),
|
||||||
ApproximateAverageCost: fixedpoint.MustNewFromString(futuresPosition.EntryPrice),
|
Base: fixedpoint.MustNewFromString(futuresPosition.PositionAmt),
|
||||||
Base: fixedpoint.MustNewFromString(futuresPosition.PositionAmt),
|
Quote: fixedpoint.MustNewFromString(futuresPosition.Notional),
|
||||||
Quote: fixedpoint.MustNewFromString(futuresPosition.Notional),
|
|
||||||
|
|
||||||
PositionRisk: &types.PositionRisk{
|
PositionRisk: &types.PositionRisk{
|
||||||
Leverage: fixedpoint.MustNewFromString(futuresPosition.Leverage),
|
Leverage: fixedpoint.MustNewFromString(futuresPosition.Leverage),
|
||||||
|
|
|
@ -796,13 +796,24 @@ func (e *Exchange) QueryOrder(ctx context.Context, q types.OrderQuery) (*types.O
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var order *binance.Order
|
|
||||||
if e.IsMargin {
|
if e.IsMargin {
|
||||||
order, err = e.client.NewGetMarginOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
|
order, err := e.client.NewGetMarginOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
|
||||||
} else {
|
if err != nil {
|
||||||
order, err = e.client.NewGetOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
|
return nil, err
|
||||||
|
}
|
||||||
|
return toGlobalOrder(order, e.IsMargin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.IsFutures {
|
||||||
|
order, err := e.futuresClient.NewGetOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return toGlobalFuturesOrder(order, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
order, err := e.client.NewGetOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -994,7 +1005,6 @@ func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// could be IOC or FOK
|
|
||||||
if len(order.TimeInForce) > 0 {
|
if len(order.TimeInForce) > 0 {
|
||||||
// TODO: check the TimeInForce value
|
// TODO: check the TimeInForce value
|
||||||
req.TimeInForce(binance.TimeInForceType(order.TimeInForce))
|
req.TimeInForce(binance.TimeInForceType(order.TimeInForce))
|
||||||
|
|
|
@ -115,10 +115,10 @@ func convertSubscriptions(ss []types.Subscription) ([]WebSocketCommand, error) {
|
||||||
switch s.Channel {
|
switch s.Channel {
|
||||||
case types.BookChannel:
|
case types.BookChannel:
|
||||||
// see https://docs.kucoin.com/#level-2-market-data
|
// see https://docs.kucoin.com/#level-2-market-data
|
||||||
subscribeTopic = "/market/level2" + ":" + toLocalSymbol(s.Symbol)
|
subscribeTopic = "/market/level2" + ":" + ToLocalSymbol(s.Symbol)
|
||||||
|
|
||||||
case types.KLineChannel:
|
case types.KLineChannel:
|
||||||
subscribeTopic = "/market/candles" + ":" + toLocalSymbol(s.Symbol) + "_" + toLocalInterval(types.Interval(s.Options.Interval))
|
subscribeTopic = "/market/candles" + ":" + ToLocalSymbol(s.Symbol) + "_" + toLocalInterval(types.Interval(s.Options.Interval))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("websocket channel %s is not supported by kucoin", s.Channel)
|
return nil, fmt.Errorf("websocket channel %s is not supported by kucoin", s.Channel)
|
||||||
|
|
|
@ -165,7 +165,7 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type
|
||||||
}
|
}
|
||||||
|
|
||||||
req := e.client.MarketDataService.NewGetKLinesRequest()
|
req := e.client.MarketDataService.NewGetKLinesRequest()
|
||||||
req.Symbol(toLocalSymbol(symbol))
|
req.Symbol(ToLocalSymbol(symbol))
|
||||||
req.Interval(toLocalInterval(interval))
|
req.Interval(toLocalInterval(interval))
|
||||||
if options.StartTime != nil {
|
if options.StartTime != nil {
|
||||||
req.StartAt(*options.StartTime)
|
req.StartAt(*options.StartTime)
|
||||||
|
@ -208,7 +208,7 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type
|
||||||
|
|
||||||
func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (createdOrder *types.Order, err error) {
|
func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (createdOrder *types.Order, err error) {
|
||||||
req := e.client.TradeService.NewPlaceOrderRequest()
|
req := e.client.TradeService.NewPlaceOrderRequest()
|
||||||
req.Symbol(toLocalSymbol(order.Symbol))
|
req.Symbol(ToLocalSymbol(order.Symbol))
|
||||||
req.Side(toLocalSide(order.Side))
|
req.Side(toLocalSide(order.Side))
|
||||||
|
|
||||||
if order.ClientOrderID != "" {
|
if order.ClientOrderID != "" {
|
||||||
|
@ -298,7 +298,7 @@ You will not be able to query for cancelled orders that have happened more than
|
||||||
*/
|
*/
|
||||||
func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders []types.Order, err error) {
|
func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders []types.Order, err error) {
|
||||||
req := e.client.TradeService.NewListOrdersRequest()
|
req := e.client.TradeService.NewListOrdersRequest()
|
||||||
req.Symbol(toLocalSymbol(symbol))
|
req.Symbol(ToLocalSymbol(symbol))
|
||||||
req.Status("active")
|
req.Status("active")
|
||||||
orderList, err := req.Do(ctx)
|
orderList, err := req.Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -316,7 +316,7 @@ func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders [
|
||||||
|
|
||||||
func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) (orders []types.Order, err error) {
|
func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) (orders []types.Order, err error) {
|
||||||
req := e.client.TradeService.NewListOrdersRequest()
|
req := e.client.TradeService.NewListOrdersRequest()
|
||||||
req.Symbol(toLocalSymbol(symbol))
|
req.Symbol(ToLocalSymbol(symbol))
|
||||||
req.Status("done")
|
req.Status("done")
|
||||||
req.StartAt(since)
|
req.StartAt(since)
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ var launchDate = time.Date(2017, 9, 0, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) (trades []types.Trade, err error) {
|
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) (trades []types.Trade, err error) {
|
||||||
req := e.client.TradeService.NewGetFillsRequest()
|
req := e.client.TradeService.NewGetFillsRequest()
|
||||||
req.Symbol(toLocalSymbol(symbol))
|
req.Symbol(ToLocalSymbol(symbol))
|
||||||
|
|
||||||
// we always sync trades in the ascending order, and kucoin does not support last trade ID query
|
// we always sync trades in the ascending order, and kucoin does not support last trade ID query
|
||||||
// hence we need to set the start time here
|
// hence we need to set the start time here
|
||||||
|
@ -422,7 +422,7 @@ func (e *Exchange) NewStream() types.Stream {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Exchange) QueryDepth(ctx context.Context, symbol string) (types.SliceOrderBook, int64, error) {
|
func (e *Exchange) QueryDepth(ctx context.Context, symbol string) (types.SliceOrderBook, int64, error) {
|
||||||
orderBook, err := e.client.MarketDataService.GetOrderBook(toLocalSymbol(symbol), 100)
|
orderBook, err := e.client.MarketDataService.GetOrderBook(ToLocalSymbol(symbol), 100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.SliceOrderBook{}, 0, err
|
return types.SliceOrderBook{}, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ import (
|
||||||
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
|
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
|
||||||
package kucoin
|
package kucoin
|
||||||
|
|
||||||
var symbolMap = map[string]string{
|
var SymbolMap = map[string]string{
|
||||||
{{- range $k, $v := . }}
|
{{- range $k, $v := . }}
|
||||||
{{ printf "%q" $k }}: {{ printf "%q" $v }},
|
{{ printf "%q" $k }}: {{ printf "%q" $v }},
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLocalSymbol(symbol string) string {
|
func ToLocalSymbol(symbol string) string {
|
||||||
s, ok := symbolMap[symbol]
|
s, ok := symbolMap[symbol]
|
||||||
if ok {
|
if ok {
|
||||||
return s
|
return s
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
package kucoin
|
package kucoin
|
||||||
|
|
||||||
var symbolMap = map[string]string{
|
var SymbolMap = map[string]string{
|
||||||
"1EARTHUSDT": "1EARTH-USDT",
|
"1EARTHUSDT": "1EARTH-USDT",
|
||||||
"1INCHUSDT": "1INCH-USDT",
|
"1INCHUSDT": "1INCH-USDT",
|
||||||
"2CRZBTC": "2CRZ-BTC",
|
"2CRZBTC": "2CRZ-BTC",
|
||||||
|
@ -1107,8 +1107,8 @@ var symbolMap = map[string]string{
|
||||||
"ZRXETH": "ZRX-ETH",
|
"ZRXETH": "ZRX-ETH",
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLocalSymbol(symbol string) string {
|
func ToLocalSymbol(symbol string) string {
|
||||||
s, ok := symbolMap[symbol]
|
s, ok := SymbolMap[symbol]
|
||||||
if ok {
|
if ok {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,29 +247,6 @@ func toGlobalTradeV3(t v3.Trade) ([]types.Trade, error) {
|
||||||
return trades, nil
|
return trades, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toGlobalTradeV2(t max.Trade) (*types.Trade, error) {
|
|
||||||
isMargin := t.WalletType == max.WalletTypeMargin
|
|
||||||
side := toGlobalSideType(t.Side)
|
|
||||||
return &types.Trade{
|
|
||||||
ID: t.ID,
|
|
||||||
OrderID: t.OrderID,
|
|
||||||
Price: t.Price,
|
|
||||||
Symbol: toGlobalSymbol(t.Market),
|
|
||||||
Exchange: types.ExchangeMax,
|
|
||||||
Quantity: t.Volume,
|
|
||||||
Side: side,
|
|
||||||
IsBuyer: t.IsBuyer(),
|
|
||||||
IsMaker: t.IsMaker(),
|
|
||||||
Fee: t.Fee,
|
|
||||||
FeeCurrency: toGlobalCurrency(t.FeeCurrency),
|
|
||||||
QuoteQuantity: t.Funds,
|
|
||||||
Time: types.Time(t.CreatedAt),
|
|
||||||
IsMargin: isMargin,
|
|
||||||
IsIsolated: false,
|
|
||||||
IsFutures: false,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toGlobalDepositStatus(a max.DepositState) types.DepositStatus {
|
func toGlobalDepositStatus(a max.DepositState) types.DepositStatus {
|
||||||
switch a {
|
switch a {
|
||||||
|
|
||||||
|
@ -284,11 +261,21 @@ func toGlobalDepositStatus(a max.DepositState) types.DepositStatus {
|
||||||
|
|
||||||
case max.DepositStateAccepted:
|
case max.DepositStateAccepted:
|
||||||
return types.DepositSuccess
|
return types.DepositSuccess
|
||||||
|
|
||||||
|
case max.DepositStateFailed: // v3 state
|
||||||
|
return types.DepositRejected
|
||||||
|
|
||||||
|
case max.DepositStateProcessing: // v3 states
|
||||||
|
return types.DepositPending
|
||||||
|
|
||||||
|
case max.DepositStateDone: // v3 states
|
||||||
|
return types.DepositSuccess
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// other states goes to this
|
// other states goes to this
|
||||||
// max.DepositStateSuspect, max.DepositStateSuspended
|
// max.DepositStateSuspect, max.DepositStateSuspended
|
||||||
log.Warnf("unsupported deposit state %q from max exchange", a)
|
log.Errorf("unsupported deposit state %q from max exchange", a)
|
||||||
return types.DepositStatus(a)
|
return types.DepositStatus(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,11 @@ const (
|
||||||
DepositStateSuspended DepositState = "suspended"
|
DepositStateSuspended DepositState = "suspended"
|
||||||
DepositStateAccepted DepositState = "accepted"
|
DepositStateAccepted DepositState = "accepted"
|
||||||
DepositStateChecking DepositState = "checking"
|
DepositStateChecking DepositState = "checking"
|
||||||
|
|
||||||
|
// v3 states
|
||||||
|
DepositStateProcessing DepositState = "processing"
|
||||||
|
DepositStateFailed DepositState = "failed"
|
||||||
|
DepositStateDone DepositState = "done"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Deposit struct {
|
type Deposit struct {
|
||||||
|
|
|
@ -136,6 +136,12 @@ func (c *CancelOrderRequest) GetSlugsMap() (map[string]string, error) {
|
||||||
return slugs, nil
|
return slugs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPath returns the request path of the API
|
||||||
|
func (c *CancelOrderRequest) GetPath() string {
|
||||||
|
return "/api/v3/order"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do generates the request object and send the request object to the API endpoint
|
||||||
func (c *CancelOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
func (c *CancelOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
||||||
|
|
||||||
params, err := c.GetParameters()
|
params, err := c.GetParameters()
|
||||||
|
@ -144,7 +150,9 @@ func (c *CancelOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
apiURL := "/api/v3/order"
|
var apiURL string
|
||||||
|
|
||||||
|
apiURL = c.GetPath()
|
||||||
|
|
||||||
req, err := c.client.NewAuthenticatedRequest(ctx, "DELETE", apiURL, query, params)
|
req, err := c.client.NewAuthenticatedRequest(ctx, "DELETE", apiURL, query, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -157,8 +165,32 @@ func (c *CancelOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiResponse max.Order
|
var apiResponse max.Order
|
||||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
|
||||||
return nil, err
|
type responseUnmarshaler interface {
|
||||||
|
Unmarshal(data []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok {
|
||||||
|
if err := unmarshaler.Unmarshal(response.Body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The line below checks the content type, however, some API server might not send the correct content type header,
|
||||||
|
// Hence, this is commented for backward compatibility
|
||||||
|
// response.IsJSON()
|
||||||
|
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseValidator interface {
|
||||||
|
Validate() error
|
||||||
|
}
|
||||||
|
|
||||||
|
if validator, ok := interface{}(&apiResponse).(responseValidator); ok {
|
||||||
|
if err := validator.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &apiResponse, nil
|
return &apiResponse, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/c9s/bbgo/pkg/exchange/max/maxapi"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
max "github.com/c9s/bbgo/pkg/exchange/max/maxapi"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CancelWalletOrderAllRequest) Side(side string) *CancelWalletOrderAllRequest {
|
func (c *CancelWalletOrderAllRequest) Side(side string) *CancelWalletOrderAllRequest {
|
||||||
|
@ -166,6 +165,12 @@ func (c *CancelWalletOrderAllRequest) GetSlugsMap() (map[string]string, error) {
|
||||||
return slugs, nil
|
return slugs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPath returns the request path of the API
|
||||||
|
func (c *CancelWalletOrderAllRequest) GetPath() string {
|
||||||
|
return "/api/v3/wallet/:walletType/orders"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do generates the request object and send the request object to the API endpoint
|
||||||
func (c *CancelWalletOrderAllRequest) Do(ctx context.Context) ([]OrderCancelResponse, error) {
|
func (c *CancelWalletOrderAllRequest) Do(ctx context.Context) ([]OrderCancelResponse, error) {
|
||||||
|
|
||||||
params, err := c.GetParameters()
|
params, err := c.GetParameters()
|
||||||
|
@ -174,7 +179,9 @@ func (c *CancelWalletOrderAllRequest) Do(ctx context.Context) ([]OrderCancelResp
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
apiURL := "/api/v3/wallet/:walletType/orders"
|
var apiURL string
|
||||||
|
|
||||||
|
apiURL = c.GetPath()
|
||||||
slugs, err := c.GetSlugsMap()
|
slugs, err := c.GetSlugsMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -193,8 +200,32 @@ func (c *CancelWalletOrderAllRequest) Do(ctx context.Context) ([]OrderCancelResp
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiResponse []OrderCancelResponse
|
var apiResponse []OrderCancelResponse
|
||||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
|
||||||
return nil, err
|
type responseUnmarshaler interface {
|
||||||
|
Unmarshal(data []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok {
|
||||||
|
if err := unmarshaler.Unmarshal(response.Body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The line below checks the content type, however, some API server might not send the correct content type header,
|
||||||
|
// Hence, this is commented for backward compatibility
|
||||||
|
// response.IsJSON()
|
||||||
|
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseValidator interface {
|
||||||
|
Validate() error
|
||||||
|
}
|
||||||
|
|
||||||
|
if validator, ok := interface{}(&apiResponse).(responseValidator); ok {
|
||||||
|
if err := validator.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return apiResponse, nil
|
return apiResponse, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,12 @@ func (c *CreateWalletOrderRequest) GetSlugsMap() (map[string]string, error) {
|
||||||
return slugs, nil
|
return slugs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPath returns the request path of the API
|
||||||
|
func (c *CreateWalletOrderRequest) GetPath() string {
|
||||||
|
return "/api/v3/wallet/:walletType/order"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do generates the request object and send the request object to the API endpoint
|
||||||
func (c *CreateWalletOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
func (c *CreateWalletOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
||||||
|
|
||||||
params, err := c.GetParameters()
|
params, err := c.GetParameters()
|
||||||
|
@ -244,7 +250,9 @@ func (c *CreateWalletOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
apiURL := "/api/v3/wallet/:walletType/order"
|
var apiURL string
|
||||||
|
|
||||||
|
apiURL = c.GetPath()
|
||||||
slugs, err := c.GetSlugsMap()
|
slugs, err := c.GetSlugsMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -263,8 +271,32 @@ func (c *CreateWalletOrderRequest) Do(ctx context.Context) (*max.Order, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiResponse max.Order
|
var apiResponse max.Order
|
||||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
|
||||||
return nil, err
|
type responseUnmarshaler interface {
|
||||||
|
Unmarshal(data []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok {
|
||||||
|
if err := unmarshaler.Unmarshal(response.Body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The line below checks the content type, however, some API server might not send the correct content type header,
|
||||||
|
// Hence, this is commented for backward compatibility
|
||||||
|
// response.IsJSON()
|
||||||
|
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseValidator interface {
|
||||||
|
Validate() error
|
||||||
|
}
|
||||||
|
|
||||||
|
if validator, ok := interface{}(&apiResponse).(responseValidator); ok {
|
||||||
|
if err := validator.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &apiResponse, nil
|
return &apiResponse, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,13 +109,21 @@ func (g *GetMarginADRatioRequest) GetSlugsMap() (map[string]string, error) {
|
||||||
return slugs, nil
|
return slugs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPath returns the request path of the API
|
||||||
|
func (g *GetMarginADRatioRequest) GetPath() string {
|
||||||
|
return "/api/v3/wallet/m/ad_ratio"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do generates the request object and send the request object to the API endpoint
|
||||||
func (g *GetMarginADRatioRequest) Do(ctx context.Context) (*ADRatio, error) {
|
func (g *GetMarginADRatioRequest) Do(ctx context.Context) (*ADRatio, error) {
|
||||||
|
|
||||||
// no body params
|
// no body params
|
||||||
var params interface{}
|
var params interface{}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
apiURL := "/api/v3/wallet/m/ad_ratio"
|
var apiURL string
|
||||||
|
|
||||||
|
apiURL = g.GetPath()
|
||||||
|
|
||||||
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -128,8 +136,32 @@ func (g *GetMarginADRatioRequest) Do(ctx context.Context) (*ADRatio, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiResponse ADRatio
|
var apiResponse ADRatio
|
||||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
|
||||||
return nil, err
|
type responseUnmarshaler interface {
|
||||||
|
Unmarshal(data []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok {
|
||||||
|
if err := unmarshaler.Unmarshal(response.Body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The line below checks the content type, however, some API server might not send the correct content type header,
|
||||||
|
// Hence, this is commented for backward compatibility
|
||||||
|
// response.IsJSON()
|
||||||
|
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseValidator interface {
|
||||||
|
Validate() error
|
||||||
|
}
|
||||||
|
|
||||||
|
if validator, ok := interface{}(&apiResponse).(responseValidator); ok {
|
||||||
|
if err := validator.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &apiResponse, nil
|
return &apiResponse, nil
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user