Merge branch 'main' into profit-report-parameter

This commit is contained in:
Andy Cheng 2023-06-15 17:28:02 +08:00 committed by GitHub
commit 6b46b1e01e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
345 changed files with 10378 additions and 2393 deletions

55
config/scmaker.yaml Normal file
View File

@ -0,0 +1,55 @@
sessions:
binance:
exchange: max
envVarPrefix: max
makerFeeRate: 0%
takerFeeRate: 0.025%
exchangeStrategies:
- on: max
scmaker:
symbol: USDCUSDT
## adjustmentUpdateInterval is the interval for adjusting position
adjustmentUpdateInterval: 1m
## liquidityUpdateInterval is the interval for updating liquidity orders
liquidityUpdateInterval: 1h
midPriceEMA:
interval: 1h
window: 99
## priceRangeBollinger is used for the liquidity price range
priceRangeBollinger:
interval: 1h
window: 10
k: 1.0
numOfLiquidityLayers: 10
liquidityLayerTickSize: 0.0001
strengthInterval: 1m
minProfit: 0.01%
liquidityScale:
exp:
domain: [0, 9]
range: [1, 4]
backtest:
sessions:
- max
startTime: "2023-05-20"
endTime: "2023-06-01"
symbols:
- USDCUSDT
account:
max:
makerFeeRate: 0.0%
takerFeeRate: 0.025%
balances:
USDC: 5000
USDT: 5000

View File

@ -90,7 +90,7 @@ exchangeStrategies:
closePosition: 100% closePosition: 100%
- higherHighLowerLowStopLoss: - higherHighLowerLowStopLoss:
# interval is the kline interval used by this exit # interval is the kline interval used by this exit
interval: 15 interval: 15m
# window is used as the range to determining higher highs and lower lows # window is used as the range to determining higher highs and lower lows
window: 5 window: 5
# highLowWindow is the range to calculate the number of higher highs and lower lows # highLowWindow is the range to calculate the number of higher highs and lower lows

44
config/xalign.yaml Normal file
View File

@ -0,0 +1,44 @@
---
notifications:
slack:
defaultChannel: "dev-bbgo"
errorChannel: "bbgo-error"
switches:
trade: true
orderUpdate: true
submitOrder: true
sessions:
max:
exchange: max
envVarPrefix: max
binance:
exchange: binance
envVarPrefix: binance
persistence:
json:
directory: var/data
redis:
host: 127.0.0.1
port: 6379
db: 0
crossExchangeStrategies:
- xalign:
interval: 1m
sessions:
- max
- binance
## quoteCurrencies config specifies which quote currency should be used for BUY order or SELL order.
## when specifying [USDC,TWD] for "BUY", then it will consider BTCUSDT first then BTCTWD second.
quoteCurrencies:
buy: [USDC, TWD]
sell: [USDT]
expectedBalances:
BTC: 0.0440

View File

