Compare commits

...

236 Commits

Author SHA1 Message Date
c9s
ac31fd0da2
Merge df5838dc9e into 37106c35b7 2024-09-19 09:41:08 +08:00
c9s
37106c35b7
Merge pull request #1748 from c9s/c9s/add-pos-index
Some checks failed
Go / build (1.21, 6.2) (push) Has been cancelled
golang-lint / lint (push) Has been cancelled
IMPROVE: [db] add index on positions table
2024-09-18 17:06:34 +08:00
c9s
8265ada5a0
compile and update migration package 2024-09-18 13:30:56 +08:00
c9s
744ca57c71
migrations: add index on positions table 2024-09-18 13:30:20 +08:00
c9s
17d3097e06
add v1.60.3 release note
Some checks failed
Go / build (1.21, 6.2) (push) Has been cancelled
golang-lint / lint (push) Has been cancelled
2024-09-16 00:31:00 +08:00
c9s
a0c41f89f2
bump version to v1.60.3 2024-09-16 00:31:00 +08:00
c9s
35a6639530
update command doc files 2024-09-16 00:30:59 +08:00
c9s
26b1fd2ae7
xmaker: fix price initialization 2024-09-16 00:29:37 +08:00
c9s
80430fec46
Merge pull request #1744 from lanphan/activeorder
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
2024-09-14 23:19:10 +08:00
Lan Phan
1f8b2b3710 call b.EmitNew() when new order is added into activeorderbook 2024-09-14 18:26:36 +07:00
c9s
25a2203000
add v1.60.2 release note
Some checks failed
Go / build (1.21, 6.2) (push) Has been cancelled
golang-lint / lint (push) Has been cancelled
2024-09-12 17:51:57 +08:00
c9s
aca2c32442
bump version to v1.60.2 2024-09-12 17:51:57 +08:00
c9s
df915d6ee8
update command doc files 2024-09-12 17:51:57 +08:00
c9s
0d6b7b29d5
Merge pull request #1742 from c9s/c9s/fix-ws-close-err
FIX: types/stream: change errorf to warnf
2024-09-12 17:46:24 +08:00
c9s
2784ef4687
Merge pull request #1741 from c9s/c9s/upgrade-requestgen
FIX: upgrade github.com/c9s/requestgen to 1.4.3
2024-09-12 17:37:55 +08:00
c9s
ea8f3a5485
types/stream: change errorf to warnf 2024-09-12 17:35:13 +08:00
c9s
52f32e0ad0
upgrade github.com/c9s/requestgen to 1.4.3 2024-09-12 17:27:30 +08:00
c9s
50cdf617f2
Merge pull request #1740 from c9s/c9s/upgrade-requestgen
FIX: upgrade requestgen and re-generate max cancel order request files
2024-09-12 12:31:08 +08:00
c9s
de0d11b511
max: regenerate order cancel requests 2024-09-11 16:47:20 +08:00
c9s
789bb1e53e
upgrade github.com/c9s/requestgen to 1.4.2 2024-09-11 16:38:35 +08:00
kbearXD
a9b71adce9
Merge pull request #1739 from c9s/kbearXD/dca2/set-exchange-fee-rate
Some checks failed
Go / build (1.21, 6.2) (push) Has been cancelled
golang-lint / lint (push) Has been cancelled
FEATURE: [dca2] set exchange fee rate for round position
2024-09-11 16:15:13 +08:00
kbearXD
f83491af26 FEATURE: [dca2] set exchange fee rate for round position 2024-09-11 15:40:59 +08:00
bailantaotao
82d07a0098
Merge pull request #1738 from c9s/edwin/okx/add-polusdt
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
FEATURE: [okx] update symbols to latest
2024-09-10 17:23:42 +08:00
edwin
619cce53f6 pkg/exchange: update to latest 2024-09-10 17:11:58 +08:00
c9s
643ecde2e9
Merge pull request #1726 from c9s/dependabot/github_actions/morphy2k/revive-action-2.5.10
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
dep: bump morphy2k/revive-action from 2.5.9 to 2.5.10
2024-09-10 10:28:58 +08:00
c9s
d7ddc9c462
Merge pull request #1737 from c9s/c9s/xmaker/ioc-arb
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
FEATURE: [xmaker] implement tryArbitrage
2024-09-09 22:57:20 +08:00
c9s
bd19b63c7b
go: update sum file 2024-09-09 22:23:25 +08:00
c9s
83ed9b0811
go: upgrade go sqlite 2024-09-09 22:23:02 +08:00
c9s
34ef50d889
xmaker: refactor and clean up tryArbitrage 2024-09-09 22:03:06 +08:00
c9s
52925c5643
xmaker: calculate balance for arbitrage 2024-09-09 18:12:46 +08:00
c9s
b4f2748892
xmaker: fix sides 2024-09-09 18:03:03 +08:00
c9s
ceda1e06b9
xmaker: implement tryArbitrage 2024-09-09 17:49:53 +08:00
c9s
bc1715f8ad
Merge pull request #1736 from c9s/kbearXD/session/remove-log
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
MINOR: [session] remove environment nil validation log
2024-09-09 16:17:17 +08:00
c9s
f361b19564
Merge pull request #1734 from c9s/c9s/xmaker/ioc-arb
REFACTOR: [xmaker] refactor for supporting ioc arb [part1]
2024-09-09 16:05:11 +08:00
kbearXD
f44486447e MINOR: [session] remove environment nil validation log 2024-09-09 16:04:04 +08:00
kbearXD
a2eca66af5
Merge pull request #1735 from c9s/kbearXD/dca2/fix-memory-leak
FIX: configure environment
2024-09-09 15:50:24 +08:00
kbearXD
129e2c438e FIX: add debug log 2024-09-09 15:13:02 +08:00
c9s
90749f4873
xmaker: pull out s.UseDepthPrice dependency 2024-09-09 15:04:56 +08:00
c9s
77dfe213e5
xmaker: pull out getLayerPrice and add test against the method 2024-09-09 14:41:41 +08:00
c9s
960ea89d8c
testhelper: add more test helpers 2024-09-09 14:41:27 +08:00
c9s
f24a96c8c3
xmaker: refactor getInitialLayerQuantity for quantity multiplier 2024-09-07 14:19:07 +08:00
c9s
6ad16b7488
xmaker: add EnableArbitrage option and makerBook 2024-09-07 13:47:34 +08:00
c9s
e14f09a914
xmaker: add sourceDepthLevel option 2024-09-06 21:47:43 +08:00
c9s
3cc96ff6ad
Merge pull request #1724 from dropbigfish/main
Some checks failed
Go / build (1.21, 6.2) (push) Has been cancelled
golang-lint / lint (push) Has been cancelled
fix: fix slice init length
2024-09-06 18:06:21 +08:00
c9s
6ea996bec4
Merge pull request #1733 from c9s/c9s/fix-initialize-defaults-steps
FIX: [bbgo] fix the defaults / initialize steps
2024-09-06 17:54:13 +08:00
c9s
ef935f8ca0
Merge pull request #1732 from lanphan/telegram_doc
fix env name
2024-09-06 17:40:26 +08:00
c9s
a282654c02
bbgo: fix the defaults / initialize steps 2024-09-06 17:33:31 +08:00
Lan Phan
336dd7a108 fix env name 2024-09-05 22:51:43 +07:00
kbearXD
f2a443a499
Merge pull request #1728 from c9s/kbearXD/dca2/fix-memory-leak
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
FIX: [core] fix memory leak
2024-09-05 17:52:41 +08:00
kbearXD
63a58e1b12 FIX: fix memory leak 2024-09-05 17:05:58 +08:00
c9s
1b40118bba
Merge pull request #1731 from longhutianjie/main
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
bug: fix json tag
2024-09-05 13:57:31 +08:00
longhutianjie
c75a685cc0
bug: fix json tag 2024-09-04 17:58:27 +08:00
c9s
50262f2a84
Merge pull request #1730 from c9s/c9s/xmaker/market-trade-signal
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
FEATURE: [xmaker] add market trade signal
2024-09-04 16:27:42 +08:00
c9s
9fc3a1b44a
xmaker: rename to aggTradeVolume 2024-09-04 16:09:58 +08:00
c9s
656112de45
xmaker: call signalConfig.TradeVolumeWindowSignal.Bind 2024-09-04 16:07:28 +08:00
c9s
ba73eeaad1
xmaker: add TradeVolumeWindowSignal 2024-09-04 15:59:21 +08:00
c9s
2527c0c7b7
max: convert v3 DepositStateFailed into rejected 2024-09-04 15:00:37 +08:00
c9s
a2f8fe5f72
max: add v3 DepositStateFailed state 2024-09-04 14:59:58 +08:00
c9s
ed51eff242
max: drop unused function 2024-09-04 14:59:10 +08:00
c9s
f6865f664c
add v1.60.1 release note
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
2024-09-04 14:58:07 +08:00
c9s
24de49860f
bump version to v1.60.1 2024-09-04 14:58:07 +08:00
c9s
83dc981c92
update command doc files 2024-09-04 14:58:07 +08:00
c9s
ec68e3c5f6
Merge pull request #1727 from lanphan/ioc
FIX: update timeInForce for binance margin order
2024-09-04 14:38:40 +08:00
c9s
699164484b
Merge pull request #1729 from c9s/c9s/max/fix-v3-deposit-state-conversion
FIX: [max] fix v3 deposit state conversion
2024-09-04 11:41:22 +08:00
c9s
f27afac77b
max: use error log instead of warning log for convertion 2024-09-04 11:20:30 +08:00
c9s
d404b20bd1
deposit2transfer: fix comments 2024-09-04 11:19:43 +08:00
c9s
1b8d7bd805
max: fix v3 deposit state conversion 2024-09-04 11:17:56 +08:00
c9s
7d034d1ba8
bbgo: add stringer method to the quota struct
Some checks failed
Go / build (1.21, 6.2) (push) Has been cancelled
golang-lint / lint (push) Has been cancelled
2024-09-03 03:26:47 +08:00
c9s
7135895006
xmaker: fix MaxExposurePosition check condition 2024-09-03 03:25:37 +08:00
Lan Phan
ba913ce4de update timeInForce for binance margin order 2024-09-03 00:38:17 +07:00
c9s
f12ba1adb9
bbgo: add comments to the quota methods
Some checks are pending
Go / build (1.21, 6.2) (push) Waiting to run
golang-lint / lint (push) Waiting to run
2024-09-02 22:18:13 +08:00
c9s
294e529a98
xmaker: add more logs 2024-09-02 16:08:51 +08:00
c9s
f30aca1b5a
xmaker: update position metrics when restored 2024-09-02 15:51:31 +08:00
c9s
f9b9832fff
add more logs 2024-09-02 15:51:31 +08:00
c9s
2bf1072977
Merge pull request #1725 from c9s/c9s/xmaker/stb-improvements
IMPROVE: [xmaker] improve hedge margin account leverage calculation
2024-09-02 15:29:53 +08:00
dependabot[bot]
01f8b78008
dep: bump morphy2k/revive-action from 2.5.9 to 2.5.10
Bumps [morphy2k/revive-action](https://github.com/morphy2k/revive-action) from 2.5.9 to 2.5.10.
- [Release notes](https://github.com/morphy2k/revive-action/releases)
- [Commits](https://github.com/morphy2k/revive-action/compare/v2.5.9...v2.5.10)

---
updated-dependencies:
- dependency-name: morphy2k/revive-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 06:39:35 +00:00
c9s
4d1c357c3d
xmaker: reuse makerMarket field 2024-09-01 17:55:00 +08:00
c9s
a4833524cf
xmaker: add more logs 2024-09-01 16:41:16 +08:00
c9s
ed073264f1
xmaker: add MaxHedgeAccountLeverage option 2024-09-01 15:42:36 +08:00
c9s
ad6056834e
xmaker: separate maximumValueInUsd in a new var 2024-09-01 01:34:25 +08:00
c9s
8b1306a6a6
xmaker: calculate maximum leveraged account value 2024-09-01 01:31:50 +08:00
c9s
d85da78e17
xmaker: improve hedge account credit calculation 2024-09-01 00:58:50 +08:00
dropbigfish
9d581adc04 fix: fix slice init length
Signed-off-by: dropbigfish <fillfish@foxmail.com>
2024-09-01 00:36:43 +08:00
c9s
cff7103ece
fix math import
Some checks failed
Go / build (1.21, 6.2) (push) Has been cancelled
golang-lint / lint (push) Has been cancelled
2024-08-30 22:41:13 +08:00
c9s
d501e8ff4d
xmaker: apply math.Abs on signal for margin scale 2024-08-30 22:38:59 +08:00
c9s
b87213827e
Merge pull request #1723 from c9s/c9s/xmaker/add-signals
FIX: [xmaker] avoid calculate margin from 0.0 signal
2024-08-30 18:01:57 +08:00
c9s
ec80cbfd9f
xmaker: check 0.0 2024-08-30 17:52:28 +08:00
c9s
04bed165d0
Merge pull request #1722 from c9s/c9s/xmaker/add-signals
FEATURE: [xmaker] add signals
2024-08-30 17:50:52 +08:00
c9s
7c4b3e81df
xmaker: add more logs 2024-08-30 17:42:20 +08:00
c9s
cc820d3df0
xmaker: apply margin from signal 2024-08-30 17:39:25 +08:00
c9s
371db8e7d1
xmaker: update signal conditions to metrics 2024-08-30 17:18:29 +08:00
c9s
b8abc065de
xmaker: initialize bollinger band signal 2024-08-30 17:15:12 +08:00
c9s
9ebab4f4f7
xmaker: add signal providers 2024-08-30 15:44:55 +08:00
c9s
d9fb9ff3e0
xmaker: remove unused var 2024-08-29 13:18:50 +08:00
c9s
88d7783843
bbgo/activeOrderBook: filter market order when filtering existing orders 2024-08-29 00:38:44 +08:00
c9s
86e464b1bc
xmaker: when submitting hedge orders, do not add it to the active orderbook 2024-08-29 00:33:04 +08:00
c9s
8e91a023ca
Merge pull request #1721 from c9s/c9s/xmaker/fix-agg-price-method
FIX: [xmaker] fix aggregatePrice method
2024-08-28 22:46:19 +08:00
c9s
8de0c67503
xmaker: fix aggregatePrice function 2024-08-28 22:36:44 +08:00
c9s
e187614179
xmaker: log best bid and best ask 2024-08-28 22:32:56 +08:00
c9s
bda66040e7
Merge pull request #1720 from c9s/c9s/xmaker/margin-credit-improvement
FEATURE: [xmaker] margin credit improvement
2024-08-28 16:53:33 +08:00
c9s
d36bbe5fb5
xmaker: adjust accountUpdater's ticker to 3 min 2024-08-28 16:41:50 +08:00
c9s
77b7b29739
xmaker: adjust credit buffer algo 2024-08-28 16:37:39 +08:00
c9s
1d6282a10b
xmaker: add account updater and handle margin account to add more flexibility 2024-08-28 16:07:11 +08:00
c9s
108fb6138a
xmaker: check hedge balance only when it's spot account 2024-08-28 14:48:38 +08:00
c9s
1e2f086643
xmaker: set notional var back 2024-08-27 22:45:43 +08:00
c9s
6011fd5157
xmaker: set profitChanged only when Hedge is called 2024-08-27 22:45:11 +08:00
c9s
652c9b62e8
Merge pull request #1719 from c9s/c9s/xmaker/stb-improvements
IMPROVE: [xmaker] fix bollinger band price calculation
2024-08-27 20:14:48 +08:00
c9s
9939b5ce68
xmaker: improve bid/ask pricing when UseDepthPrice and DepthQuantity are on 2024-08-27 20:05:46 +08:00
c9s
a740ef10c2
xmaker: add price checker and field logger 2024-08-27 20:05:45 +08:00
c9s
f81ce5ce95
xmaker: improve bollinger band trend detection 2024-08-27 20:05:45 +08:00
c9s
4ae8ad77b3
xmaker: add profit changed flag for notification 2024-08-27 20:05:45 +08:00
c9s
3819feacf3
xmaker: improve dust quantity check 2024-08-27 20:05:45 +08:00
c9s
4af12a7e5d
Merge pull request #1718 from c9s/c9s/xmaker/config-metrics
FEATURE: [xmaker] add more config metrics
2024-08-27 18:09:47 +08:00
c9s
3d4ccd1344
xmaker: integrate bid/ask margin metrics 2024-08-27 15:39:38 +08:00
c9s
b3c8739983
xmaker: add config bid/ask margin metrics 2024-08-27 15:35:39 +08:00
c9s
5edf5a763f
Merge pull request #1717 from c9s/c9s/xmaker/worker-method
REFACTOR: [xmaker] refactor hedge worker and quote worker
2024-08-27 15:32:56 +08:00
c9s
7e65aca62e
xmaker: add more config metrics 2024-08-27 15:22:06 +08:00
c9s
199b86df86
xmaker: add comments 2024-08-27 14:52:27 +08:00
c9s
6fb6467d59
xmaker: refactor hedge worker and quote worker 2024-08-27 14:48:30 +08:00
c9s
2479f2e9fc
Merge pull request #1715 from c9s/c9s/upgrade-pkgs
UPGRADE: [go] upgrade packages that are too old
2024-08-26 19:06:54 +08:00
c9s
e77f0cbacb
Merge pull request #1714 from c9s/dependabot/github_actions/actions/setup-node-4
dep: bump actions/setup-node from 2 to 4
2024-08-26 18:54:54 +08:00
c9s
be386d085a
go: upgrade packages that are too old 2024-08-26 18:54:21 +08:00
c9s
ea93bf959a
Merge pull request #1716 from c9s/c9s/xmaker/stb-improvements
FIX: [xmaker] profit object can be nil
2024-08-26 18:39:29 +08:00
c9s
e3079c134c
xmaker: add todo note 2024-08-26 18:37:02 +08:00
c9s
7367ea73b8
xmaker: profit object can be nil 2024-08-26 18:35:10 +08:00
c9s
f96e0d6552
config: change staging to local 2024-08-26 16:11:48 +08:00
c9s
68e56d76e6
Merge pull request #1713 from c9s/c9s/chart/env-section 2024-08-26 14:59:00 +08:00
dependabot[bot]
ec51eb7cdd
dep: bump actions/setup-node from 2 to 4
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 06:21:47 +00:00
c9s
3ec509b379
ci: use list literal 2024-08-26 13:34:20 +08:00
c9s
84edcb0e02
ci: add github actions pull requests types 2024-08-26 13:33:43 +08:00
c9s
866751cc3d
Merge pull request #1712 from c9s/c9s/xmaker/add-profit-fixer
FEATURE: [xmaker] add profit fixer
2024-08-26 13:32:41 +08:00
c9s
80949bf0e1
Merge pull request #1710 from c9s/c9s/xmaker/stb-improvements
IMPROVE: [xmaker] improve stability
2024-08-26 13:32:19 +08:00
c9s
86cc821626
config: add circuit breaker enabled = true 2024-08-26 13:16:40 +08:00
c9s
90bcd25bef
Merge pull request #1711 from lanphan/autoborrowrepay
FEATURE: [binance] add new margin order side effect AUTO_BORROW_REPAY
2024-08-26 13:15:21 +08:00
c9s
f59ff90c5e
bump chart version 2024-08-26 13:04:45 +08:00
c9s
3013e37a38
chart: add env vars section into the chart 2024-08-26 13:03:58 +08:00
c9s
349a3040f3
Merge pull request #1709 from c9s/c9s/xmaker/improve-profit-ticker
IMPROVE: [xmaker] improve profit stats ticker and integrate rate limiter
2024-08-26 12:50:31 +08:00
c9s
7fdb3f671f
risk: add Enabled config to circuitbreaker 2024-08-26 12:50:13 +08:00
c9s
321eb23514
config: add profit fixer to the sample config 2024-08-26 12:46:45 +08:00
c9s
77e185ffa7
xmaker: add profit fixer 2024-08-26 12:45:18 +08:00
c9s
e7fd90ed59
Merge pull request #1708 from c9s/c9s/xmaker/integrate-circuitbreaker
FEATURE: [xmaker] integrate circuit breaker
2024-08-26 12:36:17 +08:00
Lan Phan
9a7517a72a add new sideeffect AUTO_BORROW_REPAY 2024-08-25 12:31:10 +07:00
c9s
9a1d9ae27b
xmaker: rewrite maker order submission logics and integrate metrics 2024-08-24 21:22:35 +08:00
c9s
c76a80da6a
xmaker: add xmaker metrics 2024-08-24 21:22:34 +08:00
c9s
afac81a3e8
all: integrate metrics into stream book 2024-08-24 21:22:34 +08:00
c9s
b0cc009d67
remove travis config 2024-08-24 13:29:45 +08:00
c9s
0df56ad6e7
xmaker: use v2 indicator boll 2024-08-24 13:28:32 +08:00
c9s
1c1959b8a8
all: rename priceresolver to pricesolver
integrate pricesolver into xmaker
2024-08-24 12:28:05 +08:00
c9s
9f01dc28c8
xmaker: remove report ticker and
isolate rate limiter for each different instance
2024-08-24 12:19:34 +08:00
c9s
e8bd370aa2
xmaker: remove duplicated log entry 2024-08-24 12:13:44 +08:00
c9s
5ca1c4fb62
xmaker: rewrite and clean up order submission 2024-08-24 12:13:15 +08:00
c9s
f7dc07327e
xmaker: assign position strategy id and instance id 2024-08-24 12:01:11 +08:00
c9s
6ef8aa62e5
xmaker: integrate CircuitBreaker 2024-08-24 11:58:09 +08:00
c9s
5f65e87e89
change default HaltDuration to 1h 2024-08-24 11:43:12 +08:00
c9s
14fff8dbad
xmaker: integrate circuitbreaker 2024-08-24 11:42:16 +08:00
c9s
92ad80f2f8
bump chart version to 0.4.2 2024-08-23 21:43:00 +08:00
c9s
0a2d442ceb
fix cronjob_sync indentation 2024-08-23 21:42:30 +08:00
c9s
f7f8ecfd15
Merge pull request #1707 from c9s/c9s/xmaker/stb-improvements
FIX: [xmaker] position metrics missing label
2024-08-23 20:12:31 +08:00
c9s
40a0585187
types: fix missing labels 2024-08-23 19:59:56 +08:00
c9s
9f510da78b
xmaker: use margin order to hedge positions 2024-08-23 16:57:29 +08:00
c9s
c2679b4ae1
Merge pull request #1705 from c9s/c9s/k8s/sync-cronjob
FIX: [k8s] fix sync.enabled option
2024-08-23 15:56:55 +08:00
c9s
106a01e508
wrap cronjob with sync.enabled var 2024-08-23 15:19:07 +08:00
c9s
02ff04cfb5
fix default sync schedule 2024-08-23 15:04:00 +08:00
c9s
80ad5c32b5
Merge pull request #1704 from c9s/c9s/k8s/sync-cronjob
FEATURE: [k8s] add cronjob for sync
2024-08-23 14:57:07 +08:00
c9s
e3c4314aed
bump chart versions 2024-08-23 14:23:24 +08:00
c9s
c7f62cf1e5
add since parameter 2024-08-23 14:22:49 +08:00
c9s
4bbb9c5e38
add cronjob for sync 2024-08-23 14:19:33 +08:00
c9s
b2f1f7d735
Merge pull request #1700 from c9s/narumi/autobuy-boll
Fix: [autobuy] fix error when bollinger settings is not set
2024-08-23 13:05:18 +08:00
narumi
1b06fcc961 validate parameters 2024-08-22 14:36:06 +08:00
narumi
b820fccce1 add order type to config 2024-08-22 14:36:06 +08:00
c9s
bb06a6a046
Merge pull request #1703 from c9s/c9s/core/position-metrics
FEATURE: [core] add position metrics
2024-08-22 13:32:39 +08:00
c9s
72575e3cd8
elliottwave: use AverageCost instead 2024-08-22 11:26:46 +08:00
c9s
a900c72032
types/position: drop approximateAverageCost 2024-08-22 11:19:54 +08:00
c9s
5635e31487
types: pull out calculateFeeInQuote method 2024-08-22 11:07:45 +08:00
c9s
e2d68f2a86
types: add fee cost settter to the position 2024-08-22 10:59:38 +08:00
c9s
6718087cb1
Merge pull request #1702 from c9s/c9s/core/improve-metrics
IMPROVE: improve balance related metrics
2024-08-22 10:02:51 +08:00
c9s
9136877207
types: update position metrics after adding trades 2024-08-21 16:35:57 +08:00
c9s
c063df6467
document privateChannels and privateChannelSymbols 2024-08-21 16:24:19 +08:00
c9s
80fc10a1fd
bbgo: add session name to the metrics 2024-08-21 15:46:09 +08:00
c9s
fead99aaa6
add more balance metrics 2024-08-21 15:33:27 +08:00
c9s
40d3a40277
types: add marginType 2024-08-21 15:24:43 +08:00
c9s
055cfbb3ff
Merge pull request #1699 from c9s/c9s/refactor-twap
REFACTOR: [twap] upgrade twap command and add optional order update rate limiter
2024-08-21 15:20:57 +08:00
c9s
d18adfcbc7
Merge pull request #1701 from c9s/release/v1.60
RELEASE: v1.60.0
2024-08-21 14:55:34 +08:00
c9s
48eeae0a10
add v1.60.0 release note 2024-08-21 14:42:59 +08:00
c9s
d855d9bbc0
bump version to v1.60.0 2024-08-21 14:42:59 +08:00
c9s
1a1f700122
update command doc files 2024-08-21 14:42:58 +08:00
c9s
3a2e4dfd26
add new scripts 2024-08-21 14:40:29 +08:00
c9s
6f0cf8f3e0
chmod +x 2024-08-21 14:37:39 +08:00
narumi
731fa9af7e fix error when bollinger settings is not set 2024-08-21 13:28:22 +08:00
c9s
a06b63c897
twap: rename constructor 2024-08-20 18:13:42 +08:00
c9s
ebaf3a330f
twap: pull out submitOrder method 2024-08-20 17:53:19 +08:00
c9s
a0cbf82d97
twap: handle delayInterval after canceling order 2024-08-20 17:51:44 +08:00
c9s
9a2b792ed1
twap: split doneSignal into a single file 2024-08-20 17:49:18 +08:00
c9s
2392fddc3c
fix method name 2024-08-20 17:47:39 +08:00
c9s
c92c395f67
twap: improve rate limiter syntax parser and support order update rate limiter in twap 2024-08-20 17:07:29 +08:00
c9s
48029f95cc
cmd: pull out and refine twap order executor command 2024-08-20 16:24:34 +08:00
c9s
8911f88ed6
Merge pull request #1683 from c9s/dependabot/github_actions/codecov/codecov-action-4
dep: bump codecov/codecov-action from 3 to 4
2024-08-20 14:46:16 +08:00
c9s
a1c77e42f1
Merge pull request #1680 from c9s/dependabot/github_actions/docker/setup-buildx-action-3
dep: bump docker/setup-buildx-action from 1 to 3
2024-08-20 14:42:33 +08:00
c9s
c03f5f47c2
Merge pull request #1684 from c9s/dependabot/github_actions/docker/metadata-action-5
dep: bump docker/metadata-action from 3 to 5
2024-08-20 14:42:20 +08:00
c9s
40260a1204
Merge pull request #1681 from c9s/dependabot/github_actions/actions/checkout-4
dep: bump actions/checkout from 2 to 4
2024-08-20 14:42:10 +08:00
c9s
baffefac07
Merge pull request #1689 from anywhy/fix_float64_series
fix float64 series use mean or stdev function result is zero
2024-08-20 14:41:29 +08:00
c9s
6d3a18ad55
twap: call trade collector process when shutdown 2024-08-20 14:30:08 +08:00
c9s
d1617b6a0b
Merge pull request #1697 from c9s/c9s/refactor-twap
FEATURE: redesign and refactor twap order executor
2024-08-20 14:24:48 +08:00
c9s
b9c41b7ad7
twap: improve cancelContextIfTargetQuantityFilled check method 2024-08-20 14:14:19 +08:00
c9s
0530809834
fix position test 2024-08-20 14:10:22 +08:00
c9s
d8fad8250c
fix duplicated field 2024-08-20 14:01:19 +08:00
c9s
c8aea81505
twap: implement twap mock testing 2024-08-20 14:01:04 +08:00
c9s
cec078f4bf
twap: add stream executor test 2024-08-20 14:01:04 +08:00
c9s
648e10fd7c
binance: fix time in force setting for limit maker 2024-08-20 14:01:04 +08:00
c9s
b7d18e687e
twap: implement orderUpdater 2024-08-20 14:01:03 +08:00
c9s
51c1b995c2
twap: add v2 fixed quantity executor 2024-08-20 14:01:03 +08:00
dependabot[bot]
f845918d80
dep: bump actions/checkout from 2 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-20 05:49:53 +00:00
c9s
47c7714d33
Merge pull request #1698 from c9s/feature/max/update-get-trade-api
FEATURE: update get trades api
2024-08-20 13:49:39 +08:00
c9s
408a41342b
Merge pull request #1692 from c9s/narumi/delete-python
DELETE: delete python
2024-08-20 13:49:17 +08:00
c9s
f7ad141b04
Merge pull request #1693 from anywhy/fix_binance_query_order
fix binance exchange query futures order
2024-08-19 18:01:03 +08:00
kbearXD
90712aff29 FEATURE: update get trades api 2024-08-19 17:05:02 +08:00
c9s
0a83c26fd5
types: add warning to the price type 2024-08-17 14:15:43 +08:00
c9s
1294cd95be
rename twap.Execution to twap.StreamExecutor 2024-08-17 14:09:25 +08:00
c9s
9dd85623b9
types,strategy: refactor price type and add more bbo (best bid offer) 2024-08-17 14:05:29 +08:00
c9s
621a2b86cf
twap: move twap execution to a single package 2024-08-17 13:29:27 +08:00
anywhy
714275fedb fix binance exchange query futures order 2024-08-16 13:06:43 +08:00
anywhy
b27fc896f9 add serie_float64 test case 2024-08-15 16:44:45 +08:00
narumi
cf42d9d0b0 delete python 2024-08-13 17:07:56 +08:00
Any Yang
8773c220f5
fix float64 series use mean or stdev function result is zero 2024-08-07 17:52:39 +08:00
dependabot[bot]
b4bb7b6716
dep: bump docker/metadata-action from 3 to 5
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 3 to 5.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v3...v5)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 06:57:58 +00:00
dependabot[bot]
57a68f8063
dep: bump codecov/codecov-action from 3 to 4
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 06:57:53 +00:00
dependabot[bot]
c35805916a
dep: bump docker/setup-buildx-action from 1 to 3
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 06:57:38 +00:00
c9s
df5838dc9e
indicator/v2: fix TestVolumeProfile test since the timestamp was parsed incorrectly 2024-07-09 15:28:30 +08:00
c9s
a4f220749a
csvsource: fix okex timestamp parsing (it's milliseconds) 2024-07-09 15:28:30 +08:00
c9s
ee4455fa85
csvsource: fix TestCSVKLineReader_ReadWithBinanceDecoder test 2024-07-09 15:28:30 +08:00
c9s
dae140aacc
bbgo: move CsvConfig to bbgo package 2024-07-09 15:28:30 +08:00
c9s
4d84308b99
indicator/v2: rename volume profile indicator and fix tests 2024-07-09 15:28:30 +08:00
c9s
29301cbe7f
all: fix constant naming and type naming 2024-07-09 15:28:30 +08:00
Sven Woldt
af1e63f345
release candidate csvsource backtest 2024-07-09 15:28:30 +08:00
c9s
e23e8fde1d
cmd: skip reports for session has no trade 2024-07-09 15:28:29 +08:00
Sven Woldt
a484211aa5
add more trade stats 2024-07-09 15:28:29 +08:00
229 changed files with 7804 additions and 4478 deletions

View File

@ -20,7 +20,7 @@ jobs:
steps:
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
uses: docker/metadata-action@v5
with:
# list of Docker images to use as base name for tags
images: |
@ -38,7 +38,7 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:

View File

@ -2,8 +2,10 @@ name: Go
on:
push:
branches: [ main ]
branches:
- "main"
pull_request:
types: [synchronize, opened, reopened]
branches:
- "main"
- "v*"
@ -27,7 +29,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
# lfs: 'true'
ssh-key: ${{ secrets.git_ssh_key }}
@ -88,13 +90,13 @@ jobs:
sed -i -e '/_requestgen.go/d' coverage_dnum.txt
- 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:
reporter: github-pr-review
fail_on_error: true
- name: Upload Coverage Report
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
files: ./coverage.txt,./coverage_dnum.txt

View File

@ -21,9 +21,9 @@ jobs:
node-version: [ 16.x ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install -g yarn

View File

@ -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 .

View File

@ -16,13 +16,13 @@ jobs:
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: "1.21"
- name: Install Node
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: "16"
- name: Build

2
.gitignore vendored
View File

@ -14,6 +14,7 @@
*.out
.idea
.vscode
# Dependency directories (remove the comment below to include it)
# vendor/
@ -48,6 +49,7 @@ testoutput
*.swp
/pkg/backtest/assets.go
/data/backtest
coverage.txt
coverage_dum.txt

View File

@ -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/...

View File

@ -15,9 +15,9 @@ type: application
# 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.
# 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
# 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.
appVersion: 1.21.0
appVersion: 1.60.0

View 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 -}}

View File

@ -98,6 +98,18 @@ spec:
mountPath: /config
# 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:
- secretRef:
name: {{ .Values.dotenv.secret | default .Release.Name }}

View File

@ -75,11 +75,11 @@ webserver:
enabled: false
metrics:
enabled: false
enabled: true
port: 9090
logFormatter:
enabled: false
enabled: true
format: json
grpc:
@ -89,6 +89,18 @@ grpc:
debug:
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:
# 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

View File

@ -21,7 +21,7 @@ logging:
trade: true
order: true
fields:
env: staging
env: local
sessions:
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
# 18002.00
pips: 10
persistence:
type: redis
## profitFixer is used for fixing the profit stats and the position
# 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

View File

@ -58,4 +58,4 @@ bbgo [flags]
* [bbgo userdatastream](bbgo_userdatastream.md) - Listen to session events (orderUpdate, tradeUpdate, balanceUpdate, balanceSnapshot)
* [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

View File

@ -41,4 +41,4 @@ bbgo account [--session SESSION] [flags]
* [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

View File

@ -50,4 +50,4 @@ bbgo backtest [flags]
* [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

View File

@ -40,4 +40,4 @@ bbgo balances [--session SESSION] [flags]
* [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

View File

@ -39,4 +39,4 @@ bbgo build [flags]
* [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

View File

@ -49,4 +49,4 @@ bbgo cancel-order [flags]
* [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

View File

@ -41,4 +41,4 @@ bbgo deposits [flags]
* [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

View File

@ -9,8 +9,10 @@ bbgo execute-order --session SESSION --symbol SYMBOL --side SIDE --target-quanti
### Options
```
--deadline duration deadline of the order execution
--deadline duration deadline duration of the order execution, e.g. 1h
--delay-interval duration order delay time after filled (default 3s)
-h, --help help for execute-order
--order-update-rate-limit string order update rate limit, syntax: 1+1/1m (default "1s")
--price-ticks int the number of price tick for the jump spread, default to 0
--session string the exchange session name for sync
--side string the trading side: buy or sell
@ -48,4 +50,4 @@ bbgo execute-order --session SESSION --symbol SYMBOL --side SIDE --target-quanti
* [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

View File

@ -42,4 +42,4 @@ bbgo get-order --session SESSION --order-id ORDER_ID [flags]
* [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

View File

@ -45,4 +45,4 @@ bbgo hoptimize [flags]
* [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

View File

@ -42,4 +42,4 @@ bbgo kline [flags]
* [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

View File

@ -41,4 +41,4 @@ bbgo list-orders open|closed --session SESSION --symbol SYMBOL [flags]
* [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

View File

@ -38,4 +38,4 @@ margin related history
* [bbgo margin loans](bbgo_margin_loans.md) - query loans 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

View File

@ -41,4 +41,4 @@ bbgo margin interests --session=SESSION_NAME --asset=ASSET [flags]
* [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

View File

@ -41,4 +41,4 @@ bbgo margin loans --session=SESSION_NAME --asset=ASSET [flags]
* [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

View File

@ -41,4 +41,4 @@ bbgo margin repays --session=SESSION_NAME --asset=ASSET [flags]
* [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

View File

@ -40,4 +40,4 @@ bbgo market [flags]
* [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

View File

@ -44,4 +44,4 @@ bbgo optimize [flags]
* [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

View File

@ -42,4 +42,4 @@ bbgo orderbook --session=[exchange_name] --symbol=[pair_name] [flags]
* [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

View File

@ -40,4 +40,4 @@ bbgo orderupdate [flags]
* [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

View File

@ -49,4 +49,4 @@ bbgo pnl [flags]
* [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

View File

@ -51,4 +51,4 @@ bbgo run [flags]
* [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

View File

@ -46,4 +46,4 @@ bbgo submit-order --session SESSION --symbol SYMBOL --side SIDE --quantity QUANT
* [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

View File

@ -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
###### Auto generated by spf13/cobra on 20-May-2024
###### Auto generated by spf13/cobra on 16-Sep-2024

View File

@ -42,4 +42,4 @@ bbgo trades --session=[exchange_name] --symbol=[pair_name] [flags]
* [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

View File

@ -40,4 +40,4 @@ bbgo tradeupdate --session=[exchange_name] [flags]
* [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

View File

@ -42,4 +42,4 @@ bbgo transfer-history [flags]
* [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

View File

@ -40,4 +40,4 @@ bbgo userdatastream [flags]
* [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

View File

@ -39,4 +39,4 @@ bbgo version [flags]
* [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

View File

@ -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,
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
TELEGRAM_BOT_AUTH_TOKEN=itsme55667788

62
doc/release/v1.60.0.md Normal file
View 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
View 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
View 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
View 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

View File

@ -1,9 +1,13 @@
## 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.*
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
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`
* 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
bbgo backtest -v --sync --config config/grid.yaml
```
@ -67,6 +74,11 @@ Run back-test:
```sh
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:

View File

@ -48,7 +48,7 @@ var rootCmd = &cobra.Command{
stream.SetPublicOnly()
stream.Subscribe(types.BookChannel, symbol, types.SubscribeOptions{})
streamBook := types.NewStreamBook(symbol)
streamBook := types.NewStreamBook(symbol, exchange.Name())
streamBook.BindStream(stream)
go func() {

96
go.mod
View File

@ -2,16 +2,16 @@
module github.com/c9s/bbgo
go 1.21
go 1.21.0
toolchain go1.21.6
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/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/c9s/requestgen v1.3.6
github.com/c9s/requestgen v1.4.3
github.com/c9s/rockhopper/v2 v2.0.4
github.com/cenkalti/backoff/v4 v4.2.0
github.com/cheggaaa/pb/v3 v3.0.8
@ -20,13 +20,13 @@ require (
github.com/fatih/camelcase v1.0.0
github.com/fatih/color v1.14.1
github.com/gertd/go-pluralize v0.2.1
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.9.1
github.com/go-redis/redis/v8 v8.8.0
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0
github.com/go-redis/redis/v8 v8.11.5
github.com/go-sql-driver/mysql v1.8.1
github.com/gofrs/flock v0.8.1
github.com/google/uuid v1.4.0
github.com/gorilla/websocket v1.5.1
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/heroku/rollrus v0.2.0
github.com/jedib0t/go-pretty/v6 v6.5.8
github.com/jmoiron/sqlx v1.3.4
@ -55,72 +55,77 @@ require (
github.com/zserge/lorca v0.1.9
go.uber.org/mock v0.4.0
go.uber.org/multierr v1.11.0
golang.org/x/oauth2 v0.15.0
golang.org/x/sync v0.7.0
golang.org/x/time v0.5.0
golang.org/x/oauth2 v0.22.0
golang.org/x/sync v0.8.0
golang.org/x/time v0.6.0
gonum.org/v1/gonum v0.8.2
google.golang.org/api v0.153.0
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
google.golang.org/api v0.194.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
gopkg.in/tucnak/telebot.v2 v2.5.0
gopkg.in/yaml.v3 v3.0.1
)
require (
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/auth v0.9.1 // 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
github.com/VividCortex/ewma v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/bytedance/sonic v1.12.1 // indirect
github.com/bytedance/sonic/loader v0.2.0 // 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/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/denisenkom/go-mssqldb v0.12.3 // 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/felixge/httpsnoop v1.0.4 // 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/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/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/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/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.8 // 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/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // 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/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/lib/pq v1.10.9 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // 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/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // 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/common v0.32.1 // indirect
@ -137,23 +142,24 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tebeka/strftime v0.1.3 // 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
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v0.19.0 // indirect
go.opentelemetry.io/otel/metric v0.19.0 // indirect
go.opentelemetry.io/otel/trace v0.19.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.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/image v0.5.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.20.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.24.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

245
go.sum
View File

@ -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.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
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.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.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.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
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/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
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/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/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.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
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 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
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/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
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/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
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.5.0/go.mod h1:41Up2dG4NfMXpCldrDPETEtiOq+pHoGsFZ73xGgaumo=
github.com/adshao/go-binance/v2 v2.6.0 h1:sXPkfix+SgBojJmkt+sNJbJBQZOJK5GFP/WtAu+B5r0=
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/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=
@ -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/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/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
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/go.mod h1:knmS8+Iyq5PPy1YUeIEq0pMFR4Y6x7z/CySc9HlZTCY=
github.com/c9s/requestgen v1.3.6 h1:ul7dZ2uwGYjNBjreooRfSY10WTXvQmQSjZsHebz6QfE=
github.com/c9s/requestgen v1.3.6/go.mod h1:QwkZudcv84kJ8g9+E0RDTj+13btFXbTvv2aI+zbuLbc=
github.com/c9s/requestgen v1.4.3 h1:0QZ27RVBLb9QuBKfiSBTOB5zSUuasrJm2p6/GZZHZZw=
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/go.mod h1:x0XuYI2Su3kS/74UYu/3Cqc9m5Dtzqh7j7JZarczfss=
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/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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
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/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/math32 v1.0.0/go.mod h1:Miac6hA1ohdDUTagnvJy/q+aNnEk16qWUdb8ZVhvCN0=
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/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/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/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
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.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
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.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
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/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
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/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
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.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
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/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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
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-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.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
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/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/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/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.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-redis/redis/v8 v8.8.0 h1:fDZP58UN/1RD3DjtTXP/fFZ04TFohSYhjZDkcDe2dnw=
github.com/go-redis/redis/v8 v8.8.0/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
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.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
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.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
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.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
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/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
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.2/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.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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 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-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
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.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
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.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
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/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.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.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
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/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/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.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
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-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
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/heroku/rollrus v0.2.0 h1:b3AgcXJKFJNUwbQOC2S69/+mxuTpe4laznem9VJdPEo=
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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
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/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.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.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
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.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/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.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
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.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=
@ -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/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/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
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/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.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.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
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/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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/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/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/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
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/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
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/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
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/errors v0.8.0/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.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.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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/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/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
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/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/valyala/fastjson v1.5.1 h1:SXaQZVSwLjZOVhDEhjiCcDtnX0Feu7Z7A1+C5atpoHM=
github.com/valyala/fastjson v1.5.1/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
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.27/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/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
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.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
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/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
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.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
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.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=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
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-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-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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
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-20180807140117-3d87b88a115f/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.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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
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-20180724234803-3673e40ba225/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-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-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-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-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-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-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-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-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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
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-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-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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
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-20181108010431-42b317875d0f/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-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-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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
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-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-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-20181107165924-66b7b1311ac8/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-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-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-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-20191228213918-04cbcbbfeed8/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-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-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-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-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-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-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.5.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.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
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.3.0/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.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
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-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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
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-20180525024113-a5b4c53f6e8b/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-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-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.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
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-20190513163551-3ee3066db522/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.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
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.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
google.golang.org/api v0.194.0 h1:dztZKG9HgtIpbI35FhfuSNR/zmaMVdxNlntHj1sIS4s=
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.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.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.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-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
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-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-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
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/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
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.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.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.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
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/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=
@ -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.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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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 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-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/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/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
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/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
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/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

View 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

View 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

View File

@ -55,7 +55,7 @@ var ErrEmptyOrderType = errors.New("order type can not be empty string")
type Exchange struct {
sourceName types.ExchangeName
publicExchange types.Exchange
srv *service.BacktestService
srv service.BackTestable
currentTime time.Time
account *types.Account
@ -78,7 +78,7 @@ type Exchange struct {
}
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) {
ex := sourceExchange
@ -366,6 +366,7 @@ func (e *Exchange) SubscribeMarketData(
loadedIntervals[sub.Options.Interval] = struct{}{}
default:
// todo support stream back test with csv tick source
// 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)
}
@ -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)
if len(symbols) == 0 {
log.Warnf("empty symbols, will not query kline data from the database")

View File

@ -359,6 +359,7 @@ func (b *ActiveOrderBook) Add(orders ...types.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) {
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) {
existingOrders.Add(o)
}

View File

@ -151,6 +151,7 @@ type Backtest struct {
// sync 1 second interval KLines
SyncSecKLines bool `json:"syncSecKLines,omitempty" yaml:"syncSecKLines,omitempty"`
CsvSource *CsvSourceConfig `json:"csvConfig,omitempty" yaml:"csvConfig,omitempty"`
}
func (b *Backtest) GetAccount(n string) BacktestAccount {
@ -706,3 +707,8 @@ func reUnmarshal(conf interface{}, tpe interface{}) (interface{}, error) {
return val.Elem().Interface(), nil
}
type CsvSourceConfig struct {
Market types.MarketType `json:"market"`
Granularity types.MarketDataType `json:"granularity"`
}

View File

@ -42,9 +42,9 @@ var defaultSyncBufferPeriod = 30 * time.Minute
// IsBackTesting is a global variable that indicates the current environment is back-test or not.
var IsBackTesting = false
var BackTestService *service.BacktestService
var BackTestService service.BackTestable
func SetBackTesting(s *service.BacktestService) {
func SetBackTesting(s service.BackTestable) {
BackTestService = s
IsBackTesting = s != nil
}
@ -87,7 +87,7 @@ type Environment struct {
TradeService *service.TradeService
ProfitService *service.ProfitService
PositionService *service.PositionService
BacktestService *service.BacktestService
BacktestService service.BackTestable
RewardService *service.RewardService
MarginService *service.MarginService
SyncService *service.SyncService

View File

@ -5,6 +5,8 @@ import "github.com/c9s/bbgo/pkg/types"
const MaxNumOfKLines = 5_000
const MaxNumOfKLinesTruncate = 100
const CapacityOfKLineWindowLimit = 5_000
// MarketDataStore receives and maintain the public market data of a single symbol
//go:generate callbackgen -type MarketDataStore
type MarketDataStore struct {
@ -57,10 +59,20 @@ func (store *MarketDataStore) AddKLine(k types.KLine) {
}
window.Add(k)
if len(*window) > MaxNumOfKLines {
*window = (*window)[MaxNumOfKLinesTruncate-1:]
}
truncateKLineWindowIfNeeded(window)
store.EmitKLineClosed(k)
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]
}
}

View 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++
}
}

View File

@ -9,34 +9,93 @@ var (
Help: "bbgo exchange session connection status",
},
[]string{
"session",
"exchange", // exchange name
"channel", // channel: user or market
"margin", // margin type: none, margin or isolated
"margin_type", // margin type: none, margin or isolated
"symbol", // margin symbol of the connection.
},
)
metricsLockedBalances = prometheus.NewGaugeVec(
metricsBalanceLockedMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_balances_locked",
Help: "bbgo exchange locked balances",
},
[]string{
"session",
"exchange", // exchange name
"margin", // margin of connection. 1 or 0
"margin_type", // margin of connection. 1 or 0
"symbol", // margin symbol of the connection.
"currency",
},
)
metricsAvailableBalances = prometheus.NewGaugeVec(
metricsBalanceAvailableMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_balances_available",
Help: "bbgo exchange available balances",
},
[]string{
"session",
"exchange", // exchange name
"margin", // margin of connection. none, margin or isolated
"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",
},
@ -48,8 +107,9 @@ var (
Help: "bbgo exchange session total balances",
},
[]string{
"session",
"exchange", // exchange name
"margin", // margin of connection. none, margin or isolated
"margin_type", // margin of connection. none, margin or isolated
"symbol", // margin symbol of the connection.
"currency",
},
@ -61,8 +121,9 @@ var (
Help: "bbgo exchange session trades",
},
[]string{
"session",
"exchange", // exchange name
"margin", // margin of connection. none, margin or isolated
"margin_type", // margin of connection. none, margin or isolated
"symbol", // margin symbol of the connection.
"side", // side: buy or sell
"liquidity", // maker or taker
@ -75,22 +136,24 @@ var (
Help: "bbgo trading volume",
},
[]string{
"session",
"exchange", // exchange name
"margin", // margin of connection. none, margin or isolated
"margin_type", // margin of connection. none, margin or isolated
"symbol", // margin symbol of the connection.
"side", // side: buy or sell
"liquidity", // maker or taker
},
)
metricsLastUpdateTimeBalance = prometheus.NewGaugeVec(
metricsLastUpdateTimeMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_last_update_time",
Help: "bbgo last update time of different channel",
},
[]string{
"session",
"exchange", // exchange name
"margin", // margin of connection. none, margin or isolated
"margin_type", // margin of connection. none, margin or isolated
"channel", // channel: user, market
"data_type", // type: balance, ticker, kline, orderbook, trade, order
"symbol", // for market data, trade and order
@ -103,10 +166,14 @@ func init() {
prometheus.MustRegister(
metricsConnectionStatus,
metricsTotalBalances,
metricsLockedBalances,
metricsAvailableBalances,
metricsBalanceNetMetrics,
metricsBalanceLockedMetrics,
metricsBalanceAvailableMetrics,
metricsBalanceDebtMetrics,
metricsBalanceBorrowedMetrics,
metricsBalanceInterestMetrics,
metricsTradesTotal,
metricsTradingVolume,
metricsLastUpdateTimeBalance,
metricsLastUpdateTimeMetrics,
)
}

View File

@ -27,7 +27,9 @@ var quantityReduceDelta = fixedpoint.NewFromFloat(0.005)
// This is for the maximum retries
const submitOrderRetryLimit = 5
// BaseOrderExecutor provides the common accessors for order executor
type BaseOrderExecutor struct {
exchange types.Exchange
session *ExchangeSession
activeMakerOrders *ActiveOrderBook
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
func (e *BaseOrderExecutor) GracefulCancel(ctx context.Context, orders ...types.Order) error {
if err := e.activeMakerOrders.GracefulCancel(ctx, e.session.Exchange, orders...); err != nil {
return errors.Wrap(err, "graceful cancel error")
if err := e.activeMakerOrders.GracefulCancel(ctx, e.exchange, orders...); err != nil {
return errors.Wrap(err, "graceful cancel order error")
}
return nil
@ -84,6 +86,7 @@ func NewGeneralOrderExecutor(
executor := &GeneralOrderExecutor{
BaseOrderExecutor: BaseOrderExecutor{
session: session,
exchange: session.Exchange,
activeMakerOrders: NewActiveOrderBook(symbol),
orderStore: orderStore,
},
@ -111,7 +114,7 @@ func (e *GeneralOrderExecutor) SetMaxRetries(maxRetries uint) {
}
func (e *GeneralOrderExecutor) startMarginAssetUpdater(ctx context.Context) {
marginService, ok := e.session.Exchange.(types.MarginBorrowRepayService)
marginService, ok := e.exchange.(types.MarginBorrowRepayService)
if !ok {
log.Warnf("session %s (%T) exchange does not support MarginBorrowRepayService", e.session.Name, e.session.Exchange)
return

View File

@ -22,6 +22,7 @@ func NewSimpleOrderExecutor(session *ExchangeSession) *SimpleOrderExecutor {
return &SimpleOrderExecutor{
BaseOrderExecutor: BaseOrderExecutor{
session: session,
exchange: session.Exchange,
activeMakerOrders: NewActiveOrderBook(""),
orderStore: core.NewOrderStore(""),
},

View File

@ -12,12 +12,16 @@ type Quota struct {
Locked fixedpoint.Value
}
// Add adds the fund to the available quota
func (q *Quota) Add(fund fixedpoint.Value) {
q.mu.Lock()
q.Available = q.Available.Add(fund)
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 {
if fund.Compare(q.Available) > 0 {
return false
@ -31,12 +35,15 @@ func (q *Quota) Lock(fund fixedpoint.Value) bool {
return true
}
// Commit commits the locked fund
func (q *Quota) Commit() {
q.mu.Lock()
q.Locked = fixedpoint.Zero
q.mu.Unlock()
}
// Rollback rolls back the locked fund
// this will move the locked fund to the available quota
func (q *Quota) Rollback() {
q.mu.Lock()
q.Available = q.Available.Add(q.Locked)
@ -44,12 +51,21 @@ func (q *Quota) Rollback() {
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 {
mu sync.Mutex
BaseAsset Quota
QuoteAsset Quota
}
// Commit commits the transaction
func (m *QuotaTransaction) Commit() bool {
m.mu.Lock()
m.BaseAsset.Commit()
@ -58,6 +74,7 @@ func (m *QuotaTransaction) Commit() bool {
return true
}
// Rollback rolls back the transaction
func (m *QuotaTransaction) Rollback() bool {
m.mu.Lock()
m.BaseAsset.Rollback()

View File

@ -54,11 +54,11 @@ type ExchangeSession struct {
PublicOnly bool `json:"publicOnly,omitempty" yaml:"publicOnly"`
// 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"`
// 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"`
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)
}
session.logger.Infof("environment config: %+v", environ.environmentConfig)
disableMarketDataStore := environ.environmentConfig != nil && environ.environmentConfig.DisableMarketDataStore
disableSessionTradeBuffer := environ.environmentConfig != nil && environ.environmentConfig.DisableSessionTradeBuffer
maxSessionTradeBufferSize := 0
@ -464,7 +466,7 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
for _, sub := range session.Subscriptions {
switch sub.Channel {
case types.BookChannel:
book := types.NewStreamBook(sub.Symbol)
book := types.NewStreamBook(sub.Symbol, session.ExchangeName)
book.BindStream(session.MarketDataStream)
session.orderBooks[sub.Symbol] = book
@ -886,32 +888,42 @@ func (session *ExchangeSession) InitExchange(name string, ex types.Exchange) err
return nil
}
func (session *ExchangeSession) MarginType() string {
margin := "none"
func (session *ExchangeSession) MarginType() types.MarginType {
if session.Margin {
margin = "margin"
if session.IsolatedMargin {
margin = "isolated"
return types.MarginTypeIsolatedMargin
} else {
return types.MarginTypeCrossMargin
}
}
return margin
return types.MarginTypeSpot
}
func (session *ExchangeSession) metricsBalancesUpdater(balances types.BalanceMap) {
for currency, balance := range balances {
labels := prometheus.Labels{
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"symbol": session.IsolatedMarginSymbol,
"currency": currency,
}
metricsTotalBalances.With(labels).Set(balance.Total().Float64())
metricsLockedBalances.With(labels).Set(balance.Locked.Float64())
metricsAvailableBalances.With(labels).Set(balance.Available.Float64())
metricsLastUpdateTimeBalance.With(prometheus.Labels{
metricsBalanceNetMetrics.With(labels).Set(balance.Net().Float64())
metricsBalanceAvailableMetrics.With(labels).Set(balance.Available.Float64())
metricsBalanceLockedMetrics.With(labels).Set(balance.Locked.Float64())
// margin metrics
metricsBalanceDebtMetrics.With(labels).Set(balance.Debt().Float64())
metricsBalanceBorrowedMetrics.With(labels).Set(balance.Borrowed.Float64())
metricsBalanceInterestMetrics.With(labels).Set(balance.Interest.Float64())
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"channel": "user",
"data_type": "balance",
"symbol": "",
@ -922,9 +934,10 @@ func (session *ExchangeSession) metricsBalancesUpdater(balances types.BalanceMap
}
func (session *ExchangeSession) metricsOrderUpdater(order types.Order) {
metricsLastUpdateTimeBalance.With(prometheus.Labels{
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"channel": "user",
"data_type": "order",
"symbol": order.Symbol,
@ -934,17 +947,19 @@ func (session *ExchangeSession) metricsOrderUpdater(order types.Order) {
func (session *ExchangeSession) metricsTradeUpdater(trade types.Trade) {
labels := prometheus.Labels{
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"side": trade.Side.String(),
"symbol": trade.Symbol,
"liquidity": trade.Liquidity(),
}
metricsTradingVolume.With(labels).Add(trade.Quantity.Mul(trade.Price).Float64())
metricsTradesTotal.With(labels).Inc()
metricsLastUpdateTimeBalance.With(prometheus.Labels{
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"channel": "user",
"data_type": "trade",
"symbol": trade.Symbol,
@ -954,9 +969,10 @@ func (session *ExchangeSession) metricsTradeUpdater(trade types.Trade) {
func (session *ExchangeSession) bindMarketDataStreamMetrics(stream types.Stream) {
stream.OnBookUpdate(func(book types.SliceOrderBook) {
metricsLastUpdateTimeBalance.With(prometheus.Labels{
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"channel": "market",
"data_type": "book",
"symbol": book.Symbol,
@ -964,9 +980,10 @@ func (session *ExchangeSession) bindMarketDataStreamMetrics(stream types.Stream)
}).SetToCurrentTime()
})
stream.OnKLineClosed(func(kline types.KLine) {
metricsLastUpdateTimeBalance.With(prometheus.Labels{
metricsLastUpdateTimeMetrics.With(prometheus.Labels{
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"channel": "market",
"data_type": "kline",
"symbol": kline.Symbol,
@ -983,16 +1000,18 @@ func (session *ExchangeSession) bindUserDataStreamMetrics(stream types.Stream) {
stream.OnDisconnect(func() {
metricsConnectionStatus.With(prometheus.Labels{
"channel": "user",
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"symbol": session.IsolatedMarginSymbol,
}).Set(0.0)
})
stream.OnConnect(func() {
metricsConnectionStatus.With(prometheus.Labels{
"channel": "user",
"session": session.Name,
"exchange": session.ExchangeName.String(),
"margin": session.MarginType(),
"margin_type": string(session.MarginType()),
"symbol": session.IsolatedMarginSymbol,
}).Set(1.0)
})

View File

@ -16,8 +16,18 @@ import (
)
// Strategy method calls:
// -> Initialize() (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)
// -> Run() (optional method)
// -> Shutdown(shutdownCtx context.Context, wg *sync.WaitGroup)
@ -112,6 +122,12 @@ func (trader *Trader) DisableLogging() {
}
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 {
trader.SetRiskControls(userConfig.RiskControls)
}
@ -171,12 +187,6 @@ func (trader *Trader) SetRiskControls(riskControls *RiskControls) {
func (trader *Trader) RunSingleExchangeStrategy(
ctx context.Context, strategy SingleExchangeStrategy, session *ExchangeSession, orderExecutor OrderExecutor,
) 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 {
trader.gracefulShutdown.OnShutdown(shutdown.Shutdown)
}
@ -238,12 +248,6 @@ func (trader *Trader) injectFieldsAndSubscribe(ctx context.Context) error {
return err
}
if defaulter, ok := strategy.(StrategyDefaulter); ok {
if err := defaulter.Defaults(); err != nil {
panic(err)
}
}
if subscriber, ok := strategy.(ExchangeSessionSubscriber); ok {
subscriber.Subscribe(session)
} 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 {
subscriber.CrossSubscribe(trader.environment.sessions)
} else {
@ -356,8 +354,23 @@ func (trader *Trader) Run(ctx context.Context) error {
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 {
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 {
return initializer.Initialize()
}

View File

@ -12,12 +12,6 @@ import (
"github.com/fatih/color"
"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"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@ -26,13 +20,18 @@ import (
"github.com/c9s/bbgo/pkg/accounting/pnl"
"github.com/c9s/bbgo/pkg/backtest"
"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/fixedpoint"
"github.com/c9s/bbgo/pkg/service"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/bbgo/pkg/util"
)
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-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")
@ -77,6 +76,11 @@ var BacktestCmd = &cobra.Command{
return err
}
modeCsv, err := cmd.Flags().GetBool("csv")
if err != nil {
return err
}
wantSync, err := cmd.Flags().GetBool("sync")
if err != nil {
return err
@ -156,6 +160,21 @@ var BacktestCmd = &cobra.Command{
log.Infof("starting backtest with startTime %s", startTime.Format(time.RFC3339))
environ := bbgo.NewEnvironment()
if userConfig.Backtest.CsvSource == nil {
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
}
@ -163,8 +182,7 @@ var BacktestCmd = &cobra.Command{
if environ.DatabaseService == nil {
return errors.New("database service is not enabled, please check your environment variables DB_DRIVER and DB_DSN")
}
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB}
}
environ.BacktestService = backtestService
bbgo.SetBackTesting(backtestService)
@ -692,7 +710,7 @@ func createSymbolReport(
}
func verify(
userConfig *bbgo.Config, backtestService *service.BacktestService,
userConfig *bbgo.Config, backtestService service.BackTestable,
sourceExchanges map[types.ExchangeName]types.Exchange, startTime, endTime time.Time,
) error {
for _, sourceExchange := range sourceExchanges {
@ -735,7 +753,7 @@ func getExchangeIntervals(ex types.Exchange) types.IntervalMap {
}
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,
) error {
for _, symbol := range userConfig.Backtest.Symbols {
@ -750,13 +768,11 @@ func sync(
var intervals = supportIntervals.Slice()
intervals.Sort()
for _, interval := range intervals {
if err := backtestService.Sync(ctx, sourceExchange, symbol, interval, syncFrom, syncTo); err != nil {
if err := backtestService.Sync(ctx, sourceExchange, symbol, intervals, syncFrom, syncTo); err != nil {
return err
}
}
}
}
return nil
}

221
pkg/cmd/execute_order.go Normal file
View 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
},
}

View File

@ -54,7 +54,7 @@ var orderbookCmd = &cobra.Command{
return fmt.Errorf("session %s not found", sessionName)
}
orderBook := types.NewMutexOrderBook(symbol)
orderBook := types.NewMutexOrderBook(symbol, session.Exchange.Name())
s := session.Exchange.NewStream()
s.SetPublicOnly()

View File

@ -3,19 +3,14 @@ package cmd
import (
"context"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/fixedpoint"
"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
var submitOrderCmd = &cobra.Command{
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().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(getOrderCmd)
RootCmd.AddCommand(submitOrderCmd)
RootCmd.AddCommand(executeOrderCmd)
}

View File

@ -135,7 +135,8 @@ var PnLCmd = &cobra.Command{
// we need the backtest klines for the daily prices
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
}
}

View 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
}

View 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
}

View 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)
})
}
}

View 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
}
}
}

View 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
}

View 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)
}

View 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))
}

View 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
}

View 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")
}

View 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
}

View 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")
}

View 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
1 1.70027E+12 36613.91 36613.92 36388.12 36400.01 1005.75727 1.70027E+12 36712312.49831390 35985 440.81212 16088534.76584510 0
2 1.70027E+12 36400.01 36456.53 36377.88 36405.46 507.51514 1.70027E+12 18486014.80771630 25492 236.98883 8631509.902 0
3 1.70027E+12 36405.47 36447.75 36390.44 36408.09 341.53256 1.70028E+12 12438100.14362490 19727 163.41072 5951076.256 0
4 1.70028E+12 36408.1 36424.01 36360.22 36371.81 444.73045 1.70028E+12 16180477.67130620 25089 207.88416 7562956.27 0
5 1.70028E+12 36371.8 36426.51 36369.45 36369.45 378.50007 1.70028E+12 13775839.75999540 20728 197.99667 7205858.497 0
6 1.70028E+12 36369.45 36378.65 36303.98 36334 629.09574 1.70029E+12 22862757.37912180 33883 269.3097 9787132.913 0
7 1.70029E+12 36334.01 36361.1 36250.01 36252 615.52755 1.70029E+12 22350527.58640450 30392 238.90543 8675479.987 0
8 1.70029E+12 36251.99 36428 36178.58 36417.16 1191.24433 1.70029E+12 43265058.27238300 41466 628.67499 22834722.23764540 0
9 1.70029E+12 36417.15 36479.22 36375.28 36448.01 600.66262 1.7003E+12 21883116.44525150 29227 301.84047 10996141.34025750 0
10 1.7003E+12 36448 36453.09 36392 36397.45 398.07607 1.7003E+12 14499345.43090060 22193 159.60456 5813290.376 0
11 1.7003E+12 36397.44 36486.48 36397.44 36472.46 601.46574 1.70031E+12 21917527.53081410 24881 354.42545 12916946.80705190 0
12 1.70031E+12 36472.46 36538.61 36400 36402.8 549.76216 1.70031E+12 20053594.27145890 29706 248.4342 9062453.31 0
13 1.70031E+12 36402.79 36484.31 36393.44 36449.3 513.24545 1.70031E+12 18705069.10380380 26631 244.2024 8898609.715 0
14 1.70031E+12 36449.3 36483.13 36347.69 36430.21 887.7206 1.70032E+12 32327899.78688460 41973 391.19851 14246544.95513180 0
15 1.70032E+12 36430.21 36568.76 36421.1 36507.03 803.12819 1.70032E+12 29307346.75876810 36941 447.83113 16341815.04367800 0
16 1.70032E+12 36507.04 36682.2 36505.14 36664.16 1440.91018 1.70032E+12 52738306.52534310 50174 755.06676 27635771.88129150 0
17 1.70032E+12 36664.17 36845.49 36639.96 36674 1669.58835 1.70033E+12 61326939.61543430 61313 823.2455 30239331.84409360 0
18 1.70033E+12 36674 36701.76 36600.1 36620 933.50168 1.70033E+12 34203500.13911480 39514 402.07999 14731130.96536590 0
19 1.70033E+12 36620.01 36745.5 36611.47 36707.19 583.0753 1.70033E+12 21373512.11709470 29144 289.6688 10617175.15516390 0
20 1.70033E+12 36707.18 36768 36653.51 36679.1 598.67548 1.70034E+12 21974467.38315690 31067 277.3739 10180364.08398050 0
21 1.70034E+12 36679.09 36707.16 36536.08 36598 779.88183 1.70034E+12 28546655.20047820 43054 314.94592 11526766.07883920 0
22 1.70034E+12 36597.99 36611.78 36524.31 36542.01 581.49791 1.70034E+12 21265094.72761260 36319 230.4279 8425965.352 0
23 1.70034E+12 36542.01 36556.56 36443.1 36506 543.12589 1.70035E+12 19821336.79152930 27472 245.46068 8956420.024 0
24 1.70035E+12 36506 36571.33 36498.34 36568.1 504.0213 1.70035E+12 18419269.57915190 22127 231.75877 8469107.669 0

View 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
1 11782578 6.00000000 1.00000000 14974844 14974844 1698623884463 True True
2 11782579 6.00000000 1.00000000 14974845 14974845 1698623884666 True True
3 11782580 6.00000000 1.00000000 14974846 14974846 1698623893793 True True
4 11782581 6.00000000 5.00000000 14974847 14974847 1698623920955 True True
5 11782582 6.00000000 10.50000000 14974848 14974848 1698623939783 False True

View 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
1 timestamp symbol side size price tickDirection trdMatchID grossValue homeNotional foreignNotional
2 1696982287.4922 FXSUSDT Sell 0.86 5.2390 ZeroMinusTick f7496ecb-b174-51b9-ba56-150186ba6c27 4.50554e+08 0.86 4.50554
3 1696982322.0561 FXSUSDT Buy 0.13 5.2395 PlusTick 2089f1f4-d890-5762-a652-49a743fab436 6.81135e+07 0.13 0.6811349999999999
4 1696982333.0308 FXSUSDT Buy 48.9 5.2420 PlusTick 8e7d405a-0003-5aa1-972d-46b08fe520c0 2.563338e+10 48.9 256.3338
5 1696982333.0377 FXSUSDT Buy 0.77 5.2425 PlusTick 9f250e94-da5b-5a94-9126-084e46c9c692 4.0367249999999994e+08 0.77 4.036725
6 1696982359.7441 FXSUSDT Buy 0.12 5.2450 PlusTick 08a0c666-da06-53f6-8eec-9d3462582b4f 6.293999999999999e+07 0.12 0.6294
7 1696982359.7441 FXSUSDT Buy 0.19 5.2450 ZeroMinusTick 8a61753b-2a8e-5881-8e9b-9ad66806ee23 9.9655e+07 0.19 0.99655
8 1696982359.7443 FXSUSDT Buy 12.12 5.2450 ZeroMinusTick 34b2f272-2f68-5d0a-a4ad-6c02f5342ca1 6.356939999999999e+09 12.12 63.569399999999995
9 1696982359.7443 FXSUSDT Buy 2.19 5.2450 ZeroMinusTick 0cae9717-0fe1-51dd-bb10-1a61d5e83d98 1.148655e+09 2.19 11.48655
10 1696982359.7449 FXSUSDT Buy 35.66 5.2450 ZeroMinusTick 0a5f0734-af3a-5439-9f17-d98ce7ea4f24 1.870367e+10 35.66 187.0367
11 1696982359.7512 FXSUSDT Buy 10.97 5.2450 ZeroMinusTick d8529e38-d3f5-5a7a-97f7-55cf3335de77 5.753765000000001e+09 10.97 57.537650000000006
12 1696982359.7512 FXSUSDT Buy 22.97 5.2450 ZeroMinusTick 44361b86-78e1-533b-a3d0-7dd12d538992 1.2047765e+10 22.97 120.47765
13 1696982369.5962 FXSUSDT Buy 0.05 5.2470 PlusTick 6800a047-b6e5-520a-9817-eb4d463a3cce 2.6235000000000004e+07 0.05 0.26235
14 1696982389.6288 FXSUSDT Buy 0.02 5.2495 PlusTick a4bc238f-3e6a-58a3-a012-1342563c2ced 1.0499000000000002e+07 0.02 0.10499000000000001
15 1696982389.6288 FXSUSDT Buy 6.06 5.2495 ZeroMinusTick eb27200e-c34e-537a-a0a0-4636dab66f07 3.181197e+09 6.06 31.81197
16 1696982389.6297 FXSUSDT Buy 6.04 5.2495 ZeroMinusTick c6badf81-05c5-5b35-b932-3c71941340fb 3.170698e+09 6.04 31.70698

View 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
1 trade_id/���id side/���׷��� size/���� price/�۸� created_time/�ɽ�ʱ��
2 450372093 buy 0.00418025 35910.6 1700239042832
3 450372094 buy 0.0104 35911.9 1700239043163
4 450372860 buy 0.17316796 35911.9 1700239133047
5 450372095 buy 0.2227 35912.3 1700239043283
6 450372874 buy 0.63393 35913.4 1700239135563
7 450372876 buy 0.01751154 35913.6 1700239135563
8 450372096 buy 0.0478082 35913.7 1700239043339
9 450372877 buy 0.00030629 35913.7 1700239135563
10 450372878 buy 0.00030629 35913.8 1700239135563
11 450372880 buy 0.32111425 35913.9 1700239135563
12 450372881 buy 0.00027844 35914.0 1700239135563
13 450372882 buy 0.00058473 35914.2 1700239135563
14 450372032 buy 0.00132007 35914.3 1700239040621
15 450372883 buy 0.00058473 35914.3 1700239135563
16 450372884 buy 0.00052904 35914.4 1700239135563

View 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()
}

View 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)
}
}

View 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
}

View 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
}

View File

@ -2,9 +2,10 @@ package binance
import (
"fmt"
"github.com/c9s/bbgo/pkg/exchange/binance/binanceapi"
"time"
"github.com/c9s/bbgo/pkg/exchange/binance/binanceapi"
"github.com/adshao/go-binance/v2/futures"
"github.com/pkg/errors"
@ -44,7 +45,6 @@ func toGlobalFuturesPositions(futuresPositions []*binanceapi.FuturesAccountPosit
retFuturesPositions[futuresPosition.Symbol] = types.FuturesPosition{ // TODO: types.FuturesPosition
Isolated: futuresPosition.Isolated,
AverageCost: fixedpoint.MustNewFromString(futuresPosition.EntryPrice),
ApproximateAverageCost: fixedpoint.MustNewFromString(futuresPosition.EntryPrice),
Base: fixedpoint.MustNewFromString(futuresPosition.PositionAmt),
Quote: fixedpoint.MustNewFromString(futuresPosition.Notional),

View File

@ -796,13 +796,24 @@ func (e *Exchange) QueryOrder(ctx context.Context, q types.OrderQuery) (*types.O
return nil, err
}
var order *binance.Order
if e.IsMargin {
order, err = e.client.NewGetMarginOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
} else {
order, err = e.client.NewGetOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
order, err := e.client.NewGetMarginOrderService().Symbol(q.Symbol).OrderID(orderID).Do(ctx)
if err != nil {
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 {
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 {
// TODO: check the TimeInForce value
req.TimeInForce(binance.TimeInForceType(order.TimeInForce))

View File

@ -115,10 +115,10 @@ func convertSubscriptions(ss []types.Subscription) ([]WebSocketCommand, error) {
switch s.Channel {
case types.BookChannel:
// see https://docs.kucoin.com/#level-2-market-data
subscribeTopic = "/market/level2" + ":" + toLocalSymbol(s.Symbol)
subscribeTopic = "/market/level2" + ":" + ToLocalSymbol(s.Symbol)
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:
return nil, fmt.Errorf("websocket channel %s is not supported by kucoin", s.Channel)

View File

@ -165,7 +165,7 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type
}
req := e.client.MarketDataService.NewGetKLinesRequest()
req.Symbol(toLocalSymbol(symbol))
req.Symbol(ToLocalSymbol(symbol))
req.Interval(toLocalInterval(interval))
if options.StartTime != nil {
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) {
req := e.client.TradeService.NewPlaceOrderRequest()
req.Symbol(toLocalSymbol(order.Symbol))
req.Symbol(ToLocalSymbol(order.Symbol))
req.Side(toLocalSide(order.Side))
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) {
req := e.client.TradeService.NewListOrdersRequest()
req.Symbol(toLocalSymbol(symbol))
req.Symbol(ToLocalSymbol(symbol))
req.Status("active")
orderList, err := req.Do(ctx)
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) {
req := e.client.TradeService.NewListOrdersRequest()
req.Symbol(toLocalSymbol(symbol))
req.Symbol(ToLocalSymbol(symbol))
req.Status("done")
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) {
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
// 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) {
orderBook, err := e.client.MarketDataService.GetOrderBook(toLocalSymbol(symbol), 100)
orderBook, err := e.client.MarketDataService.GetOrderBook(ToLocalSymbol(symbol), 100)
if err != nil {
return types.SliceOrderBook{}, 0, err
}

View File

@ -17,13 +17,13 @@ import (
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
package kucoin
var symbolMap = map[string]string{
var SymbolMap = map[string]string{
{{- range $k, $v := . }}
{{ printf "%q" $k }}: {{ printf "%q" $v }},
{{- end }}
}
func toLocalSymbol(symbol string) string {
func ToLocalSymbol(symbol string) string {
s, ok := symbolMap[symbol]
if ok {
return s

View File

@ -1,7 +1,7 @@
// Code generated by go generate; DO NOT EDIT.
package kucoin
var symbolMap = map[string]string{
var SymbolMap = map[string]string{
"1EARTHUSDT": "1EARTH-USDT",
"1INCHUSDT": "1INCH-USDT",
"2CRZBTC": "2CRZ-BTC",
@ -1107,8 +1107,8 @@ var symbolMap = map[string]string{
"ZRXETH": "ZRX-ETH",
}
func toLocalSymbol(symbol string) string {
s, ok := symbolMap[symbol]
func ToLocalSymbol(symbol string) string {
s, ok := SymbolMap[symbol]
if ok {
return s
}

View File

@ -247,29 +247,6 @@ func toGlobalTradeV3(t v3.Trade) ([]types.Trade, error) {
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 {
switch a {
@ -284,11 +261,21 @@ func toGlobalDepositStatus(a max.DepositState) types.DepositStatus {
case max.DepositStateAccepted:
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
// 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)
}

View File

@ -116,6 +116,11 @@ const (
DepositStateSuspended DepositState = "suspended"
DepositStateAccepted DepositState = "accepted"
DepositStateChecking DepositState = "checking"
// v3 states
DepositStateProcessing DepositState = "processing"
DepositStateFailed DepositState = "failed"
DepositStateDone DepositState = "done"
)
type Deposit struct {

View File

@ -136,6 +136,12 @@ func (c *CancelOrderRequest) GetSlugsMap() (map[string]string, error) {
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) {
params, err := c.GetParameters()
@ -144,7 +150,9 @@ func (c *CancelOrderRequest) Do(ctx context.Context) (*max.Order, error) {
}
query := url.Values{}
apiURL := "/api/v3/order"
var apiURL string
apiURL = c.GetPath()
req, err := c.client.NewAuthenticatedRequest(ctx, "DELETE", apiURL, query, params)
if err != nil {
@ -157,8 +165,32 @@ func (c *CancelOrderRequest) Do(ctx context.Context) (*max.Order, error) {
}
var apiResponse max.Order
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
}

View File

@ -6,11 +6,10 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/exchange/max/maxapi"
"net/url"
"reflect"
"regexp"
max "github.com/c9s/bbgo/pkg/exchange/max/maxapi"
)
func (c *CancelWalletOrderAllRequest) Side(side string) *CancelWalletOrderAllRequest {
@ -166,6 +165,12 @@ func (c *CancelWalletOrderAllRequest) GetSlugsMap() (map[string]string, error) {
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) {
params, err := c.GetParameters()
@ -174,7 +179,9 @@ func (c *CancelWalletOrderAllRequest) Do(ctx context.Context) ([]OrderCancelResp
}
query := url.Values{}
apiURL := "/api/v3/wallet/:walletType/orders"
var apiURL string
apiURL = c.GetPath()
slugs, err := c.GetSlugsMap()
if err != nil {
return nil, err
@ -193,8 +200,32 @@ func (c *CancelWalletOrderAllRequest) Do(ctx context.Context) ([]OrderCancelResp
}
var apiResponse []OrderCancelResponse
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
}

View File

@ -236,6 +236,12 @@ func (c *CreateWalletOrderRequest) GetSlugsMap() (map[string]string, error) {
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) {
params, err := c.GetParameters()
@ -244,7 +250,9 @@ func (c *CreateWalletOrderRequest) Do(ctx context.Context) (*max.Order, error) {
}
query := url.Values{}
apiURL := "/api/v3/wallet/:walletType/order"
var apiURL string
apiURL = c.GetPath()
slugs, err := c.GetSlugsMap()
if err != nil {
return nil, err
@ -263,8 +271,32 @@ func (c *CreateWalletOrderRequest) Do(ctx context.Context) (*max.Order, error) {
}
var apiResponse max.Order
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
}

View File

@ -109,13 +109,21 @@ func (g *GetMarginADRatioRequest) GetSlugsMap() (map[string]string, error) {
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) {
// no body params
var params interface{}
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)
if err != nil {
@ -128,8 +136,32 @@ func (g *GetMarginADRatioRequest) Do(ctx context.Context) (*ADRatio, error) {
}
var apiResponse ADRatio
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
}

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