@ -60,4 +60,4 @@ bbgo [flags]
* [bbgo userdatastream](bbgo_userdatastream.md) - Listen to session events (orderUpdate, tradeUpdate, balanceUpdate, balanceSnapshot) * [bbgo userdatastream](bbgo_userdatastream.md) - Listen to session events (orderUpdate, tradeUpdate, balanceUpdate, balanceSnapshot)
* [bbgo version](bbgo_version.md) - show version name * [bbgo version](bbgo_version.md) - show version name
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -43,4 +43,4 @@ bbgo account [--session SESSION] [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -52,4 +52,4 @@ bbgo backtest [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -42,4 +42,4 @@ bbgo balances [--session SESSION] [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -41,4 +41,4 @@ bbgo build [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -51,4 +51,4 @@ bbgo cancel-order [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -43,4 +43,4 @@ bbgo deposits [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -50,4 +50,4 @@ bbgo execute-order --session SESSION --symbol SYMBOL --side SIDE --target-quanti
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -44,4 +44,4 @@ bbgo get-order --session SESSION --order-id ORDER_ID [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -47,4 +47,4 @@ bbgo hoptimize [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -44,4 +44,4 @@ bbgo kline [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -43,4 +43,4 @@ bbgo list-orders open|closed --session SESSION --symbol SYMBOL [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -40,4 +40,4 @@ margin related history
* [bbgo margin loans](bbgo_margin_loans.md) - query loans history * [bbgo margin loans](bbgo_margin_loans.md) - query loans history
* [bbgo margin repays](bbgo_margin_repays.md) - query repay history * [bbgo margin repays](bbgo_margin_repays.md) - query repay history
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -43,4 +43,4 @@ bbgo margin interests --session=SESSION_NAME --asset=ASSET [flags]
* [bbgo margin](bbgo_margin.md) - margin related history * [bbgo margin](bbgo_margin.md) - margin related history
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -43,4 +43,4 @@ bbgo margin loans --session=SESSION_NAME --asset=ASSET [flags]
* [bbgo margin](bbgo_margin.md) - margin related history * [bbgo margin](bbgo_margin.md) - margin related history
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -43,4 +43,4 @@ bbgo margin repays --session=SESSION_NAME --asset=ASSET [flags]
* [bbgo margin](bbgo_margin.md) - margin related history * [bbgo margin](bbgo_margin.md) - margin related history
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -42,4 +42,4 @@ bbgo market [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -46,4 +46,4 @@ bbgo optimize [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -44,4 +44,4 @@ bbgo orderbook --session=[exchange_name] --symbol=[pair_name] [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -42,4 +42,4 @@ bbgo orderupdate [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -51,4 +51,4 @@ bbgo pnl [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -53,4 +53,4 @@ bbgo run [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -48,4 +48,4 @@ bbgo submit-order --session SESSION --symbol SYMBOL --side SIDE --quantity QUANT
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -44,4 +44,4 @@ bbgo sync [--session=[exchange_name]] [--symbol=[pair_name]] [[--since=yyyy/mm/d
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -44,4 +44,4 @@ bbgo trades --session=[exchange_name] --symbol=[pair_name] [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -42,4 +42,4 @@ bbgo tradeupdate --session=[exchange_name] [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -44,4 +44,4 @@ bbgo transfer-history [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -42,4 +42,4 @@ bbgo userdatastream [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

View File

@ -41,4 +41,4 @@ bbgo version [flags]
* [bbgo](bbgo.md) - bbgo is a crypto trading bot * [bbgo](bbgo.md) - bbgo is a crypto trading bot
###### Auto generated by spf13/cobra on 19-Apr-2023 ###### Auto generated by spf13/cobra on 15-Jun-2023

11
doc/release/v1.47.0.md Normal file
View File

@ -0,0 +1,11 @@
[Full Changelog](https://github.com/c9s/bbgo/compare/v1.46.0...main)
- [#1165](https://github.com/c9s/bbgo/pull/1165): FIX: [supertrend] fix config type error
- [#1164](https://github.com/c9s/bbgo/pull/1164): FIX: [grid2] apply defensive programming on the order quantity
- [#1162](https://github.com/c9s/bbgo/pull/1162): FIX: [grid2] emit grid profit after profit stats fix
- [#1158](https://github.com/c9s/bbgo/pull/1158): IMPROVE: types: improve order struct json size
- [#1161](https://github.com/c9s/bbgo/pull/1161): FIX: [grid2] add initialOrderID check and try to fix profitStats.Since
- [#1160](https://github.com/c9s/bbgo/pull/1160): FIX: [grid2] add profit fixer and options
- [#1159](https://github.com/c9s/bbgo/pull/1159): FIX: avoid global persistenceServiceFacade concurrent write
- [#1157](https://github.com/c9s/bbgo/pull/1157): FIX: add context to LoadState
- [#1155](https://github.com/c9s/bbgo/pull/1155): FIX: [supertrend] adding opposite position amount to the order amount

39
doc/release/v1.48.0.md Normal file
View File

@ -0,0 +1,39 @@
## New Strategies
- xalign
- scmaker
## Features
- v2 indicator apis
[Full Changelog](https://github.com/c9s/bbgo/compare/v1.47.0...main)
- [#1197](https://github.com/c9s/bbgo/pull/1197): FEATURE: [strategy] add stable coin market maker
- [#1174](https://github.com/c9s/bbgo/pull/1174): FEATURE: [grid2] recover with twin orders
- [#1194](https://github.com/c9s/bbgo/pull/1194): IMPROVE: improve hhllstop message
- [#1196](https://github.com/c9s/bbgo/pull/1196): FIX: [xalign] add DryRun and fix quote amount calculation
- [#1195](https://github.com/c9s/bbgo/pull/1195): FEATURE: [strategy] add xalign strategy
- [#1192](https://github.com/c9s/bbgo/pull/1192): IMPROVE: improve order executor error checking, trailing stop and indicators
- [#1193](https://github.com/c9s/bbgo/pull/1193): FIX: [schedule] fix quantity truncation add minBaseBalance config
- [#1188](https://github.com/c9s/bbgo/pull/1188): FEATURE: [indicator] add multiply operator
- [#1185](https://github.com/c9s/bbgo/pull/1185): FIX: [autoborrow] add max borrowable check and add more notifications
- [#1189](https://github.com/c9s/bbgo/pull/1189): FEATURE: [indicator] add v2 stddev indicator
- [#1190](https://github.com/c9s/bbgo/pull/1190): FEATURE: [indicator] add v2 pivot low indicator
- [#1187](https://github.com/c9s/bbgo/pull/1187): FEATURE: [indicator] add stoch v2
- [#1186](https://github.com/c9s/bbgo/pull/1186): FEATURE: [indicator] add v2 CMA indicator
- [#1184](https://github.com/c9s/bbgo/pull/1184): FEATURE: [indicator] add v2 MACD, SMA
- [#1183](https://github.com/c9s/bbgo/pull/1183): REFACTOR: [indicator] replace all Index(i) callers
- [#1182](https://github.com/c9s/bbgo/pull/1182): REFACTOR: add parameter index to the Last method
- [#1181](https://github.com/c9s/bbgo/pull/1181): FEATURE: [indicator] add new ATR, RMA indicators with the new API design
- [#1179](https://github.com/c9s/bbgo/pull/1179): FEATURE: new indicator API design
- [#1180](https://github.com/c9s/bbgo/pull/1180): FIX: [grid2] fix base quote investment check
- [#1176](https://github.com/c9s/bbgo/pull/1176): FIX: [grid2] fix base + quote calculation and add baseGridNumber config field
- [#1177](https://github.com/c9s/bbgo/pull/1177): FIX: some types and operations in SeriesExtended are not supported
- [#1172](https://github.com/c9s/bbgo/pull/1172): FEATURE: [grid2] truncate base quantity for quote+base mode
- [#1171](https://github.com/c9s/bbgo/pull/1171): FEATURE: Add types.StrInt64 for decoding integer in JSON string format
- [#1169](https://github.com/c9s/bbgo/pull/1169): WIP: FEATURE: add bitget exchange support
- [#1156](https://github.com/c9s/bbgo/pull/1156): REFACTOR: pull out Fast* methods to FastOrderExecutor
- [#1166](https://github.com/c9s/bbgo/pull/1166): FIX: [max] replace deprecated max v3 API
- [#1167](https://github.com/c9s/bbgo/pull/1167): FEATURE: support binance futures trading data

10
go.mod
View File

@ -2,12 +2,12 @@
module github.com/c9s/bbgo module github.com/c9s/bbgo
go 1.17 go 1.18
require ( require (
github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/Masterminds/squirrel v1.5.3 github.com/Masterminds/squirrel v1.5.3
github.com/adshao/go-binance/v2 v2.4.1 github.com/adshao/go-binance/v2 v2.4.2
github.com/c-bata/goptuna v0.8.1 github.com/c-bata/goptuna v0.8.1
github.com/c9s/requestgen v1.3.4 github.com/c9s/requestgen v1.3.4
github.com/c9s/rockhopper v1.2.2-0.20220617053729-ffdc87df194b github.com/c9s/rockhopper v1.2.2-0.20220617053729-ffdc87df194b
@ -47,7 +47,7 @@ require (
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.7.4 github.com/stretchr/testify v1.8.1
github.com/valyala/fastjson v1.5.1 github.com/valyala/fastjson v1.5.1
github.com/wcharczuk/go-chart/v2 v2.1.0 github.com/wcharczuk/go-chart/v2 v2.1.0
github.com/webview/webview v0.0.0-20210216142346-e0bfdf0e5d90 github.com/webview/webview v0.0.0-20210216142346-e0bfdf0e5d90
@ -67,7 +67,7 @@ require (
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/VividCortex/ewma v1.1.1 // indirect github.com/VividCortex/ewma v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect github.com/bitly/go-simplejson v0.5.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
@ -78,6 +78,7 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/denisenkom/go-mssqldb v0.12.2 // indirect github.com/denisenkom/go-mssqldb v0.12.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dmarkham/enumer v1.5.8 // indirect
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
@ -110,6 +111,7 @@ require (
github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pascaldekloe/name v1.0.0 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect

12
go.sum
View File

@ -51,6 +51,8 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/adshao/go-binance/v2 v2.4.1 h1:fOZ2tCbN7sgDZvvsawUMjhsOoe40X87JVE4DklIyyyc= github.com/adshao/go-binance/v2 v2.4.1 h1:fOZ2tCbN7sgDZvvsawUMjhsOoe40X87JVE4DklIyyyc=
github.com/adshao/go-binance/v2 v2.4.1/go.mod h1:6Qoh+CYcj8U43h4HgT6mqJnsGj4mWZKA/nsj8LN8ZTU= github.com/adshao/go-binance/v2 v2.4.1/go.mod h1:6Qoh+CYcj8U43h4HgT6mqJnsGj4mWZKA/nsj8LN8ZTU=
github.com/adshao/go-binance/v2 v2.4.2 h1:NBNMUyXrci45v3sr0RkZosiBYSw1/yuqCrJNkyEM8U0=
github.com/adshao/go-binance/v2 v2.4.2/go.mod h1:41Up2dG4NfMXpCldrDPETEtiOq+pHoGsFZ73xGgaumo=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -72,6 +74,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
@ -145,6 +149,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dmarkham/enumer v1.5.8 h1:fIF11F9l5jyD++YYvxcSH5WgHfeaSGPaN/T4kOQ4qEM=
github.com/dmarkham/enumer v1.5.8/go.mod h1:d10o8R3t/gROm2p3BXqTkMt2+HMuxEmWCXzorAruYak=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -532,6 +538,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/name v1.0.0 h1:n7LKFgHixETzxpRv2R77YgPUFo85QHGZKrdaYm7eY5U=
github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
@ -650,6 +658,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -661,6 +670,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=

View File

@ -12,18 +12,18 @@ for each kline, the backtest engine:
There are 2 ways that a strategy could work with backtest engine: There are 2 ways that a strategy could work with backtest engine:
1. the strategy receives kline from the market data stream, and then it submits the order by the given market data to the backtest engine. 1. the strategy receives kline from the market data stream, and then it submits the order by the given market data to the backtest engine.
backtest engine receives the order and then pushes the trade and order updates to the user data stream. backtest engine receives the order and then pushes the trade and order updates to the user data stream.
the strategy receives the trade and update its position. the strategy receives the trade and update its position.
2. the strategy places the orders when it starts. (like grid) the strategy then receives the order updates and then submit a new order 2. the strategy places the orders when it starts. (like grid) the strategy then receives the order updates and then submit a new order
by its order update message. by its order update message.
We need to ensure that: We need to ensure that:
1. if the strategy submits the order from the market data stream, since it's a separate goroutine, the strategy should block the backtest engine 1. if the strategy submits the order from the market data stream, since it's a separate goroutine, the strategy should block the backtest engine
to process the trades before the next kline is published. to process the trades before the next kline is published.
*/ */
package backtest package backtest
@ -270,8 +270,8 @@ func (e *Exchange) QueryTicker(ctx context.Context, symbol string) (*types.Ticke
Open: kline.Open, Open: kline.Open,
High: kline.High, High: kline.High,
Low: kline.Low, Low: kline.Low,
Buy: kline.Close, Buy: kline.Close.Sub(matching.Market.TickSize),
Sell: kline.Close, Sell: kline.Close.Add(matching.Market.TickSize),
}, nil }, nil
} }

View File

@ -228,12 +228,16 @@ func (environ *Environment) ConfigureExchangeSessions(userConfig *Config) error
func (environ *Environment) AddExchangesByViperKeys() error { func (environ *Environment) AddExchangesByViperKeys() error {
for _, n := range types.SupportedExchanges { for _, n := range types.SupportedExchanges {
if viper.IsSet(string(n) + "-api-key") { if viper.IsSet(string(n) + "-api-key") {
ex, err := exchange.NewWithEnvVarPrefix(n, "") exMinimal, err := exchange.NewWithEnvVarPrefix(n, "")
if err != nil { if err != nil {
return err return err
} }
environ.AddExchange(n.String(), ex) if ex, ok := exMinimal.(types.Exchange); ok {
environ.AddExchange(n.String(), ex)
} else {
log.Errorf("exchange %T does not implement types.Exchange", exMinimal)
}
} }
} }

View File

@ -3,6 +3,7 @@ package bbgo
import ( import (
"context" "context"
"fmt" "fmt"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
@ -82,14 +83,14 @@ func (s *HigherHighLowerLowStop) updateActivated(position *types.Position, close
r := fixedpoint.One.Add(s.ActivationRatio) r := fixedpoint.One.Add(s.ActivationRatio)
if closePrice.Compare(position.AverageCost.Mul(r)) >= 0 { if closePrice.Compare(position.AverageCost.Mul(r)) >= 0 {
s.activated = true s.activated = true
Notify("[hhllStop] Stop of %s activated for long position, activation ratio %s:", s.Symbol, s.ActivationRatio.Percentage()) Notify("[hhllStop] %s stop is activated for long position, activation ratio %s:", s.Symbol, s.ActivationRatio.Percentage())
} }
} else if position.IsShort() { } else if position.IsShort() {
r := fixedpoint.One.Sub(s.ActivationRatio) r := fixedpoint.One.Sub(s.ActivationRatio)
// for short position, if the close price is less than the activation price then this is a profit position. // for short position, if the close price is less than the activation price then this is a profit position.
if closePrice.Compare(position.AverageCost.Mul(r)) <= 0 { if closePrice.Compare(position.AverageCost.Mul(r)) <= 0 {
s.activated = true s.activated = true
Notify("[hhllStop] Stop of %s activated for short position, activation ratio %s:", s.Symbol, s.ActivationRatio.Percentage()) Notify("[hhllStop] %s stop is activated for short position, activation ratio %s:", s.Symbol, s.ActivationRatio.Percentage())
} }
} }
} }
@ -99,15 +100,18 @@ func (s *HigherHighLowerLowStop) updateHighLowNumber(kline types.KLine) {
s.klines.Truncate(s.Window - 1) s.klines.Truncate(s.Window - 1)
if s.klines.Len() >= s.Window-1 { if s.klines.Len() >= s.Window-1 {
if s.klines.GetHigh().Compare(kline.GetHigh()) < 0 { high := kline.GetHigh()
low := kline.GetLow()
if s.klines.GetHigh().Compare(high) < 0 {
s.highLows = append(s.highLows, types.DirectionUp) s.highLows = append(s.highLows, types.DirectionUp)
log.Debugf("[hhllStop] new higher high for %s", s.Symbol) Notify("[hhllStop] detected %s new higher high %f", s.Symbol, high.Float64())
} else if s.klines.GetLow().Compare(kline.GetLow()) > 0 { } else if s.klines.GetLow().Compare(low) > 0 {
s.highLows = append(s.highLows, types.DirectionDown) s.highLows = append(s.highLows, types.DirectionDown)
log.Debugf("[hhllStop] new lower low for %s", s.Symbol) Notify("[hhllStop] detected %s new lower low %f", s.Symbol, low.Float64())
} else { } else {
s.highLows = append(s.highLows, types.DirectionNone) s.highLows = append(s.highLows, types.DirectionNone)
} }
// Truncate highLows // Truncate highLows
if len(s.highLows) > s.HighLowWindow { if len(s.highLows) > s.HighLowWindow {
end := len(s.highLows) end := len(s.highLows)
@ -118,6 +122,7 @@ func (s *HigherHighLowerLowStop) updateHighLowNumber(kline types.KLine) {
kn := s.highLows[start:] kn := s.highLows[start:]
s.highLows = kn s.highLows = kn
} }
} else { } else {
s.highLows = append(s.highLows, types.DirectionNone) s.highLows = append(s.highLows, types.DirectionNone)
} }
@ -183,13 +188,17 @@ func (s *HigherHighLowerLowStop) Bind(session *ExchangeSession, orderExecutor *G
// Close position & reset // Close position & reset
if s.shouldStop(position) { if s.shouldStop(position) {
defer func() {
s.activated = false
}()
err := s.orderExecutor.ClosePosition(context.Background(), fixedpoint.One, "hhllStop") err := s.orderExecutor.ClosePosition(context.Background(), fixedpoint.One, "hhllStop")
if err != nil { if err != nil {
Notify("[hhllStop] Stop of %s triggered but failed to close %s position:", s.Symbol, err) Notify("[hhllStop] Stop of %s triggered but failed to close %s position:", s.Symbol, err)
} else { return
s.activated = false
Notify("[hhllStop] Stop of %s triggered and position closed", s.Symbol)
} }
Notify("[hhllStop] Stop of %s triggered and position closed", s.Symbol)
} }
})) }))

View File

@ -48,7 +48,7 @@ func (s *LowerShadowTakeProfit) Bind(session *ExchangeSession, orderExecutor *Ge
} }
// skip close price higher than the ewma // skip close price higher than the ewma
if closePrice.Float64() > ewma.Last() { if closePrice.Float64() > ewma.Last(0) {
return return
} }

View File

@ -59,15 +59,11 @@ func (s *TrailingStop2) Bind(session *ExchangeSession, orderExecutor *GeneralOrd
})) }))
if !IsBackTesting && enableMarketTradeStop { if !IsBackTesting && enableMarketTradeStop {
session.MarketDataStream.OnMarketTrade(func(trade types.Trade) { session.MarketDataStream.OnMarketTrade(types.TradeWith(position.Symbol, func(trade types.Trade) {
if trade.Symbol != position.Symbol {
return
}
if err := s.checkStopPrice(trade.Price, position); err != nil { if err := s.checkStopPrice(trade.Price, position); err != nil {
log.WithError(err).Errorf("error") log.WithError(err).Errorf("error")
} }
}) }))
} }
} }
@ -78,8 +74,10 @@ func (s *TrailingStop2) getRatio(price fixedpoint.Value, position *types.Positio
// for short position, it's: // for short position, it's:
// (avg_cost - price) / avg_cost // (avg_cost - price) / avg_cost
return position.AverageCost.Sub(price).Div(position.AverageCost), nil return position.AverageCost.Sub(price).Div(position.AverageCost), nil
case types.SideTypeSell: case types.SideTypeSell:
return price.Sub(position.AverageCost).Div(position.AverageCost), nil return price.Sub(position.AverageCost).Div(position.AverageCost), nil
default: default:
if position.IsLong() { if position.IsLong() {
return price.Sub(position.AverageCost).Div(position.AverageCost), nil return price.Sub(position.AverageCost).Div(position.AverageCost), nil
@ -174,12 +172,15 @@ func (s *TrailingStop2) triggerStop(price fixedpoint.Value) error {
s.activated = false s.activated = false
s.latestHigh = fixedpoint.Zero s.latestHigh = fixedpoint.Zero
}() }()
Notify("[TrailingStop] %s %s stop loss triggered. price: %f callback rate: %f", s.Symbol, s, price.Float64(), s.CallbackRate.Float64())
Notify("[TrailingStop] %s %s tailingStop is triggered. price: %f callbackRate: %s", s.Symbol, s.ActivationRatio.Percentage(), price.Float64(), s.CallbackRate.Percentage())
ctx := context.Background() ctx := context.Background()
p := fixedpoint.One p := fixedpoint.One
if !s.ClosePosition.IsZero() { if !s.ClosePosition.IsZero() {
p = s.ClosePosition p = s.ClosePosition
} }
return s.orderExecutor.ClosePosition(ctx, p, "trailingStop") tagName := fmt.Sprintf("trailingStop:activation=%s,callback=%s", s.ActivationRatio.Percentage(), s.CallbackRate.Percentage())
return s.orderExecutor.ClosePosition(ctx, p, tagName)
} }

View File

@ -41,7 +41,7 @@ func TestTrailingStop_ShortPosition(t *testing.T) {
Type: types.OrderTypeMarket, Type: types.OrderTypeMarket,
Market: market, Market: market,
Quantity: fixedpoint.NewFromFloat(1.0), Quantity: fixedpoint.NewFromFloat(1.0),
Tag: "trailingStop", Tag: "trailingStop:activation=1%,callback=1%",
MarginSideEffect: types.SideEffectTypeAutoRepay, MarginSideEffect: types.SideEffectTypeAutoRepay,
}) })
@ -119,7 +119,7 @@ func TestTrailingStop_LongPosition(t *testing.T) {
Type: types.OrderTypeMarket, Type: types.OrderTypeMarket,
Market: market, Market: market,
Quantity: fixedpoint.NewFromFloat(1.0), Quantity: fixedpoint.NewFromFloat(1.0),
Tag: "trailingStop", Tag: "trailingStop:activation=1%,callback=1%",
MarginSideEffect: types.SideEffectTypeAutoRepay, MarginSideEffect: types.SideEffectTypeAutoRepay,
}) })

View File

@ -0,0 +1,67 @@
package bbgo
import (
"context"
"github.com/pkg/errors"
"github.com/c9s/bbgo/pkg/types"
)
// FastOrderExecutor provides shorter submit order / cancel order round-trip time
// for strategies that need to response more faster, e.g. 1s kline or market trades related strategies.
type FastOrderExecutor struct {
*GeneralOrderExecutor
}
func NewFastOrderExecutor(session *ExchangeSession, symbol, strategy, strategyInstanceID string, position *types.Position) *FastOrderExecutor {
oe := NewGeneralOrderExecutor(session, symbol, strategy, strategyInstanceID, position)
return &FastOrderExecutor{
GeneralOrderExecutor: oe,
}
}
// SubmitOrders sends []types.SubmitOrder directly to the exchange without blocking wait on the status update.
// This is a faster version of GeneralOrderExecutor.SubmitOrders(). Created orders will be consumed in newly created goroutine (in non-backteset session).
// @param ctx: golang context type.
// @param submitOrders: Lists of types.SubmitOrder to be sent to the exchange.
// @return *types.SubmitOrder: SubmitOrder with calculated quantity and price.
// @return error: Error message.
func (e *FastOrderExecutor) SubmitOrders(ctx context.Context, submitOrders ...types.SubmitOrder) (types.OrderSlice, error) {
formattedOrders, err := e.session.FormatOrders(submitOrders)
if err != nil {
return nil, err
}
createdOrders, errIdx, err := BatchPlaceOrder(ctx, e.session.Exchange, nil, formattedOrders...)
if len(errIdx) > 0 {
return nil, err
}
if IsBackTesting {
e.orderStore.Add(createdOrders...)
e.activeMakerOrders.Add(createdOrders...)
e.tradeCollector.Process()
} else {
go func() {
e.orderStore.Add(createdOrders...)
e.activeMakerOrders.Add(createdOrders...)
e.tradeCollector.Process()
}()
}
return createdOrders, err
}
// Cancel cancels all active maker orders if orders is not given, otherwise cancel the given orders
func (e *FastOrderExecutor) Cancel(ctx context.Context, orders ...types.Order) error {
if e.activeMakerOrders.NumOfOrders() == 0 {
return nil
}
if err := e.activeMakerOrders.FastCancel(ctx, e.session.Exchange, orders...); err != nil {
return errors.Wrap(err, "fast cancel order error")
}
return nil
}

View File

@ -190,38 +190,6 @@ func (e *GeneralOrderExecutor) CancelOrders(ctx context.Context, orders ...types
return err return err
} }
// FastSubmitOrders send []types.SubmitOrder directly to the exchange without blocking wait on the status update.
// This is a faster version of SubmitOrders(). Created orders will be consumed in newly created goroutine (in non-backteset session).
// @param ctx: golang context type.
// @param submitOrders: Lists of types.SubmitOrder to be sent to the exchange.
// @return *types.SubmitOrder: SubmitOrder with calculated quantity and price.
// @return error: Error message.
func (e *GeneralOrderExecutor) FastSubmitOrders(ctx context.Context, submitOrders ...types.SubmitOrder) (types.OrderSlice, error) {
formattedOrders, err := e.session.FormatOrders(submitOrders)
if err != nil {
return nil, err
}
createdOrders, errIdx, err := BatchPlaceOrder(ctx, e.session.Exchange, nil, formattedOrders...)
if len(errIdx) > 0 {
return nil, err
}
if IsBackTesting {
e.orderStore.Add(createdOrders...)
e.activeMakerOrders.Add(createdOrders...)
e.tradeCollector.Process()
} else {
go func() {
e.orderStore.Add(createdOrders...)
e.activeMakerOrders.Add(createdOrders...)
e.tradeCollector.Process()
}()
}
return createdOrders, err
}
func (e *GeneralOrderExecutor) SetLogger(logger log.FieldLogger) { func (e *GeneralOrderExecutor) SetLogger(logger log.FieldLogger) {
e.logger = logger e.logger = logger
} }
@ -300,7 +268,7 @@ func (e *GeneralOrderExecutor) reduceQuantityAndSubmitOrder(ctx context.Context,
submitOrder.Quantity = q submitOrder.Quantity = q
if e.position.Market.IsDustQuantity(submitOrder.Quantity, price) { if e.position.Market.IsDustQuantity(submitOrder.Quantity, price) {
return nil, types.NewZeroAssetError(fmt.Errorf("dust quantity")) return nil, types.NewZeroAssetError(fmt.Errorf("dust quantity, quantity = %f, price = %f", submitOrder.Quantity.Float64(), price.Float64()))
} }
createdOrder, err2 := e.SubmitOrders(ctx, submitOrder) createdOrder, err2 := e.SubmitOrders(ctx, submitOrder)
@ -366,10 +334,15 @@ func (e *GeneralOrderExecutor) NewOrderFromOpenPosition(ctx context.Context, opt
return nil, err return nil, err
} }
if price.IsZero() {
return nil, errors.New("unable to calculate quantity: zero price given")
}
quantity = quoteQuantity.Div(price) quantity = quoteQuantity.Div(price)
} }
if e.position.Market.IsDustQuantity(quantity, price) { if e.position.Market.IsDustQuantity(quantity, price) {
log.Warnf("dust quantity: %v", quantity) log.Errorf("can not submit order: dust quantity, quantity = %f, price = %f", quantity.Float64(), price.Float64())
return nil, nil return nil, nil
} }
@ -421,9 +394,11 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
if err != nil { if err != nil {
return nil, err return nil, err
} }
if submitOrder == nil { if submitOrder == nil {
return nil, nil return nil, nil
} }
price := options.Price price := options.Price
side := "long" side := "long"
@ -431,7 +406,7 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
side = "short" side = "short"
} }
Notify("Opening %s %s position with quantity %v at price %v", e.position.Symbol, side, submitOrder.Quantity, price) Notify("Opening %s %s position with quantity %f at price %f", e.position.Symbol, side, submitOrder.Quantity.Float64(), price.Float64())
createdOrder, err := e.SubmitOrders(ctx, *submitOrder) createdOrder, err := e.SubmitOrders(ctx, *submitOrder)
if err == nil { if err == nil {
@ -467,19 +442,6 @@ func (e *GeneralOrderExecutor) GracefulCancel(ctx context.Context, orders ...typ
return nil return nil
} }
// FastCancel cancels all active maker orders if orders is not given, otherwise cancel the given orders
func (e *GeneralOrderExecutor) FastCancel(ctx context.Context, orders ...types.Order) error {
if e.activeMakerOrders.NumOfOrders() == 0 {
return nil
}
if err := e.activeMakerOrders.FastCancel(ctx, e.session.Exchange, orders...); err != nil {
return errors.Wrap(err, "fast cancel order error")
}
return nil
}
// ClosePosition closes the current position by a percentage. // ClosePosition closes the current position by a percentage.
// percentage 0.1 means close 10% position // percentage 0.1 means close 10% position
// tag is the order tag you want to attach, you may pass multiple tags, the tags will be combined into one tag string by commas. // tag is the order tag you want to attach, you may pass multiple tags, the tags will be combined into one tag string by commas.

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"os" "os"
"reflect" "reflect"
"sync"
"github.com/codingconcepts/env" "github.com/codingconcepts/env"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -28,6 +29,13 @@ func Sync(ctx context.Context, obj interface{}) {
isolation := GetIsolationFromContext(ctx) isolation := GetIsolationFromContext(ctx)
ps := isolation.persistenceServiceFacade.Get() ps := isolation.persistenceServiceFacade.Get()
locker, ok := obj.(sync.Locker)
if ok {
locker.Lock()
defer locker.Unlock()
}
err := storePersistenceFields(obj, id, ps) err := storePersistenceFields(obj, id, ps)
if err != nil { if err != nil {
log.WithError(err).Errorf("persistence sync failed") log.WithError(err).Errorf("persistence sync failed")

View File

@ -12,6 +12,7 @@ type Scale interface {
Formula() string Formula() string
FormulaOf(x float64) string FormulaOf(x float64) string
Call(x float64) (y float64) Call(x float64) (y float64)
Sum(step float64) float64
} }
func init() { func init() {
@ -21,6 +22,7 @@ func init() {
_ = Scale(&QuadraticScale{}) _ = Scale(&QuadraticScale{})
} }
// f(x) := ab^x
// y := ab^x // y := ab^x
// shift xs[0] to 0 (x - h) // shift xs[0] to 0 (x - h)
// a = y1 // a = y1
@ -56,6 +58,14 @@ func (s *ExponentialScale) Solve() error {
return nil return nil
} }
func (s *ExponentialScale) Sum(step float64) float64 {
sum := 0.0
for x := s.Domain[0]; x <= s.Domain[1]; x += step {
sum += s.Call(x)
}
return sum
}
func (s *ExponentialScale) String() string { func (s *ExponentialScale) String() string {
return s.Formula() return s.Formula()
} }
@ -100,6 +110,14 @@ func (s *LogarithmicScale) Call(x float64) (y float64) {
return y return y
} }
func (s *LogarithmicScale) Sum(step float64) float64 {
sum := 0.0
for x := s.Domain[0]; x <= s.Domain[1]; x += step {
sum += s.Call(x)
}
return sum
}
func (s *LogarithmicScale) String() string { func (s *LogarithmicScale) String() string {
return s.Formula() return s.Formula()
} }
@ -158,6 +176,14 @@ func (s *LinearScale) Call(x float64) (y float64) {
return y return y
} }
func (s *LinearScale) Sum(step float64) float64 {
sum := 0.0
for x := s.Domain[0]; x <= s.Domain[1]; x += step {
sum += s.Call(x)
}
return sum
}
func (s *LinearScale) String() string { func (s *LinearScale) String() string {
return s.Formula() return s.Formula()
} }
@ -201,6 +227,14 @@ func (s *QuadraticScale) Call(x float64) (y float64) {
return y return y
} }
func (s *QuadraticScale) Sum(step float64) float64 {
sum := 0.0
for x := s.Domain[0]; x <= s.Domain[1]; x += step {
sum += s.Call(x)
}
return sum
}
func (s *QuadraticScale) String() string { func (s *QuadraticScale) String() string {
return s.Formula() return s.Formula()
} }
@ -266,18 +300,20 @@ func (rule *SlideRule) Scale() (Scale, error) {
// LayerScale defines the scale DSL for maker layers, e.g., // LayerScale defines the scale DSL for maker layers, e.g.,
// //
// quantityScale: // quantityScale:
// byLayer: //
// exp: // byLayer:
// domain: [1, 5] // exp:
// range: [0.01, 1.0] // domain: [1, 5]
// range: [0.01, 1.0]
// //
// and // and
// //
// quantityScale: // quantityScale:
// byLayer: //
// linear: // byLayer:
// domain: [1, 3] // linear:
// range: [0.01, 1.0] // domain: [1, 3]
// range: [0.01, 1.0]
type LayerScale struct { type LayerScale struct {
LayerRule *SlideRule `json:"byLayer"` LayerRule *SlideRule `json:"byLayer"`
} }
@ -303,18 +339,20 @@ func (s *LayerScale) Scale(layer int) (quantity float64, err error) {
// PriceVolumeScale defines the scale DSL for strategy, e.g., // PriceVolumeScale defines the scale DSL for strategy, e.g.,
// //
// quantityScale: // quantityScale:
// byPrice: //
// exp: // byPrice:
// domain: [10_000, 50_000] // exp:
// range: [0.01, 1.0] // domain: [10_000, 50_000]
// range: [0.01, 1.0]
// //
// and // and
// //
// quantityScale: // quantityScale:
// byVolume: //
// linear: // byVolume:
// domain: [10_000, 50_000] // linear:
// range: [0.01, 1.0] // domain: [10_000, 50_000]
// range: [0.01, 1.0]
type PriceVolumeScale struct { type PriceVolumeScale struct {
ByPriceRule *SlideRule `json:"byPrice"` ByPriceRule *SlideRule `json:"byPrice"`
ByVolumeRule *SlideRule `json:"byVolume"` ByVolumeRule *SlideRule `json:"byVolume"`

View File

@ -703,21 +703,39 @@ func (session *ExchangeSession) FindPossibleSymbols() (symbols []string, err err
return symbols, nil return symbols, nil
} }
// newBasicPrivateExchange allocates a basic exchange instance with the user private credentials
func (session *ExchangeSession) newBasicPrivateExchange(exchangeName types.ExchangeName) (types.Exchange, error) {
var err error
var exMinimal types.ExchangeMinimal
if session.Key != "" && session.Secret != "" {
exMinimal, err = exchange2.New(exchangeName, session.Key, session.Secret, session.Passphrase)
} else {
exMinimal, err = exchange2.NewWithEnvVarPrefix(exchangeName, session.EnvVarPrefix)
}
if err != nil {
return nil, err
}
if ex, ok := exMinimal.(types.Exchange); ok {
return ex, nil
}
return nil, fmt.Errorf("exchange %T does not implement types.Exchange", exMinimal)
}
// InitExchange initialize the exchange instance and allocate memory for fields // InitExchange initialize the exchange instance and allocate memory for fields
// In this stage, the session var could be loaded from the JSON config, so the pointer fields are still nil // In this stage, the session var could be loaded from the JSON config, so the pointer fields are still nil
// The Init method will be called after this stage, environment.Init will call the session.Init method later. // The Init method will be called after this stage, environment.Init will call the session.Init method later.
func (session *ExchangeSession) InitExchange(name string, ex types.Exchange) error { func (session *ExchangeSession) InitExchange(name string, ex types.Exchange) error {
var err error var err error
var exchangeName = session.ExchangeName var exchangeName = session.ExchangeName
if ex == nil { if ex == nil {
if session.PublicOnly { if session.PublicOnly {
ex, err = exchange2.NewPublic(exchangeName) ex, err = exchange2.NewPublic(exchangeName)
} else { } else {
if session.Key != "" && session.Secret != "" { ex, err = session.newBasicPrivateExchange(exchangeName)
ex, err = exchange2.NewStandard(exchangeName, session.Key, session.Secret, session.Passphrase, session.SubAccount)
} else {
ex, err = exchange2.NewWithEnvVarPrefix(exchangeName, session.EnvVarPrefix)
}
} }
} }

View File

@ -29,7 +29,7 @@ type StandardIndicatorSet struct {
// interval -> window // interval -> window
iwbIndicators map[types.IntervalWindowBandWidth]*indicator.BOLL iwbIndicators map[types.IntervalWindowBandWidth]*indicator.BOLL
iwIndicators map[indicatorKey]indicator.KLinePusher iwIndicators map[indicatorKey]indicator.KLinePusher
macdIndicators map[indicator.MACDConfig]*indicator.MACD macdIndicators map[indicator.MACDConfig]*indicator.MACDLegacy
stream types.Stream stream types.Stream
store *MarketDataStore store *MarketDataStore
@ -47,7 +47,7 @@ func NewStandardIndicatorSet(symbol string, stream types.Stream, store *MarketDa
stream: stream, stream: stream,
iwIndicators: make(map[indicatorKey]indicator.KLinePusher), iwIndicators: make(map[indicatorKey]indicator.KLinePusher),
iwbIndicators: make(map[types.IntervalWindowBandWidth]*indicator.BOLL), iwbIndicators: make(map[types.IntervalWindowBandWidth]*indicator.BOLL),
macdIndicators: make(map[indicator.MACDConfig]*indicator.MACD), macdIndicators: make(map[indicator.MACDConfig]*indicator.MACDLegacy),
} }
} }
@ -154,14 +154,14 @@ func (s *StandardIndicatorSet) BOLL(iw types.IntervalWindow, bandWidth float64)
return inc return inc
} }
func (s *StandardIndicatorSet) MACD(iw types.IntervalWindow, shortPeriod, longPeriod int) *indicator.MACD { func (s *StandardIndicatorSet) MACD(iw types.IntervalWindow, shortPeriod, longPeriod int) *indicator.MACDLegacy {
config := indicator.MACDConfig{IntervalWindow: iw, ShortPeriod: shortPeriod, LongPeriod: longPeriod} config := indicator.MACDConfig{IntervalWindow: iw, ShortPeriod: shortPeriod, LongPeriod: longPeriod}
inc, ok := s.macdIndicators[config] inc, ok := s.macdIndicators[config]
if ok { if ok {
return inc return inc
} }
inc = &indicator.MACD{MACDConfig: config} inc = &indicator.MACDLegacy{MACDConfig: config}
s.macdIndicators[config] = inc s.macdIndicators[config] = inc
s.initAndBind(inc, config.IntervalWindow.Interval) s.initAndBind(inc, config.IntervalWindow.Interval)
return inc return inc

View File

@ -21,7 +21,7 @@ func (s *StopEMA) Bind(session *ExchangeSession, orderExecutor *GeneralOrderExec
} }
func (s *StopEMA) Allowed(closePrice fixedpoint.Value) bool { func (s *StopEMA) Allowed(closePrice fixedpoint.Value) bool {
ema := fixedpoint.NewFromFloat(s.stopEWMA.Last()) ema := fixedpoint.NewFromFloat(s.stopEWMA.Last(0))
if ema.IsZero() { if ema.IsZero() {
logrus.Infof("stopEMA protection: value is zero, skip") logrus.Infof("stopEMA protection: value is zero, skip")
return false return false

View File

@ -29,12 +29,12 @@ func (s *TrendEMA) Bind(session *ExchangeSession, orderExecutor *GeneralOrderExe
} }
s.last = s.ewma.Values[s.ewma.Length()-2] s.last = s.ewma.Values[s.ewma.Length()-2]
s.current = s.ewma.Last() s.current = s.ewma.Last(0)
}) })
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) { session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) {
s.last = s.current s.last = s.current
s.current = s.ewma.Last() s.current = s.ewma.Last(0)
})) }))
} }

63
pkg/cmd/exchangetest.go Normal file
View File

@ -0,0 +1,63 @@
//go:build exchangetest
// +build exchangetest
package cmd
import (
"context"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/c9s/bbgo/pkg/exchange"
"github.com/c9s/bbgo/pkg/types"
)
// go run ./cmd/bbgo kline --exchange=binance --symbol=BTCUSDT
var exchangeTestCmd = &cobra.Command{
Use: "exchange-test",
Short: "test the exchange",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
exchangeNameStr, err := cmd.Flags().GetString("exchange")
if err != nil {
return err
}
exchangeName, err := types.ValidExchangeName(exchangeNameStr)
if err != nil {
return err
}
exMinimal, err := exchange.NewWithEnvVarPrefix(exchangeName, "")
if err != nil {
return err
}
log.Infof("types.ExchangeMinimal: ✅")
if service, ok := exMinimal.(types.ExchangeAccountService); ok {
log.Infof("types.ExchangeAccountService: ✅ (%T)", service)
}
if service, ok := exMinimal.(types.ExchangeMarketDataService); ok {
log.Infof("types.ExchangeMarketDataService: ✅ (%T)", service)
}
if ex, ok := exMinimal.(types.Exchange); ok {
log.Infof("types.Exchange: ✅ (%T)", ex)
}
_ = ctx
// cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
return nil
},
}
func init() {
exchangeTestCmd.Flags().String("exchange", "", "session name")
exchangeTestCmd.MarkFlagRequired("exchange")
RootCmd.AddCommand(exchangeTestCmd)
}

View File

@ -29,6 +29,7 @@ import (
_ "github.com/c9s/bbgo/pkg/strategy/rebalance" _ "github.com/c9s/bbgo/pkg/strategy/rebalance"
_ "github.com/c9s/bbgo/pkg/strategy/rsmaker" _ "github.com/c9s/bbgo/pkg/strategy/rsmaker"
_ "github.com/c9s/bbgo/pkg/strategy/schedule" _ "github.com/c9s/bbgo/pkg/strategy/schedule"
_ "github.com/c9s/bbgo/pkg/strategy/scmaker"
_ "github.com/c9s/bbgo/pkg/strategy/skeleton" _ "github.com/c9s/bbgo/pkg/strategy/skeleton"
_ "github.com/c9s/bbgo/pkg/strategy/supertrend" _ "github.com/c9s/bbgo/pkg/strategy/supertrend"
_ "github.com/c9s/bbgo/pkg/strategy/support" _ "github.com/c9s/bbgo/pkg/strategy/support"
@ -36,6 +37,7 @@ import (
_ "github.com/c9s/bbgo/pkg/strategy/techsignal" _ "github.com/c9s/bbgo/pkg/strategy/techsignal"
_ "github.com/c9s/bbgo/pkg/strategy/trendtrader" _ "github.com/c9s/bbgo/pkg/strategy/trendtrader"
_ "github.com/c9s/bbgo/pkg/strategy/wall" _ "github.com/c9s/bbgo/pkg/strategy/wall"
_ "github.com/c9s/bbgo/pkg/strategy/xalign"
_ "github.com/c9s/bbgo/pkg/strategy/xbalance" _ "github.com/c9s/bbgo/pkg/strategy/xbalance"
_ "github.com/c9s/bbgo/pkg/strategy/xfunding" _ "github.com/c9s/bbgo/pkg/strategy/xfunding"
_ "github.com/c9s/bbgo/pkg/strategy/xgap" _ "github.com/c9s/bbgo/pkg/strategy/xgap"

View File

@ -15,3 +15,9 @@ func TestHigher(t *testing.T) {
out := Higher([]float64{10.0, 11.0, 12.0, 13.0, 15.0}, 12.0) out := Higher([]float64{10.0, 11.0, 12.0, 13.0, 15.0}, 12.0)
assert.Equal(t, []float64{13.0, 15.0}, out) assert.Equal(t, []float64{13.0, 15.0}, out)
} }
func TestLSM(t *testing.T) {
slice := Slice{1., 2., 3., 4.}
slope := LSM(slice)
assert.Equal(t, 1.0, slope)
}

View File

@ -1,10 +1,10 @@
package floats package floats
func (s Slice) Pivot(left, right int, f func(a, pivot float64) bool) (float64, bool) { func (s Slice) Pivot(left, right int, f func(a, pivot float64) bool) (float64, bool) {
return CalculatePivot(s, left, right, f) return FindPivot(s, left, right, f)
} }
func CalculatePivot(values Slice, left, right int, f func(a, pivot float64) bool) (float64, bool) { func FindPivot(values Slice, left, right int, f func(a, pivot float64) bool) (float64, bool) {
length := len(values) length := len(values)
if right == 0 { if right == 0 {

View File

@ -16,6 +16,12 @@ func (s *Slice) Push(v float64) {
*s = append(*s, v) *s = append(*s, v)
} }
func (s *Slice) Append(vs ...float64) {
*s = append(*s, vs...)
}
// Update equals to Push()
// which push an element into the slice
func (s *Slice) Update(v float64) { func (s *Slice) Update(v float64) {
*s = append(*s, v) *s = append(*s, v)
} }
@ -34,8 +40,43 @@ func (s Slice) Min() float64 {
return floats.Min(s) return floats.Min(s)
} }
func (s Slice) Sub(b Slice) (c Slice) {
if len(s) != len(b) {
return c
}
c = make(Slice, len(s))
for i := 0; i < len(s); i++ {
ai := s[i]
bi := b[i]
ci := ai - bi
c[i] = ci
}
return c
}
func (s Slice) Add(b Slice) (c Slice) {
if len(s) != len(b) {
return c
}
c = make(Slice, len(s))
for i := 0; i < len(s); i++ {
ai := s[i]
bi := b[i]
ci := ai + bi
c[i] = ci
}
return c
}
func (s Slice) Sum() (sum float64) { func (s Slice) Sum() (sum float64) {
return floats.Sum(s) for _, v := range s {
sum += v
}
return sum
} }
func (s Slice) Mean() (mean float64) { func (s Slice) Mean() (mean float64) {
@ -59,6 +100,18 @@ func (s Slice) Tail(size int) Slice {
return win return win
} }
func (s Slice) Average() float64 {
if len(s) == 0 {
return 0.0
}
total := 0.0
for _, value := range s {
total += value
}
return total / float64(len(s))
}
func (s Slice) Diff() (values Slice) { func (s Slice) Diff() (values Slice) {
for i, v := range s { for i, v := range s {
if i == 0 { if i == 0 {
@ -125,27 +178,61 @@ func (s Slice) Normalize() Slice {
return s.DivScalar(s.Sum()) return s.DivScalar(s.Sum())
} }
func (s *Slice) Last() float64 {
length := len(*s)
if length > 0 {
return (*s)[length-1]
}
return 0.0
}
func (s *Slice) Index(i int) float64 {
length := len(*s)
if length-i <= 0 || i < 0 {
return 0.0
}
return (*s)[length-i-1]
}
func (s *Slice) Length() int {
return len(*s)
}
func (s Slice) Addr() *Slice { func (s Slice) Addr() *Slice {
return &s return &s
} }
// Last, Index, Length implements the types.Series interface
func (s Slice) Last(i int) float64 {
length := len(s)
if i < 0 || length-1-i < 0 {
return 0.0
}
return s[length-1-i]
}
func (s Slice) Truncate(size int) Slice {
if size < 0 || len(s) <= size {
return s
}
return s[len(s)-size:]
}
// Index fetches the element from the end of the slice
// WARNING: it does not start from 0!!!
func (s Slice) Index(i int) float64 {
return s.Last(i)
}
func (s Slice) Length() int {
return len(s)
}
func (s Slice) LSM() float64 {
return LSM(s)
}
// LSM is the least squares method for linear regression
func LSM(values Slice) float64 {
var sumX, sumY, sumXSqr, sumXY = .0, .0, .0, .0
end := len(values) - 1
for i := end; i >= 0; i-- {
val := values[i]
per := float64(end - i + 1)
sumX += per
sumY += val
sumXSqr += per * per
sumXY += val * per
}
length := float64(len(values))
slope := (length*sumXY - sumX*sumY) / (length*sumXSqr - sumX*sumX)
average := sumY / length
tail := average - slope*sumX/length + slope
head := tail + slope*(length-1)
slope2 := (tail - head) / (length - 1)
return slope2
}

View File

@ -0,0 +1,33 @@
package floats
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSub(t *testing.T) {
a := New(1, 2, 3, 4, 5)
b := New(1, 2, 3, 4, 5)
c := a.Sub(b)
assert.Equal(t, Slice{.0, .0, .0, .0, .0}, c)
assert.Equal(t, 5, len(c))
assert.Equal(t, 5, c.Length())
}
func TestTruncate(t *testing.T) {
a := New(1, 2, 3, 4, 5)
for i := 5; i > 0; i-- {
a = a.Truncate(i)
assert.Equal(t, i, a.Length())
}
}
func TestAdd(t *testing.T) {
a := New(1, 2, 3, 4, 5)
b := New(1, 2, 3, 4, 5)
c := a.Add(b)
assert.Equal(t, Slice{2.0, 4.0, 6.0, 8.0, 10.0}, c)
assert.Equal(t, 5, len(c))
assert.Equal(t, 5, c.Length())
}

View File

@ -0,0 +1,33 @@
package binanceapi
import (
"time"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/requestgen"
)
type FuturesGlobalLongShortAccountRatio struct {
Symbol string `json:"symbol"`
LongShortRatio fixedpoint.Value `json:"longShortRatio"`
LongAccount fixedpoint.Value `json:"longAccount"`
ShortAccount fixedpoint.Value `json:"shortAccount"`
Timestamp types.MillisecondTimestamp `json:"timestamp"`
}
//go:generate requestgen -method GET -url "/futures/data/globalLongShortAccountRatio" -type FuturesGlobalLongShortAccountRatioRequest -responseType []FuturesGlobalLongShortAccountRatio
type FuturesGlobalLongShortAccountRatioRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
period types.Interval `param:"period"`
limit *uint64 `param:"limit"`
startTime *time.Time `param:"startTime,milliseconds"`
endTime *time.Time `param:"endTime,milliseconds"`
}
func (c *FuturesRestClient) NewFuturesGlobalLongShortAccountRatioRequest() *FuturesGlobalLongShortAccountRatioRequest {
return &FuturesGlobalLongShortAccountRatioRequest{client: c}
}

View File

@ -0,0 +1,24 @@
package binanceapi
import (
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/requestgen"
)
type FuturesOpenInterest struct {
OpenInterest fixedpoint.Value `json:"openInterest"`
Symbol string `json:"symbol"`
Time types.MillisecondTimestamp `json:"time"`
}
//go:generate requestgen -method GET -url "/fapi/v1/openInterest" -type FuturesGetOpenInterestRequest -responseType FuturesOpenInterest
type FuturesGetOpenInterestRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
}
func (c *FuturesRestClient) NewFuturesGetOpenInterestRequest() *FuturesGetOpenInterestRequest {
return &FuturesGetOpenInterestRequest{client: c}
}

View File

@ -0,0 +1,148 @@
// Code generated by "requestgen -method GET -url /fapi/v1/openInterest -type FuturesGetOpenInterestRequest -responseType FuturesOpenInterest"; DO NOT EDIT.
package binanceapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (f *FuturesGetOpenInterestRequest) Symbol(symbol string) *FuturesGetOpenInterestRequest {
f.symbol = symbol
return f
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (f *FuturesGetOpenInterestRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (f *FuturesGetOpenInterestRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := f.symbol
// assign parameter of symbol
params["symbol"] = symbol
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (f *FuturesGetOpenInterestRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := f.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if f.isVarSlice(_v) {
f.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (f *FuturesGetOpenInterestRequest) GetParametersJSON() ([]byte, error) {
params, err := f.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (f *FuturesGetOpenInterestRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (f *FuturesGetOpenInterestRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (f *FuturesGetOpenInterestRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (f *FuturesGetOpenInterestRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (f *FuturesGetOpenInterestRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := f.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (f *FuturesGetOpenInterestRequest) Do(ctx context.Context) (*FuturesOpenInterest, error) {
// empty params for GET operation
var params interface{}
query, err := f.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/fapi/v1/openInterest"
req, err := f.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := f.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse FuturesOpenInterest
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
return &apiResponse, nil
}

View File

@ -0,0 +1,32 @@
package binanceapi
import (
"time"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/requestgen"
)
type FuturesOpenInterestStatistics struct {
Symbol string `json:"symbol"`
SumOpenInterest fixedpoint.Value `json:"sumOpenInterest"`
SumOpenInterestValue fixedpoint.Value `json:"sumOpenInterestValue"`
Timestamp types.MillisecondTimestamp `json:"timestamp"`
}
//go:generate requestgen -method GET -url "/futures/data/openInterestHist" -type FuturesGetOpenInterestStatisticsRequest -responseType []FuturesOpenInterestStatistics
type FuturesGetOpenInterestStatisticsRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol,required"`
period types.Interval `param:"period,required"`
limit *uint64 `param:"limit"`
startTime *time.Time `param:"startTime,milliseconds"`
endTime *time.Time `param:"endTime,milliseconds"`
}
func (c *FuturesRestClient) NewFuturesGetOpenInterestStatisticsRequest() *FuturesGetOpenInterestStatisticsRequest {
return &FuturesGetOpenInterestStatisticsRequest{client: c}
}

View File

@ -0,0 +1,214 @@
// Code generated by "requestgen -method GET -url /futures/data/openInterestHist -type FuturesGetOpenInterestStatisticsRequest -responseType []FuturesOpenInterestStatistics"; DO NOT EDIT.
package binanceapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/types"
"net/url"
"reflect"
"regexp"
"strconv"
"time"
)
func (f *FuturesGetOpenInterestStatisticsRequest) Symbol(symbol string) *FuturesGetOpenInterestStatisticsRequest {
f.symbol = symbol
return f
}
func (f *FuturesGetOpenInterestStatisticsRequest) Period(period types.Interval) *FuturesGetOpenInterestStatisticsRequest {
f.period = period
return f
}
func (f *FuturesGetOpenInterestStatisticsRequest) Limit(limit uint64) *FuturesGetOpenInterestStatisticsRequest {
f.limit = &limit
return f
}
func (f *FuturesGetOpenInterestStatisticsRequest) StartTime(startTime time.Time) *FuturesGetOpenInterestStatisticsRequest {
f.startTime = &startTime
return f
}
func (f *FuturesGetOpenInterestStatisticsRequest) EndTime(endTime time.Time) *FuturesGetOpenInterestStatisticsRequest {
f.endTime = &endTime
return f
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (f *FuturesGetOpenInterestStatisticsRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (f *FuturesGetOpenInterestStatisticsRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := f.symbol
// TEMPLATE check-required
if len(symbol) == 0 {
return nil, fmt.Errorf("symbol is required, empty string given")
}
// END TEMPLATE check-required
// assign parameter of symbol
params["symbol"] = symbol
// check period field -> json key period
period := f.period
// TEMPLATE check-required
if len(period) == 0 {
return nil, fmt.Errorf("period is required, empty string given")
}
// END TEMPLATE check-required
// assign parameter of period
params["period"] = period
// check limit field -> json key limit
if f.limit != nil {
limit := *f.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
// check startTime field -> json key startTime
if f.startTime != nil {
startTime := *f.startTime
// assign parameter of startTime
// convert time.Time to milliseconds time stamp
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
// check endTime field -> json key endTime
if f.endTime != nil {
endTime := *f.endTime
// assign parameter of endTime
// convert time.Time to milliseconds time stamp
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (f *FuturesGetOpenInterestStatisticsRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := f.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if f.isVarSlice(_v) {
f.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (f *FuturesGetOpenInterestStatisticsRequest) GetParametersJSON() ([]byte, error) {
params, err := f.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (f *FuturesGetOpenInterestStatisticsRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (f *FuturesGetOpenInterestStatisticsRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (f *FuturesGetOpenInterestStatisticsRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (f *FuturesGetOpenInterestStatisticsRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (f *FuturesGetOpenInterestStatisticsRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := f.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (f *FuturesGetOpenInterestStatisticsRequest) Do(ctx context.Context) ([]FuturesOpenInterestStatistics, error) {
// empty params for GET operation
var params interface{}
query, err := f.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/futures/data/openInterestHist"
req, err := f.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := f.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse []FuturesOpenInterestStatistics
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
return apiResponse, nil
}

View File

@ -0,0 +1,32 @@
package binanceapi
import (
"time"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/requestgen"
)
type FuturesTakerBuySellVolume struct {
BuySellRatio fixedpoint.Value `json:"buySellRatio"`
BuyVol fixedpoint.Value `json:"buyVol"`
SellVol fixedpoint.Value `json:"sellVol"`
Timestamp types.MillisecondTimestamp `json:"timestamp"`
}
//go:generate requestgen -method GET -url "/futures/data/takerlongshortRatio" -type FuturesTakerBuySellVolumeRequest -responseType []FuturesTakerBuySellVolume
type FuturesTakerBuySellVolumeRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
period types.Interval `param:"period"`
limit *uint64 `param:"limit"`
startTime *time.Time `param:"startTime,milliseconds"`
endTime *time.Time `param:"endTime,milliseconds"`
}
func (c *FuturesRestClient) NewFuturesTakerBuySellVolumeRequest() *FuturesTakerBuySellVolumeRequest {
return &FuturesTakerBuySellVolumeRequest{client: c}
}

View File

@ -0,0 +1,33 @@
package binanceapi
import (
"time"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/requestgen"
)
type FuturesTopTraderLongShortAccountRatio struct {
Symbol string `json:"symbol"`
LongShortRatio fixedpoint.Value `json:"longShortRatio"`
LongAccount fixedpoint.Value `json:"longAccount"`
ShortAccount fixedpoint.Value `json:"shortAccount"`
Timestamp types.MillisecondTimestamp `json:"timestamp"`
}
//go:generate requestgen -method GET -url "/futures/data/topLongShortAccountRatio" -type FuturesTopTraderLongShortAccountRatioRequest -responseType []FuturesTopTraderLongShortAccountRatio
type FuturesTopTraderLongShortAccountRatioRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
period types.Interval `param:"period"`
limit *uint64 `param:"limit"`
startTime *time.Time `param:"startTime,milliseconds"`
endTime *time.Time `param:"endTime,milliseconds"`
}
func (c *FuturesRestClient) NewFuturesTopTraderLongShortAccountRatioRequest() *FuturesTopTraderLongShortAccountRatioRequest {
return &FuturesTopTraderLongShortAccountRatioRequest{client: c}
}

View File

@ -0,0 +1,33 @@
package binanceapi
import (
"time"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/requestgen"
)
type FuturesTopTraderLongShortPositionRatio struct {
Symbol string `json:"symbol"`
LongShortRatio fixedpoint.Value `json:"longShortRatio"`
LongAccount fixedpoint.Value `json:"longAccount"`
ShortAccount fixedpoint.Value `json:"shortAccount"`
Timestamp types.MillisecondTimestamp `json:"timestamp"`
}
//go:generate requestgen -method GET -url "/futures/data/topLongShortPositionRatio" -type FuturesTopTraderLongShortPositionRatioRequest -responseType []FuturesTopTraderLongShortPositionRatio
type FuturesTopTraderLongShortPositionRatioRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
period types.Interval `param:"period"`
limit *uint64 `param:"limit"`
startTime *time.Time `param:"startTime,milliseconds"`
endTime *time.Time `param:"endTime,milliseconds"`
}
func (c *FuturesRestClient) NewFuturesTopTraderLongShortPositionRatioRequest() *FuturesTopTraderLongShortPositionRatioRequest {
return &FuturesTopTraderLongShortPositionRatioRequest{client: c}
}

View File

@ -0,0 +1,202 @@
// Code generated by "requestgen -method GET -url /futures/data/globalLongShortAccountRatio -type FuturesGlobalLongShortAccountRatioRequest -responseType []FuturesGlobalLongShortAccountRatio"; DO NOT EDIT.
package binanceapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/types"
"net/url"
"reflect"
"regexp"
"strconv"
"time"
)
func (f *FuturesGlobalLongShortAccountRatioRequest) Symbol(symbol string) *FuturesGlobalLongShortAccountRatioRequest {
f.symbol = symbol
return f
}
func (f *FuturesGlobalLongShortAccountRatioRequest) Period(period types.Interval) *FuturesGlobalLongShortAccountRatioRequest {
f.period = period
return f
}
func (f *FuturesGlobalLongShortAccountRatioRequest) Limit(limit uint64) *FuturesGlobalLongShortAccountRatioRequest {
f.limit = &limit
return f
}
func (f *FuturesGlobalLongShortAccountRatioRequest) StartTime(startTime time.Time) *FuturesGlobalLongShortAccountRatioRequest {
f.startTime = &startTime
return f
}
func (f *FuturesGlobalLongShortAccountRatioRequest) EndTime(endTime time.Time) *FuturesGlobalLongShortAccountRatioRequest {
f.endTime = &endTime
return f
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (f *FuturesGlobalLongShortAccountRatioRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (f *FuturesGlobalLongShortAccountRatioRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := f.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check period field -> json key period
period := f.period
// assign parameter of period
params["period"] = period
// check limit field -> json key limit
if f.limit != nil {
limit := *f.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
// check startTime field -> json key startTime
if f.startTime != nil {
startTime := *f.startTime
// assign parameter of startTime
// convert time.Time to milliseconds time stamp
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
// check endTime field -> json key endTime
if f.endTime != nil {
endTime := *f.endTime
// assign parameter of endTime
// convert time.Time to milliseconds time stamp
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (f *FuturesGlobalLongShortAccountRatioRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := f.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if f.isVarSlice(_v) {
f.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (f *FuturesGlobalLongShortAccountRatioRequest) GetParametersJSON() ([]byte, error) {
params, err := f.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (f *FuturesGlobalLongShortAccountRatioRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (f *FuturesGlobalLongShortAccountRatioRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (f *FuturesGlobalLongShortAccountRatioRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (f *FuturesGlobalLongShortAccountRatioRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (f *FuturesGlobalLongShortAccountRatioRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := f.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (f *FuturesGlobalLongShortAccountRatioRequest) Do(ctx context.Context) ([]FuturesGlobalLongShortAccountRatio, error) {
// empty params for GET operation
var params interface{}
query, err := f.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/futures/data/globalLongShortAccountRatio"
req, err := f.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := f.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse []FuturesGlobalLongShortAccountRatio
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
return apiResponse, nil
}

View File

@ -0,0 +1,202 @@
// Code generated by "requestgen -method GET -url /futures/data/takerlongshortRatio -type FuturesTakerBuySellVolumeRequest -responseType []FuturesTakerBuySellVolume"; DO NOT EDIT.
package binanceapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/types"
"net/url"
"reflect"
"regexp"
"strconv"
"time"
)
func (f *FuturesTakerBuySellVolumeRequest) Symbol(symbol string) *FuturesTakerBuySellVolumeRequest {
f.symbol = symbol
return f
}
func (f *FuturesTakerBuySellVolumeRequest) Period(period types.Interval) *FuturesTakerBuySellVolumeRequest {
f.period = period
return f
}
func (f *FuturesTakerBuySellVolumeRequest) Limit(limit uint64) *FuturesTakerBuySellVolumeRequest {
f.limit = &limit
return f
}
func (f *FuturesTakerBuySellVolumeRequest) StartTime(startTime time.Time) *FuturesTakerBuySellVolumeRequest {
f.startTime = &startTime
return f
}
func (f *FuturesTakerBuySellVolumeRequest) EndTime(endTime time.Time) *FuturesTakerBuySellVolumeRequest {
f.endTime = &endTime
return f
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (f *FuturesTakerBuySellVolumeRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (f *FuturesTakerBuySellVolumeRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := f.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check period field -> json key period
period := f.period
// assign parameter of period
params["period"] = period
// check limit field -> json key limit
if f.limit != nil {
limit := *f.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
// check startTime field -> json key startTime
if f.startTime != nil {
startTime := *f.startTime
// assign parameter of startTime
// convert time.Time to milliseconds time stamp
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
// check endTime field -> json key endTime
if f.endTime != nil {
endTime := *f.endTime
// assign parameter of endTime
// convert time.Time to milliseconds time stamp
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (f *FuturesTakerBuySellVolumeRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := f.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if f.isVarSlice(_v) {
f.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (f *FuturesTakerBuySellVolumeRequest) GetParametersJSON() ([]byte, error) {
params, err := f.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (f *FuturesTakerBuySellVolumeRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (f *FuturesTakerBuySellVolumeRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (f *FuturesTakerBuySellVolumeRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (f *FuturesTakerBuySellVolumeRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (f *FuturesTakerBuySellVolumeRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := f.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (f *FuturesTakerBuySellVolumeRequest) Do(ctx context.Context) ([]FuturesTakerBuySellVolume, error) {
// empty params for GET operation
var params interface{}
query, err := f.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/futures/data/takerlongshortRatio"
req, err := f.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := f.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse []FuturesTakerBuySellVolume
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
return apiResponse, nil
}

View File

@ -0,0 +1,202 @@
// Code generated by "requestgen -method GET -url /futures/data/topLongShortAccountRatio -type FuturesTopTraderLongShortAccountRatioRequest -responseType []FuturesTopTraderLongShortAccountRatio"; DO NOT EDIT.
package binanceapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/types"
"net/url"
"reflect"
"regexp"
"strconv"
"time"
)
func (f *FuturesTopTraderLongShortAccountRatioRequest) Symbol(symbol string) *FuturesTopTraderLongShortAccountRatioRequest {
f.symbol = symbol
return f
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) Period(period types.Interval) *FuturesTopTraderLongShortAccountRatioRequest {
f.period = period
return f
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) Limit(limit uint64) *FuturesTopTraderLongShortAccountRatioRequest {
f.limit = &limit
return f
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) StartTime(startTime time.Time) *FuturesTopTraderLongShortAccountRatioRequest {
f.startTime = &startTime
return f
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) EndTime(endTime time.Time) *FuturesTopTraderLongShortAccountRatioRequest {
f.endTime = &endTime
return f
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (f *FuturesTopTraderLongShortAccountRatioRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (f *FuturesTopTraderLongShortAccountRatioRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := f.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check period field -> json key period
period := f.period
// assign parameter of period
params["period"] = period
// check limit field -> json key limit
if f.limit != nil {
limit := *f.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
// check startTime field -> json key startTime
if f.startTime != nil {
startTime := *f.startTime
// assign parameter of startTime
// convert time.Time to milliseconds time stamp
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
// check endTime field -> json key endTime
if f.endTime != nil {
endTime := *f.endTime
// assign parameter of endTime
// convert time.Time to milliseconds time stamp
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (f *FuturesTopTraderLongShortAccountRatioRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := f.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if f.isVarSlice(_v) {
f.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (f *FuturesTopTraderLongShortAccountRatioRequest) GetParametersJSON() ([]byte, error) {
params, err := f.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (f *FuturesTopTraderLongShortAccountRatioRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := f.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (f *FuturesTopTraderLongShortAccountRatioRequest) Do(ctx context.Context) ([]FuturesTopTraderLongShortAccountRatio, error) {
// empty params for GET operation
var params interface{}
query, err := f.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/futures/data/topLongShortAccountRatio"
req, err := f.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := f.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse []FuturesTopTraderLongShortAccountRatio
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
return apiResponse, nil
}

View File

@ -0,0 +1,202 @@
// Code generated by "requestgen -method GET -url /futures/data/topLongShortPositionRatio -type FuturesTopTraderLongShortPositionRatioRequest -responseType []FuturesTopTraderLongShortPositionRatio"; DO NOT EDIT.
package binanceapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/types"
"net/url"
"reflect"
"regexp"
"strconv"
"time"
)
func (f *FuturesTopTraderLongShortPositionRatioRequest) Symbol(symbol string) *FuturesTopTraderLongShortPositionRatioRequest {
f.symbol = symbol
return f
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) Period(period types.Interval) *FuturesTopTraderLongShortPositionRatioRequest {
f.period = period
return f
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) Limit(limit uint64) *FuturesTopTraderLongShortPositionRatioRequest {
f.limit = &limit
return f
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) StartTime(startTime time.Time) *FuturesTopTraderLongShortPositionRatioRequest {
f.startTime = &startTime
return f
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) EndTime(endTime time.Time) *FuturesTopTraderLongShortPositionRatioRequest {
f.endTime = &endTime
return f
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (f *FuturesTopTraderLongShortPositionRatioRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (f *FuturesTopTraderLongShortPositionRatioRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := f.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check period field -> json key period
period := f.period
// assign parameter of period
params["period"] = period
// check limit field -> json key limit
if f.limit != nil {
limit := *f.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
// check startTime field -> json key startTime
if f.startTime != nil {
startTime := *f.startTime
// assign parameter of startTime
// convert time.Time to milliseconds time stamp
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
// check endTime field -> json key endTime
if f.endTime != nil {
endTime := *f.endTime
// assign parameter of endTime
// convert time.Time to milliseconds time stamp
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (f *FuturesTopTraderLongShortPositionRatioRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := f.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if f.isVarSlice(_v) {
f.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (f *FuturesTopTraderLongShortPositionRatioRequest) GetParametersJSON() ([]byte, error) {
params, err := f.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (f *FuturesTopTraderLongShortPositionRatioRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := f.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (f *FuturesTopTraderLongShortPositionRatioRequest) Do(ctx context.Context) ([]FuturesTopTraderLongShortPositionRatio, error) {
// empty params for GET operation
var params interface{}
query, err := f.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/futures/data/topLongShortPositionRatio"
req, err := f.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := f.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse []FuturesTopTraderLongShortPositionRatio
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
return apiResponse, nil
}

View File

@ -45,6 +45,14 @@ func toGlobalMarket(symbol binance.Symbol) types.Market {
market.TickSize = fixedpoint.MustNewFromString(f.TickSize) market.TickSize = fixedpoint.MustNewFromString(f.TickSize)
} }
if market.MinNotional.IsZero() {
log.Warnf("binance market %s minNotional is zero", market.Symbol)
}
if market.MinQuantity.IsZero() {
log.Warnf("binance market %s minQuantity is zero", market.Symbol)
}
return market return market
} }

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/adshao/go-binance/v2/futures" "github.com/adshao/go-binance/v2/futures"
"github.com/slack-go/slack"
"github.com/adshao/go-binance/v2" "github.com/adshao/go-binance/v2"
"github.com/valyala/fastjson" "github.com/valyala/fastjson"
@ -21,43 +22,42 @@ type EventBase struct {
} }
/* /*
executionReport executionReport
{ {
"e": "executionReport", // Event type "e": "executionReport", // Event type
"E": 1499405658658, // Event time "E": 1499405658658, // Event time
"s": "ETHBTC", // Symbol "s": "ETHBTC", // Symbol
"c": "mUvoqJxFIILMdfAW5iGSOW", // Client order ID "c": "mUvoqJxFIILMdfAW5iGSOW", // Client order ID
"S": "BUY", // Side "S": "BUY", // Side
"o": "LIMIT", // Order type "o": "LIMIT", // Order type
"f": "GTC", // Time in force "f": "GTC", // Time in force
"q": "1.00000000", // Order quantity "q": "1.00000000", // Order quantity
"p": "0.10264410", // Order price "p": "0.10264410", // Order price
"P": "0.00000000", // Stop price "P": "0.00000000", // Stop price
"F": "0.00000000", // Iceberg quantity "F": "0.00000000", // Iceberg quantity
"g": -1, // OrderListId "g": -1, // OrderListId
"C": null, // Original client order ID; This is the ID of the order being canceled "C": null, // Original client order ID; This is the ID of the order being canceled
"x": "NEW", // Current execution type "x": "NEW", // Current execution type
"X": "NEW", // Current order status "X": "NEW", // Current order status
"r": "NONE", // Order reject reason; will be an error code. "r": "NONE", // Order reject reason; will be an error code.
"i": 4293153, // Order ID "i": 4293153, // Order ID
"l": "0.00000000", // Last executed quantity "l": "0.00000000", // Last executed quantity
"z": "0.00000000", // Cumulative filled quantity "z": "0.00000000", // Cumulative filled quantity
"L": "0.00000000", // Last executed price "L": "0.00000000", // Last executed price
"n": "0", // Commission amount "n": "0", // Commission amount
"N": null, // Commission asset "N": null, // Commission asset
"T": 1499405658657, // Transaction time "T": 1499405658657, // Transaction time
"t": -1, // Trade ID "t": -1, // Trade ID
"I": 8641984, // Ignore "I": 8641984, // Ignore
"w": true, // Is the order on the book? "w": true, // Is the order on the book?
"m": false, // Is this trade the maker side? "m": false, // Is this trade the maker side?
"M": false, // Ignore "M": false, // Ignore
"O": 1499405658657, // Order creation time "O": 1499405658657, // Order creation time
"Z": "0.00000000", // Cumulative quote asset transacted quantity "Z": "0.00000000", // Cumulative quote asset transacted quantity
"Y": "0.00000000", // Last quote asset transacted quantity (i.e. lastPrice * lastQty) "Y": "0.00000000", // Last quote asset transacted quantity (i.e. lastPrice * lastQty)
"Q": "0.00000000" // Quote Order Quantity "Q": "0.00000000" // Quote Order Quantity
} }
*/ */
type ExecutionReportEvent struct { type ExecutionReportEvent struct {
EventBase EventBase
@ -165,71 +165,100 @@ func (e *ExecutionReportEvent) Trade() (*types.Trade, error) {
} }
/* /*
balanceUpdate event: balanceUpdate
{ Balance Update occurs during the following:
"e": "balanceUpdate", //KLineEvent Type
"E": 1573200697110, //KLineEvent Time Deposits or withdrawals from the account
"a": "BTC", //Asset Transfer of funds between accounts (e.g. Spot to Margin)
"d": "100.00000000", //Balance Delta
"T": 1573200697068 //Clear Time {
} "e": "balanceUpdate", //KLineEvent Type
"E": 1573200697110, //KLineEvent Time
"a": "BTC", //Asset
"d": "100.00000000", //Balance Delta
"T": 1573200697068 //Clear Time
}
This event is only for Spot
*/ */
type BalanceUpdateEvent struct { type BalanceUpdateEvent struct {
EventBase EventBase
Asset string `json:"a"` Asset string `json:"a"`
Delta string `json:"d"` Delta fixedpoint.Value `json:"d"`
ClearTime int64 `json:"T"` ClearTime types.MillisecondTimestamp `json:"T"`
}
func (e *BalanceUpdateEvent) SlackAttachment() slack.Attachment {
return slack.Attachment{
Title: "Binance Balance Update Event",
Color: "warning",
Fields: []slack.AttachmentField{
{
Title: "Asset",
Value: e.Asset,
Short: true,
},
{
Title: "Delta",
Value: e.Delta.String(),
Short: true,
},
{
Title: "Time",
Value: e.ClearTime.String(),
Short: true,
},
},
}
} }
/* /*
outboundAccountInfo outboundAccountInfo
{ {
"e": "outboundAccountInfo", // KLineEvent type "e": "outboundAccountInfo", // KLineEvent type
"E": 1499405658849, // KLineEvent time "E": 1499405658849, // KLineEvent time
"m": 0, // Maker commission rate (bips) "m": 0, // Maker commission rate (bips)
"t": 0, // Taker commission rate (bips) "t": 0, // Taker commission rate (bips)
"b": 0, // Buyer commission rate (bips) "b": 0, // Buyer commission rate (bips)
"s": 0, // Seller commission rate (bips) "s": 0, // Seller commission rate (bips)
"T": true, // Can trade? "T": true, // Can trade?
"W": true, // Can withdraw? "W": true, // Can withdraw?
"D": true, // Can deposit? "D": true, // Can deposit?
"u": 1499405658848, // Time of last account update "u": 1499405658848, // Time of last account update
"B": [ // AccountBalances array "B": [ // AccountBalances array
{ {
"a": "LTC", // Asset "a": "LTC", // Asset
"f": "17366.18538083", // Free amount "f": "17366.18538083", // Free amount
"l": "0.00000000" // Locked amount "l": "0.00000000" // Locked amount
}, },
{ {
"a": "BTC", "a": "BTC",
"f": "10537.85314051", "f": "10537.85314051",
"l": "2.19464093" "l": "2.19464093"
}, },
{ {
"a": "ETH", "a": "ETH",
"f": "17902.35190619", "f": "17902.35190619",
"l": "0.00000000" "l": "0.00000000"
}, },
{ {
"a": "BNC", "a": "BNC",
"f": "1114503.29769312", "f": "1114503.29769312",
"l": "0.00000000" "l": "0.00000000"
}, },
{ {
"a": "NEO", "a": "NEO",
"f": "0.00000000", "f": "0.00000000",
"l": "0.00000000" "l": "0.00000000"
} }
], ],
"P": [ // Account Permissions "P": [ // Account Permissions
"SPOT" "SPOT"
] ]
} }
*/ */
type Balance struct { type Balance struct {
Asset string `json:"a"` Asset string `json:"a"`

View File

@ -0,0 +1,20 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
)
type CancelOrderBySymbolResponse string
//go:generate GetRequest -url "/api/spot/v1/trade/cancel-symbol-order" -type CancelOrderBySymbolRequest -responseDataType .CancelOrderBySymbolResponse
type CancelOrderBySymbolRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
}
func (c *RestClient) NewCancelOrderBySymbolRequest() *CancelOrderBySymbolRequest {
return &CancelOrderBySymbolRequest{client: c}
}

View File

@ -0,0 +1,152 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/trade/cancel-symbol-order -type CancelOrderBySymbolRequest -responseDataType .CancelOrderBySymbolResponse"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (c *CancelOrderBySymbolRequest) Symbol(symbol string) *CancelOrderBySymbolRequest {
c.symbol = symbol
return c
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (c *CancelOrderBySymbolRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (c *CancelOrderBySymbolRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := c.symbol
// assign parameter of symbol
params["symbol"] = symbol
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (c *CancelOrderBySymbolRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := c.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if c.isVarSlice(_v) {
c.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (c *CancelOrderBySymbolRequest) GetParametersJSON() ([]byte, error) {
params, err := c.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (c *CancelOrderBySymbolRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (c *CancelOrderBySymbolRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (c *CancelOrderBySymbolRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (c *CancelOrderBySymbolRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (c *CancelOrderBySymbolRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := c.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (c *CancelOrderBySymbolRequest) Do(ctx context.Context) (*CancelOrderBySymbolResponse, error) {
// empty params for GET operation
var params interface{}
query, err := c.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/api/spot/v1/trade/cancel-symbol-order"
req, err := c.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := c.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data CancelOrderBySymbolResponse
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,25 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
)
type CancelOrderResponse struct {
OrderId string `json:"orderId"`
ClientOrderId string `json:"clientOrderId"`
}
//go:generate PostRequest -url "/api/spot/v1/trade/cancel-order-v2" -type CancelOrderRequest -responseDataType .CancelOrderResponse
type CancelOrderRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
orderId *string `param:"orderId"`
clientOrderId *string `param:"clientOid"`
}
func (c *RestClient) NewCancelOrderRequest() *CancelOrderRequest {
return &CancelOrderRequest{client: c}
}

View File

@ -0,0 +1,177 @@
// Code generated by "requestgen -method POST -responseType .APIResponse -responseDataField Data -url /api/spot/v1/trade/cancel-order-v2 -type CancelOrderRequest -responseDataType .CancelOrderResponse"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (c *CancelOrderRequest) Symbol(symbol string) *CancelOrderRequest {
c.symbol = symbol
return c
}
func (c *CancelOrderRequest) OrderId(orderId string) *CancelOrderRequest {
c.orderId = &orderId
return c
}
func (c *CancelOrderRequest) ClientOrderId(clientOrderId string) *CancelOrderRequest {
c.clientOrderId = &clientOrderId
return c
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (c *CancelOrderRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (c *CancelOrderRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := c.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check orderId field -> json key orderId
if c.orderId != nil {
orderId := *c.orderId
// assign parameter of orderId
params["orderId"] = orderId
} else {
}
// check clientOrderId field -> json key clientOid
if c.clientOrderId != nil {
clientOrderId := *c.clientOrderId
// assign parameter of clientOrderId
params["clientOid"] = clientOrderId
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (c *CancelOrderRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := c.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if c.isVarSlice(_v) {
c.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (c *CancelOrderRequest) GetParametersJSON() ([]byte, error) {
params, err := c.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (c *CancelOrderRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (c *CancelOrderRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (c *CancelOrderRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (c *CancelOrderRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (c *CancelOrderRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := c.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (c *CancelOrderRequest) Do(ctx context.Context) (*CancelOrderResponse, error) {
params, err := c.GetParameters()
if err != nil {
return nil, err
}
query := url.Values{}
apiURL := "/api/spot/v1/trade/cancel-order-v2"
req, err := c.client.NewAuthenticatedRequest(ctx, "POST", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := c.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data CancelOrderResponse
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,163 @@
package bitgetapi
import (
"bytes"
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/c9s/requestgen"
"github.com/pkg/errors"
)
const defaultHTTPTimeout = time.Second * 15
const RestBaseURL = "https://api.bitget.com"
const PublicWebSocketURL = "wss://ws.bitget.com/spot/v1/stream"
const PrivateWebSocketURL = "wss://ws.bitget.com/spot/v1/stream"
type RestClient struct {
requestgen.BaseAPIClient
key, secret, passphrase string
}
func NewClient() *RestClient {
u, err := url.Parse(RestBaseURL)
if err != nil {
panic(err)
}
return &RestClient{
BaseAPIClient: requestgen.BaseAPIClient{
BaseURL: u,
HttpClient: &http.Client{
Timeout: defaultHTTPTimeout,
},
},
}
}
func (c *RestClient) Auth(key, secret, passphrase string) {
c.key = key
c.secret = secret
c.passphrase = passphrase
}
// newAuthenticatedRequest creates new http request for authenticated routes.
func (c *RestClient) NewAuthenticatedRequest(ctx context.Context, method, refURL string, params url.Values, payload interface{}) (*http.Request, error) {
if len(c.key) == 0 {
return nil, errors.New("empty api key")
}
if len(c.secret) == 0 {
return nil, errors.New("empty api secret")
}
rel, err := url.Parse(refURL)
if err != nil {
return nil, err
}
if params != nil {
rel.RawQuery = params.Encode()
}
pathURL := c.BaseURL.ResolveReference(rel)
path := pathURL.Path
if rel.RawQuery != "" {
path += "?" + rel.RawQuery
}
// See https://bitgetlimited.github.io/apidoc/en/spot/#signature
// sign(
// timestamp +
// method.toUpperCase() +
// requestPath + "?" + queryString +
// body **string
// )
// (+ means string concat) encrypt by **HMAC SHA256 **algorithm, and encode the encrypted result through **BASE64.
// set location to UTC so that it outputs "2020-12-08T09:08:57.715Z"
t := time.Now().In(time.UTC)
timestamp := strconv.FormatInt(t.UnixMilli(), 10)
body, err := castPayload(payload)
if err != nil {
return nil, err
}
signKey := timestamp + strings.ToUpper(method) + path + string(body)
signature := sign(signKey, c.secret)
req, err := http.NewRequestWithContext(ctx, method, pathURL.String(), bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
req.Header.Add("ACCESS-KEY", c.key)
req.Header.Add("ACCESS-SIGN", signature)
req.Header.Add("ACCESS-TIMESTAMP", timestamp)
req.Header.Add("ACCESS-PASSPHRASE", c.passphrase)
return req, nil
}
func sign(payload string, secret string) string {
var sig = hmac.New(sha256.New, []byte(secret))
_, err := sig.Write([]byte(payload))
if err != nil {
return ""
}
return base64.StdEncoding.EncodeToString(sig.Sum(nil))
}
func castPayload(payload interface{}) ([]byte, error) {
if payload == nil {
return nil, nil
}
switch v := payload.(type) {
case string:
return []byte(v), nil
case []byte:
return v, nil
}
return json.Marshal(payload)
}
/*
sample:
{
"code": "00000",
"msg": "success",
"data": {
"user_id": "714229403",
"inviter_id": "682221498",
"ips": "172.23.88.91",
"authorities": [
"trade",
"readonly"
],
"parentId":"566624801",
"trader":false
}
}
*/
type APIResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Data json.RawMessage `json:"data"`
}

View File

@ -0,0 +1,70 @@
package bitgetapi
import (
"context"
"os"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/testutil"
)
func getTestClientOrSkip(t *testing.T) *RestClient {
if b, _ := strconv.ParseBool(os.Getenv("CI")); b {
t.Skip("skip test for CI")
}
key, secret, ok := testutil.IntegrationTestConfigured(t, "BITGET")
if !ok {
t.Skip("BITGET_* env vars are not configured")
return nil
}
client := NewClient()
client.Auth(key, secret, os.Getenv("BITGET_API_PASSPHRASE"))
return client
}
func TestClient(t *testing.T) {
client := getTestClientOrSkip(t)
ctx := context.Background()
t.Run("GetAllTickersRequest", func(t *testing.T) {
req := client.NewGetAllTickersRequest()
tickers, err := req.Do(ctx)
assert.NoError(t, err)
t.Logf("tickers: %+v", tickers)
})
t.Run("GetTickerRequest", func(t *testing.T) {
req := client.NewGetTickerRequest()
req.Symbol("BTCUSDT_SPBL")
ticker, err := req.Do(ctx)
assert.NoError(t, err)
t.Logf("ticker: %+v", ticker)
})
t.Run("GetDepthRequest", func(t *testing.T) {
req := client.NewGetDepthRequest()
req.Symbol("BTCUSDT_SPBL")
depth, err := req.Do(ctx)
assert.NoError(t, err)
t.Logf("depth: %+v", depth)
})
t.Run("GetServerTimeRequest", func(t *testing.T) {
req := client.NewGetServerTimeRequest()
serverTime, err := req.Do(ctx)
assert.NoError(t, err)
t.Logf("time: %+v", serverTime)
})
t.Run("GetAccountAssetsRequest", func(t *testing.T) {
req := client.NewGetAccountAssetsRequest()
assets, err := req.Do(ctx)
assert.NoError(t, err)
t.Logf("assets: %+v", assets)
})
}

View File

@ -0,0 +1,29 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type AccountAsset struct {
CoinId int64 `json:"coinId"`
CoinName string `json:"coinName"`
Available fixedpoint.Value `json:"available"`
Frozen fixedpoint.Value `json:"frozen"`
Lock fixedpoint.Value `json:"lock"`
UTime types.MillisecondTimestamp `json:"uTime"`
}
//go:generate GetRequest -url "/api/spot/v1/account/assets" -type GetAccountAssetsRequest -responseDataType []AccountAsset
type GetAccountAssetsRequest struct {
client requestgen.AuthenticatedAPIClient
}
func (c *RestClient) NewGetAccountAssetsRequest() *GetAccountAssetsRequest {
return &GetAccountAssetsRequest{client: c}
}

View File

@ -0,0 +1,139 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/account/assets -type GetAccountAssetsRequest -responseDataType []AccountAsset"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetAccountAssetsRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetAccountAssetsRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetAccountAssetsRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetAccountAssetsRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetAccountAssetsRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetAccountAssetsRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetAccountAssetsRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetAccountAssetsRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetAccountAssetsRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetAccountAssetsRequest) Do(ctx context.Context) ([]AccountAsset, error) {
// no body params
var params interface{}
query := url.Values{}
apiURL := "/api/spot/v1/account/assets"
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data []AccountAsset
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -0,0 +1,28 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/types"
)
type Account struct {
UserId types.StrInt64 `json:"user_id"`
InviterId types.StrInt64 `json:"inviter_id"`
Ips string `json:"ips"`
Authorities []string `json:"authorities"`
ParentId types.StrInt64 `json:"parentId"`
Trader bool `json:"trader"`
}
//go:generate GetRequest -url "/api/spot/v1/account/getInfo" -type GetAccountRequest -responseDataType .Account
type GetAccountRequest struct {
client requestgen.AuthenticatedAPIClient
}
func (c *RestClient) NewGetAccountRequest() *GetAccountRequest {
return &GetAccountRequest{client: c}
}

View File

@ -0,0 +1,139 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/account/getInfo -type GetAccountRequest -responseDataType .Account"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetAccountRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetAccountRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetAccountRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetAccountRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetAccountRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetAccountRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetAccountRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetAccountRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetAccountRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetAccountRequest) Do(ctx context.Context) (*Account, error) {
// no body params
var params interface{}
query := url.Values{}
apiURL := "/api/spot/v1/account/getInfo"
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data Account
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,17 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
)
//go:generate GetRequest -url "/api/spot/v1/market/tickers" -type GetAllTickersRequest -responseDataType []Ticker
type GetAllTickersRequest struct {
client requestgen.APIClient
}
func (c *RestClient) NewGetAllTickersRequest() *GetAllTickersRequest {
return &GetAllTickersRequest{client: c}
}

View File

@ -0,0 +1,139 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/market/tickers -type GetAllTickersRequest -responseDataType []Ticker"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetAllTickersRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetAllTickersRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetAllTickersRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetAllTickersRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetAllTickersRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetAllTickersRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetAllTickersRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetAllTickersRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetAllTickersRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetAllTickersRequest) Do(ctx context.Context) ([]Ticker, error) {
// no body params
var params interface{}
query := url.Values{}
apiURL := "/api/spot/v1/market/tickers"
req, err := g.client.NewRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data []Ticker
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -0,0 +1,31 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type Candle struct {
Open fixedpoint.Value `json:"open"`
High fixedpoint.Value `json:"high"`
Low fixedpoint.Value `json:"low"`
Close fixedpoint.Value `json:"close"`
QuoteVol fixedpoint.Value `json:"quoteVol"`
BaseVol fixedpoint.Value `json:"baseVol"`
UsdtVol fixedpoint.Value `json:"usdtVol"`
Ts types.MillisecondTimestamp `json:"ts"`
}
//go:generate GetRequest -url "/api/spot/v1/market/candles" -type GetCandlesRequest -responseDataType []Candle
type GetCandlesRequest struct {
client requestgen.APIClient
}
func (c *RestClient) NewGetCandlesRequest() *GetCandlesRequest {
return &GetCandlesRequest{client: c}
}

View File

@ -0,0 +1,139 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/market/candles -type GetCandlesRequest -responseDataType []Candle"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetCandlesRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetCandlesRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetCandlesRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetCandlesRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetCandlesRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetCandlesRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetCandlesRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetCandlesRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetCandlesRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetCandlesRequest) Do(ctx context.Context) ([]Candle, error) {
// no body params
var params interface{}
query := url.Values{}
apiURL := "/api/spot/v1/market/candles"
req, err := g.client.NewRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data []Candle
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -0,0 +1,30 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type Depth struct {
Asks [][]fixedpoint.Value `json:"asks"`
Bids [][]fixedpoint.Value `json:"bids"`
Timestamp types.MillisecondTimestamp `json:"timestamp"`
}
//go:generate GetRequest -url "/api/spot/v1/market/depth" -type GetDepthRequest -responseDataType .Depth
type GetDepthRequest struct {
client requestgen.APIClient
symbol string `param:"symbol"`
stepType string `param:"type" default:"step0"`
limit *int `param:"limit"`
}
func (c *RestClient) NewGetDepthRequest() *GetDepthRequest {
return &GetDepthRequest{client: c}
}

View File

@ -0,0 +1,175 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/market/depth -type GetDepthRequest -responseDataType .Depth"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (g *GetDepthRequest) Symbol(symbol string) *GetDepthRequest {
g.symbol = symbol
return g
}
func (g *GetDepthRequest) StepType(stepType string) *GetDepthRequest {
g.stepType = stepType
return g
}
func (g *GetDepthRequest) Limit(limit int) *GetDepthRequest {
g.limit = &limit
return g
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetDepthRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetDepthRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := g.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check stepType field -> json key type
stepType := g.stepType
// assign parameter of stepType
params["type"] = stepType
// check limit field -> json key limit
if g.limit != nil {
limit := *g.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetDepthRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetDepthRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetDepthRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetDepthRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetDepthRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetDepthRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetDepthRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetDepthRequest) Do(ctx context.Context) (*Depth, error) {
// empty params for GET operation
var params interface{}
query, err := g.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/api/spot/v1/market/depth"
req, err := g.client.NewRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data Depth
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,43 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type Fill struct {
AccountId types.StrInt64 `json:"accountId"`
Symbol string `json:"symbol"`
OrderId types.StrInt64 `json:"orderId"`
FillId types.StrInt64 `json:"fillId"`
OrderType OrderType `json:"orderType"`
Side OrderSide `json:"side"`
FillPrice fixedpoint.Value `json:"fillPrice"`
FillQuantity fixedpoint.Value `json:"fillQuantity"`
FillTotalAmount fixedpoint.Value `json:"fillTotalAmount"`
CreationTime types.MillisecondTimestamp `json:"cTime"`
FeeCurrency string `json:"feeCcy"`
Fees fixedpoint.Value `json:"fees"`
}
//go:generate GetRequest -url "/api/spot/v1/trade/fills" -type GetFillsRequest -responseDataType .ServerTime
type GetFillsRequest struct {
client requestgen.AuthenticatedAPIClient
// after - order id
after *string `param:"after"`
// before - order id
before *string `param:"before"`
limit *string `param:"limit"`
}
func (c *RestClient) NewGetFillsRequest() *GetFillsRequest {
return &GetFillsRequest{client: c}
}

View File

@ -0,0 +1,182 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/trade/fills -type GetFillsRequest -responseDataType .ServerTime"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/types"
"net/url"
"reflect"
"regexp"
)
func (g *GetFillsRequest) After(after string) *GetFillsRequest {
g.after = &after
return g
}
func (g *GetFillsRequest) Before(before string) *GetFillsRequest {
g.before = &before
return g
}
func (g *GetFillsRequest) Limit(limit string) *GetFillsRequest {
g.limit = &limit
return g
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetFillsRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetFillsRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check after field -> json key after
if g.after != nil {
after := *g.after
// assign parameter of after
params["after"] = after
} else {
}
// check before field -> json key before
if g.before != nil {
before := *g.before
// assign parameter of before
params["before"] = before
} else {
}
// check limit field -> json key limit
if g.limit != nil {
limit := *g.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetFillsRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetFillsRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetFillsRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetFillsRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetFillsRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetFillsRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetFillsRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetFillsRequest) Do(ctx context.Context) (*types.MillisecondTimestamp, error) {
// empty params for GET operation
var params interface{}
query, err := g.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/api/spot/v1/trade/fills"
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data types.MillisecondTimestamp
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,19 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
)
//go:generate GetRequest -url "/api/spot/v1/trade/open-orders" -type GetOpenOrdersRequest -responseDataType []OrderDetail
type GetOpenOrdersRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
}
func (c *RestClient) NewGetOpenOrdersRequest() *GetOpenOrdersRequest {
return &GetOpenOrdersRequest{client: c}
}

View File

@ -0,0 +1,152 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/trade/open-orders -type GetOpenOrdersRequest -responseDataType []OrderDetail"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (g *GetOpenOrdersRequest) Symbol(symbol string) *GetOpenOrdersRequest {
g.symbol = symbol
return g
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetOpenOrdersRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetOpenOrdersRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := g.symbol
// assign parameter of symbol
params["symbol"] = symbol
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetOpenOrdersRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetOpenOrdersRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetOpenOrdersRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetOpenOrdersRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetOpenOrdersRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetOpenOrdersRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetOpenOrdersRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetOpenOrdersRequest) Do(ctx context.Context) ([]OrderDetail, error) {
// empty params for GET operation
var params interface{}
query, err := g.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/api/spot/v1/trade/open-orders"
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data []OrderDetail
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -0,0 +1,41 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type OrderDetail struct {
AccountId types.StrInt64 `json:"accountId"`
Symbol string `json:"symbol"`
OrderId types.StrInt64 `json:"orderId"`
ClientOrderId string `json:"clientOrderId"`
Price fixedpoint.Value `json:"price"`
Quantity fixedpoint.Value `json:"quantity"`
OrderType OrderType `json:"orderType"`
Side OrderSide `json:"side"`
Status OrderStatus `json:"status"`
FillPrice fixedpoint.Value `json:"fillPrice"`
FillQuantity fixedpoint.Value `json:"fillQuantity"`
FillTotalAmount fixedpoint.Value `json:"fillTotalAmount"`
EnterPointSource string `json:"enterPointSource"`
CTime types.MillisecondTimestamp `json:"cTime"`
}
//go:generate PostRequest -url "/api/spot/v1/trade/orderInfo" -type GetOrderDetailRequest -responseDataType []OrderDetail
type GetOrderDetailRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
orderId *string `param:"orderId"`
clientOrderId *string `param:"clientOid"`
}
func (c *RestClient) NewGetOrderDetailRequest() *GetOrderDetailRequest {
return &GetOrderDetailRequest{client: c}
}

View File

@ -0,0 +1,177 @@
// Code generated by "requestgen -method POST -responseType .APIResponse -responseDataField Data -url /api/spot/v1/trade/orderInfo -type GetOrderDetailRequest -responseDataType .OrderDetail"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (g *GetOrderDetailRequest) Symbol(symbol string) *GetOrderDetailRequest {
g.symbol = symbol
return g
}
func (g *GetOrderDetailRequest) OrderId(orderId string) *GetOrderDetailRequest {
g.orderId = &orderId
return g
}
func (g *GetOrderDetailRequest) ClientOrderId(clientOrderId string) *GetOrderDetailRequest {
g.clientOrderId = &clientOrderId
return g
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetOrderDetailRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetOrderDetailRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := g.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check orderId field -> json key orderId
if g.orderId != nil {
orderId := *g.orderId
// assign parameter of orderId
params["orderId"] = orderId
} else {
}
// check clientOrderId field -> json key clientOid
if g.clientOrderId != nil {
clientOrderId := *g.clientOrderId
// assign parameter of clientOrderId
params["clientOid"] = clientOrderId
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetOrderDetailRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetOrderDetailRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetOrderDetailRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetOrderDetailRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetOrderDetailRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetOrderDetailRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetOrderDetailRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetOrderDetailRequest) Do(ctx context.Context) (*OrderDetail, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
query := url.Values{}
apiURL := "/api/spot/v1/trade/orderInfo"
req, err := g.client.NewAuthenticatedRequest(ctx, "POST", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data OrderDetail
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,27 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
)
//go:generate GetRequest -url "/api/spot/v1/trade/history" -type GetOrderHistoryRequest -responseDataType []OrderDetail
type GetOrderHistoryRequest struct {
client requestgen.AuthenticatedAPIClient
symbol string `param:"symbol"`
// after - order id
after *string `param:"after"`
// before - order id
before *string `param:"before"`
limit *string `param:"limit"`
}
func (c *RestClient) NewGetOrderHistoryRequest() *GetOrderHistoryRequest {
return &GetOrderHistoryRequest{client: c}
}

View File

@ -0,0 +1,191 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/trade/history -type GetOrderHistoryRequest -responseDataType []OrderDetail"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (g *GetOrderHistoryRequest) Symbol(symbol string) *GetOrderHistoryRequest {
g.symbol = symbol
return g
}
func (g *GetOrderHistoryRequest) After(after string) *GetOrderHistoryRequest {
g.after = &after
return g
}
func (g *GetOrderHistoryRequest) Before(before string) *GetOrderHistoryRequest {
g.before = &before
return g
}
func (g *GetOrderHistoryRequest) Limit(limit string) *GetOrderHistoryRequest {
g.limit = &limit
return g
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetOrderHistoryRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetOrderHistoryRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := g.symbol
// assign parameter of symbol
params["symbol"] = symbol
// check after field -> json key after
if g.after != nil {
after := *g.after
// assign parameter of after
params["after"] = after
} else {
}
// check before field -> json key before
if g.before != nil {
before := *g.before
// assign parameter of before
params["before"] = before
} else {
}
// check limit field -> json key limit
if g.limit != nil {
limit := *g.limit
// assign parameter of limit
params["limit"] = limit
} else {
}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetOrderHistoryRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetOrderHistoryRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetOrderHistoryRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetOrderHistoryRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetOrderHistoryRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetOrderHistoryRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetOrderHistoryRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetOrderHistoryRequest) Do(ctx context.Context) ([]OrderDetail, error) {
// empty params for GET operation
var params interface{}
query, err := g.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/api/spot/v1/trade/history"
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data []OrderDetail
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -0,0 +1,21 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/types"
)
type ServerTime = types.MillisecondTimestamp
//go:generate GetRequest -url "/api/spot/v1/public/time" -type GetServerTimeRequest -responseDataType .ServerTime
type GetServerTimeRequest struct {
client requestgen.APIClient
}
func (c *RestClient) NewGetServerTimeRequest() *GetServerTimeRequest {
return &GetServerTimeRequest{client: c}
}

View File

@ -0,0 +1,140 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/public/time -type GetServerTimeRequest -responseDataType .ServerTime"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/types"
"net/url"
"reflect"
"regexp"
)
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetServerTimeRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetServerTimeRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetServerTimeRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetServerTimeRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetServerTimeRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetServerTimeRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetServerTimeRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetServerTimeRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetServerTimeRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetServerTimeRequest) Do(ctx context.Context) (*types.MillisecondTimestamp, error) {
// no body params
var params interface{}
query := url.Values{}
apiURL := "/api/spot/v1/public/time"
req, err := g.client.NewRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data types.MillisecondTimestamp
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,36 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
)
type Symbol struct {
Symbol string `json:"symbol"`
SymbolName string `json:"symbolName"`
BaseCoin string `json:"baseCoin"`
QuoteCoin string `json:"quoteCoin"`
MinTradeAmount fixedpoint.Value `json:"minTradeAmount"`
MaxTradeAmount fixedpoint.Value `json:"maxTradeAmount"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate"`
MakerFeeRate fixedpoint.Value `json:"makerFeeRate"`
PriceScale fixedpoint.Value `json:"priceScale"`
QuantityScale fixedpoint.Value `json:"quantityScale"`
MinTradeUSDT fixedpoint.Value `json:"minTradeUSDT"`
Status string `json:"status"`
BuyLimitPriceRatio fixedpoint.Value `json:"buyLimitPriceRatio"`
SellLimitPriceRatio fixedpoint.Value `json:"sellLimitPriceRatio"`
}
//go:generate GetRequest -url "/api/spot/v1/public/products" -type GetSymbolsRequest -responseDataType []Symbol
type GetSymbolsRequest struct {
client requestgen.APIClient
}
func (c *RestClient) NewGetSymbolsRequest() *GetSymbolsRequest {
return &GetSymbolsRequest{client: c}
}

View File

@ -0,0 +1,139 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/public/products -type GetSymbolsRequest -responseDataType []Symbol"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetSymbolsRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetSymbolsRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetSymbolsRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetSymbolsRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetSymbolsRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetSymbolsRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetSymbolsRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetSymbolsRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetSymbolsRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetSymbolsRequest) Do(ctx context.Context) ([]Symbol, error) {
// no body params
var params interface{}
query := url.Values{}
apiURL := "/api/spot/v1/public/products"
req, err := g.client.NewRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data []Symbol
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -0,0 +1,40 @@
package bitgetapi
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type Ticker struct {
Symbol string `json:"symbol"`
High24H fixedpoint.Value `json:"high24h"`
Low24H fixedpoint.Value `json:"low24h"`
Close fixedpoint.Value `json:"close"`
QuoteVol fixedpoint.Value `json:"quoteVol"`
BaseVol fixedpoint.Value `json:"baseVol"`
UsdtVol fixedpoint.Value `json:"usdtVol"`
Ts types.MillisecondTimestamp `json:"ts"`
BuyOne fixedpoint.Value `json:"buyOne"`
SellOne fixedpoint.Value `json:"sellOne"`
BidSz fixedpoint.Value `json:"bidSz"`
AskSz fixedpoint.Value `json:"askSz"`
OpenUtc0 fixedpoint.Value `json:"openUtc0"`
ChangeUtc fixedpoint.Value `json:"changeUtc"`
Change fixedpoint.Value `json:"change"`
}
//go:generate GetRequest -url "/api/spot/v1/market/ticker" -type GetTickerRequest -responseDataType .Ticker
type GetTickerRequest struct {
client requestgen.APIClient
symbol string `param:"symbol"`
}
func (c *RestClient) NewGetTickerRequest() *GetTickerRequest {
return &GetTickerRequest{client: c}
}

View File

@ -0,0 +1,152 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/spot/v1/market/ticker -type GetTickerRequest -responseDataType .Ticker"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
)
func (g *GetTickerRequest) Symbol(symbol string) *GetTickerRequest {
g.symbol = symbol
return g
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetTickerRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetTickerRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
// check symbol field -> json key symbol
symbol := g.symbol
// assign parameter of symbol
params["symbol"] = symbol
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetTickerRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetTickerRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetTickerRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetTickerRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetTickerRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetTickerRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetTickerRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
func (g *GetTickerRequest) Do(ctx context.Context) (*Ticker, error) {
// empty params for GET operation
var params interface{}
query, err := g.GetParametersQuery()
if err != nil {
return nil, err
}
apiURL := "/api/spot/v1/market/ticker"
req, err := g.client.NewRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data Ticker
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return &data, nil
}

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