Merge branch 'main' into feature/336-kline-table

This commit is contained in:
TonyQ Wang 2021-12-11 02:26:01 +08:00 committed by GitHub
commit 776f82fcd5
34 changed files with 731 additions and 258 deletions

View File

@ -27,7 +27,6 @@ jobs:
- name: Build - name: Build
run: | run: |
npm install --global yarn npm install --global yarn
(cd frontend && yarn install)
make dist VERSION=${{ steps.get_version.outputs.VERSION }} make dist VERSION=${{ steps.get_version.outputs.VERSION }}
shell: bash shell: bash
- name: Create Release - name: Create Release

2
.gitignore vendored
View File

@ -34,3 +34,5 @@
/pkg/server/assets.go /pkg/server/assets.go
bbgo.sqlite3 bbgo.sqlite3
node_modules

View File

@ -24,8 +24,8 @@ $(BIN_DIR):
# build native bbgo # build native bbgo
bbgo: bbgo: static
go build -tags web,release -o $(BIN_DIR)/$@ ./cmd/bbgo go build -tags web,release -o $(BIN_DIR)/bbgo ./cmd/bbgo
# build native bbgo (slim version) # build native bbgo (slim version)
bbgo-slim: bbgo-slim:
@ -124,12 +124,16 @@ dist: static dist-bbgo-linux dist-bbgo-darwin desktop
pkg/version/version.go: .FORCE pkg/version/version.go: .FORCE
bash utils/generate-version-file.sh > $@ bash utils/generate-version-file.sh > $@
git commit $@ -m "bump version to $(VERSION)" || true
version: pkg/version/version.go migrations pkg/version/dev.go: .FORCE
VERSION_SUFFIX="-dev" bash utils/generate-version-file.sh > $@
version: pkg/version/version.go pkg/version/dev.go migrations
git commit $< $(word 2,$^) -m "bump version to $(VERSION)" || true
[[ -e doc/release/$(VERSION).md ]] || (echo "file doc/release/$(VERSION).md does not exist" ; exit 1) [[ -e doc/release/$(VERSION).md ]] || (echo "file doc/release/$(VERSION).md does not exist" ; exit 1)
git add -v doc/release/$(VERSION).md && git commit doc/release/$(VERSION).md -m "add release note" || true git add -v doc/release/$(VERSION).md && git commit doc/release/$(VERSION).md -m "add release note" || true
git tag -f $(VERSION) git tag -f $(VERSION)
git push origin HEAD
git push origin $(VERSION) git push origin $(VERSION)
migrations: migrations:
@ -146,8 +150,11 @@ docker-push:
docker push yoanlin/bbgo docker push yoanlin/bbgo
bash -c "[[ -n $(DOCKER_TAG) ]] && docker push yoanlin/bbgo:$(DOCKER_TAG)" bash -c "[[ -n $(DOCKER_TAG) ]] && docker push yoanlin/bbgo:$(DOCKER_TAG)"
frontend/out/index.html: frontend/node_modules:
(cd frontend && yarn export) cd frontend && yarn install
frontend/out/index.html: frontend/node_modules
cd frontend && yarn export
pkg/server/assets.go: frontend/out/index.html pkg/server/assets.go: frontend/out/index.html
go run ./util/embed -package server -output $@ $(FRONTEND_EXPORT_DIR) go run ./util/embed -package server -output $@ $(FRONTEND_EXPORT_DIR)

View File

@ -32,9 +32,10 @@ A trading bot framework written in Go. The name bbgo comes from the BB8 bot in t
Get your exchange API key and secret after you register the accounts (you can choose one or more exchanges): Get your exchange API key and secret after you register the accounts (you can choose one or more exchanges):
- For MAX: <https://max.maicoin.com/signup?r=c7982718> - MAX: <https://max.maicoin.com/signup?r=c7982718>
- For Binance: <https://www.binancezh.com/en/register?ref=VGDGLT80> - Binance: <https://www.binancezh.com/en/register?ref=VGDGLT80>
- For FTX: <https://ftx.com/#a=7710474> - FTX: <https://ftx.com/#a=7710474>
- OKEx: <https://www.okex.com/join/2412712?src=from:ios-share>
Since the exchange implementation and support are done by a small team, if you like the work they've done for you, It Since the exchange implementation and support are done by a small team, if you like the work they've done for you, It
would be great if you can use their referral code as your support to them. :-D would be great if you can use their referral code as your support to them. :-D
@ -125,6 +126,8 @@ bbgo pnl --exchange binance --asset BTC --since "2019-01-01"
## Advanced Configuration ## Advanced Configuration
### Notification
- [Setting up Telegram notification](./doc/configuration/telegram.md) - [Setting up Telegram notification](./doc/configuration/telegram.md)
- [Setting up Slack notification](./doc/configuration/slack.md) - [Setting up Slack notification](./doc/configuration/slack.md)
@ -184,7 +187,8 @@ bbgo sync --session binance
Check out the strategy directory [strategy](pkg/strategy) for all built-in strategies: Check out the strategy directory [strategy](pkg/strategy) for all built-in strategies:
- `pricealert` strategy demonstrates how to use the notification system [pricealert](pkg/strategy/pricealert) - `pricealert` strategy demonstrates how to use the notification system [pricealert](pkg/strategy/pricealert). See
[document](./doc/strategy/pricealert.md).
- `xpuremaker` strategy demonstrates how to maintain the orderbook and submit maker - `xpuremaker` strategy demonstrates how to maintain the orderbook and submit maker
orders [xpuremaker](pkg/strategy/xpuremaker) orders [xpuremaker](pkg/strategy/xpuremaker)
- `buyandhold` strategy demonstrates how to subscribe kline events and submit market - `buyandhold` strategy demonstrates how to subscribe kline events and submit market
@ -535,7 +539,7 @@ rockhopper --config rockhopper_sqlite.yaml create --type sql add_pnl_column
rockhopper --config rockhopper_mysql.yaml create --type sql add_pnl_column rockhopper --config rockhopper_mysql.yaml create --type sql add_pnl_column
``` ```
or or you can use the util script:
``` ```
bash utils/generate-new-migration.sh add_pnl_column bash utils/generate-new-migration.sh add_pnl_column
@ -558,6 +562,21 @@ Then run the following command to compile the migration files into go files:
make migrations make migrations
``` ```
If you want to override the DSN and the Driver defined in the YAML config file, you can add some env vars in your dotenv file like this:
```shell
ROCKHOPPER_DRIVER=mysql
ROCKHOPPER_DIALECT=mysql
ROCKHOPPER_DSN="root:123123@unix(/opt/local/var/run/mysql57/mysqld.sock)/bbgo"
```
And then, run:
```shell
dotenv -f .env.local -- rockhopper --config rockhopper_mysql.yaml up
```
### Setup frontend development environment ### Setup frontend development environment
```sh ```sh

20
config/pricealert-tg.yaml Normal file
View File

@ -0,0 +1,20 @@
---
notifications:
# object routing rules
routing:
trade: "$symbol"
order: "$symbol"
submitOrder: "$session" # not supported yet
pnL: "bbgo-pnl"
sessions:
binance:
exchange: binance
envVarPrefix: binance
exchangeStrategies:
- on: binance
pricealert:
symbol: "BTCUSDT"
interval: "1m"
minChange: 300

View File

@ -1,5 +1,7 @@
# Build From Source # Build From Source
## Install Go SDK
Go to the Go official website to download the Go SDK <https://go.dev/dl/>. Go to the Go official website to download the Go SDK <https://go.dev/dl/>.
An example installation looks like this: An example installation looks like this:
@ -28,12 +30,18 @@ Make sure your `go` is successfully installed:
go version go version
``` ```
## Install go-sqlite
If you need to use go-sqlite, you will need to enable CGO first: If you need to use go-sqlite, you will need to enable CGO first:
``` ```
CGO_ENABLED=1 go get github.com/mattn/go-sqlite3 CGO_ENABLED=1 go get github.com/mattn/go-sqlite3
``` ```
## Install
### Install bbgo via go install
Install bbgo: Install bbgo:
```sh ```sh
@ -64,6 +72,41 @@ export GOPATH=~/mygo
Then your bbgo will be installed at `~/mygo/bin/bbgo`. Then your bbgo will be installed at `~/mygo/bin/bbgo`.
### Install via git clone
Since the default GOPATH is located at `~/go`, you can clone the bbgo repo into the folder `~/go/src/github.com/c9s/bbgo`:
```shell
mkdir -p ~/go/src/github.com/c9s
git clone git@github.com:c9s/bbgo.git ~/go/src/github.com/c9s/bbgo
cd ~/go/src/github.com/c9s/bbgo
```
Download the go modules:
```shell
go mod download
```
And then you should be able to run bbgo with `go run`
```shell
go run ./cmd/bbgo run
```
You can also use the makefile to build bbgo:
```shell
cd frontend && yarn install
make bbgo
```
If you don't need the web interface, you can build the slim version of bbgo:
```shell
make bbgo-slim
```
## Build inside a Alpine container ## Build inside a Alpine container
Starts a docker container with the alpine image: Starts a docker container with the alpine image:

View File

@ -22,6 +22,20 @@ you can set `TELEGRAM_AUTH_TOKEN` in the `.env.local` file, e.g.,
```sh ```sh
TELEGRAM_BOT_AUTH_TOKEN=itsme55667788 TELEGRAM_BOT_AUTH_TOKEN=itsme55667788
``` ```
The alerting strategies use Telegram bot notification without further configuration. You can check the [pricealert
yaml file](../../config/pricealert-tg.yaml) in the `config/` directory for example.
If you want the order submitting/filling notification, add the following to your `bbgo.yaml`:
```yaml
notifications:
routing:
trade: "$symbol"
order: "$symbol"
submitOrder: "$session"
pnL: "bbgo-pnl"
```
Run your bbgo. Run your bbgo.

View File

@ -0,0 +1,57 @@
# Adding New Exchange
Open an issue and paste the following checklist to that issue.
You should send multiple small pull request to implement them.
**Please avoid sending a pull request with huge changes**
## Checklist
Exchange Interface (minimum)
- [ ] QueryMarkets
- [ ] QueryKLines
- [ ] QueryTickers
- [ ] QueryOrders
- [ ] QueryTrades
- [ ] SubmitOrders
Convert functions:
- [ ] MarketData convert functions
- [ ] toGlobalMarket
- [ ] toGlobalTicker
- [ ] toGlobalKLine
- [ ] UserData convert functions
- [ ] toGlobalOrder
- [ ] toGlobalTrade
- [ ] toGlobalAccount
- [ ] toGlobalBalance
Stream
- [ ] UserDataStream
- [ ] Trade message parser
- [ ] Order message parser
- [ ] Account message parser
- [ ] Balance message parser
- [ ] MarketDataStream
- [ ] OrderBook message parser (or depth)
- [ ] KLine message parser (required for backtesting)
- [ ] Public trade message parser (optional)
- [ ] Ticker message parser (optional)
- [ ] ping/pong handling.
- [ ] heart-beat hanlding or keep-alive handling.
- [ ] handling reconnect
Database
- [ ] Add a new kline table for the exchange (this is required for back-testing)
- [ ] Add MySQL migration SQL
- [ ] Add SQLite migration SQL
Exchange Factory
- [ ] Add the exchange constructor to the exchange instance factory function.
- [ ] Add extended fields to the ExchangeSession struct. (optional)

View File

@ -1,7 +1,7 @@
## Fixes ## Fixes
- - ftx: fixed FTX OnKLineClosed event
## Features ## Migrations
- - add `is_futures` fields to orders and trades table

View File

@ -0,0 +1,25 @@
### Price Alert Strategy
This strategy will send notifications to specified channels when the price change of the specified trading pairs is
larger than the threshold.
### Prerequisite
Setup Telegram/Slack notification before using Price Alert Strategy. See [Setting up Telegram Bot Notification
](../configuration/telegram.md) and [Setting up Slack Notification](../configuration/slack.md).
#### Parameters
- `symbol`
- The trading pair symbol, e.g., `BTCUSDT`, `ETHUSDT`
- `interval`
- The K-line interval, e.g., `5m`, `1h`
- `minChange`
- Alert threshold, e.g., `100`, `500`. This is a fixed value of price change. Any price change in a single K-line
larger than this value will trigger the alert.
#### Examples
See [pricealert.yaml](../../config/pricealert.yaml) and [pricealert-tg.yaml](../../config/pricealert-tg.yaml)

View File

@ -0,0 +1,18 @@
-- +up
-- +begin
ALTER TABLE `trades` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;
-- +end
-- +begin
ALTER TABLE `orders` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;
-- +end
-- +down
-- +begin
ALTER TABLE `trades` DROP COLUMN `is_futures`;
-- +end
-- +begin
ALTER TABLE `orders` DROP COLUMN `is_futures`;
-- +end

View File

@ -0,0 +1,18 @@
-- +up
-- +begin
ALTER TABLE `trades` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;
-- +end
-- +begin
ALTER TABLE `orders` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;
-- +end
-- +down
-- +begin
ALTER TABLE `trades` RENAME COLUMN `is_futures` TO `is_futures_deleted`;
-- +end
-- +begin
ALTER TABLE `orders` RENAME COLUMN `is_futures` TO `is_futures_deleted`;
-- +end

View File

@ -33,6 +33,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/c9s/bbgo/pkg/exchange/ftx"
"github.com/c9s/bbgo/pkg/exchange/okex"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
@ -68,29 +70,29 @@ type Exchange struct {
doneC chan struct{} doneC chan struct{}
} }
func NewExchange(sourceName types.ExchangeName, srv *service.BacktestService, config *bbgo.Backtest) *Exchange { func NewExchange(sourceName types.ExchangeName, srv *service.BacktestService, config *bbgo.Backtest) (*Exchange, error) {
ex, err := newPublicExchange(sourceName) ex, err := newPublicExchange(sourceName)
if err != nil { if err != nil {
panic(err) return nil, err
} }
if config == nil { if config == nil {
panic(errors.New("backtest config can not be nil")) return nil, errors.New("backtest config can not be nil")
} }
markets, err := bbgo.LoadExchangeMarketsWithCache(context.Background(), ex) markets, err := bbgo.LoadExchangeMarketsWithCache(context.Background(), ex)
if err != nil { if err != nil {
panic(err) return nil, err
} }
startTime, err := config.ParseStartTime() startTime, err := config.ParseStartTime()
if err != nil { if err != nil {
panic(err) return nil, err
} }
endTime, err := config.ParseEndTime() endTime, err := config.ParseEndTime()
if err != nil { if err != nil {
panic(err) return nil, err
} }
account := &types.Account{ account := &types.Account{
@ -117,7 +119,7 @@ func NewExchange(sourceName types.ExchangeName, srv *service.BacktestService, co
} }
e.resetMatchingBooks() e.resetMatchingBooks()
return e return e, nil
} }
func (e *Exchange) addTrade(trade types.Trade) { func (e *Exchange) addTrade(trade types.Trade) {
@ -313,7 +315,11 @@ func newPublicExchange(sourceExchange types.ExchangeName) (types.Exchange, error
return binance.New("", ""), nil return binance.New("", ""), nil
case types.ExchangeMax: case types.ExchangeMax:
return max.New("", ""), nil return max.New("", ""), nil
case types.ExchangeFTX:
return ftx.NewExchange("", "", ""), nil
case types.ExchangeOKEx:
return okex.New("", "", ""), nil
} }
return nil, fmt.Errorf("exchange %s is not supported", sourceExchange) return nil, fmt.Errorf("public data from exchange %s is not supported", sourceExchange)
} }

View File

@ -179,11 +179,11 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
if trade.IsBuyer { if trade.IsBuyer {
err = m.Account.UseLockedBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(trade.Price*trade.Quantity)) err = m.Account.UseLockedBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(trade.Price*trade.Quantity))
_ = m.Account.AddBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(trade.Quantity)) m.Account.AddBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(trade.Quantity))
} else { } else {
err = m.Account.UseLockedBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(trade.Quantity)) err = m.Account.UseLockedBalance(m.Market.BaseCurrency, fixedpoint.NewFromFloat(trade.Quantity))
_ = m.Account.AddBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(trade.Quantity*trade.Price)) m.Account.AddBalance(m.Market.QuoteCurrency, fixedpoint.NewFromFloat(trade.Quantity*trade.Price))
} }
if err != nil { if err != nil {

View File

@ -180,7 +180,7 @@ var BacktestCmd = &cobra.Command{
// if we don't have klines before the start time endpoint, the back-test will fail. // if we don't have klines before the start time endpoint, the back-test will fail.
// because the last price will be missing. // because the last price will be missing.
if firstKLine != nil && syncFromTime.Before(firstKLine.EndTime) { if firstKLine != nil && syncFromTime.Before(firstKLine.StartTime) {
return fmt.Errorf("the sync-from-time you gave %s is earlier than the previous sync-start-time %s. "+ return fmt.Errorf("the sync-from-time you gave %s is earlier than the previous sync-start-time %s. "+
"re-syncing data from the earlier date before your first sync is not support,"+ "re-syncing data from the earlier date before your first sync is not support,"+
"please clean up the kline table and restart a new sync", "please clean up the kline table and restart a new sync",
@ -261,7 +261,11 @@ var BacktestCmd = &cobra.Command{
} }
} }
backtestExchange := backtest.NewExchange(exchangeName, backtestService, userConfig.Backtest) backtestExchange, err := backtest.NewExchange(exchangeName, backtestService, userConfig.Backtest)
if err != nil {
return errors.Wrap(err, "failed to create backtest exchange")
}
environ.SetStartTime(startTime) environ.SetStartTime(startTime)
environ.AddExchange(exchangeName.String(), backtestExchange) environ.AddExchange(exchangeName.String(), backtestExchange)
@ -338,11 +342,15 @@ var BacktestCmd = &cobra.Command{
if jsonOutputEnabled { if jsonOutputEnabled {
result := struct { result := struct {
Symbol string `json:"symbol,omitempty"` Symbol string `json:"symbol,omitempty"`
LastPrice float64 `json:"lastPrice,omitempty"`
StartPrice float64 `json:"startPrice,omitempty"`
PnLReport *pnl.AverageCostPnlReport `json:"pnlReport,omitempty"` PnLReport *pnl.AverageCostPnlReport `json:"pnlReport,omitempty"`
InitialBalances types.BalanceMap `json:"initialBalances,omitempty"` InitialBalances types.BalanceMap `json:"initialBalances,omitempty"`
FinalBalances types.BalanceMap `json:"finalBalances,omitempty"` FinalBalances types.BalanceMap `json:"finalBalances,omitempty"`
}{ }{
Symbol: symbol, Symbol: symbol,
LastPrice: lastPrice,
StartPrice: startPrice,
PnLReport: report, PnLReport: report,
InitialBalances: initBalances, InitialBalances: initBalances,
FinalBalances: finalBalances, FinalBalances: finalBalances,

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/adshao/go-binance/v2" "github.com/adshao/go-binance/v2"
"github.com/adshao/go-binance/v2/futures"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
@ -14,6 +15,42 @@ import (
"github.com/c9s/bbgo/pkg/util" "github.com/c9s/bbgo/pkg/util"
) )
func toGlobalMarket(symbol binance.Symbol) types.Market {
market := types.Market{
Symbol: symbol.Symbol,
LocalSymbol: symbol.Symbol,
PricePrecision: symbol.QuotePrecision,
VolumePrecision: symbol.BaseAssetPrecision,
QuoteCurrency: symbol.QuoteAsset,
BaseCurrency: symbol.BaseAsset,
}
if f := symbol.MinNotionalFilter(); f != nil {
market.MinNotional = util.MustParseFloat(f.MinNotional)
market.MinAmount = util.MustParseFloat(f.MinNotional)
}
// The LOT_SIZE filter defines the quantity (aka "lots" in auction terms) rules for a symbol.
// There are 3 parts:
// minQty defines the minimum quantity/icebergQty allowed.
// maxQty defines the maximum quantity/icebergQty allowed.
// stepSize defines the intervals that a quantity/icebergQty can be increased/decreased by.
if f := symbol.LotSizeFilter(); f != nil {
market.MinQuantity = util.MustParseFloat(f.MinQuantity)
market.MaxQuantity = util.MustParseFloat(f.MaxQuantity)
market.StepSize = util.MustParseFloat(f.StepSize)
}
if f := symbol.PriceFilter(); f != nil {
market.MaxPrice = util.MustParseFloat(f.MaxPrice)
market.MinPrice = util.MustParseFloat(f.MinPrice)
market.TickSize = util.MustParseFloat(f.TickSize)
}
return market
}
func toGlobalIsolatedUserAsset(userAsset binance.IsolatedUserAsset) types.IsolatedUserAsset { func toGlobalIsolatedUserAsset(userAsset binance.IsolatedUserAsset) types.IsolatedUserAsset {
return types.IsolatedUserAsset{ return types.IsolatedUserAsset{
Asset: userAsset.Asset, Asset: userAsset.Asset,
@ -90,8 +127,8 @@ func toGlobalMarginAccount(account *binance.MarginAccount) *types.MarginAccount
} }
} }
func toGlobalTicker(stats *binance.PriceChangeStats) types.Ticker { func toGlobalTicker(stats *binance.PriceChangeStats) (*types.Ticker, error) {
return types.Ticker{ return &types.Ticker{
Volume: util.MustParseFloat(stats.Volume), Volume: util.MustParseFloat(stats.Volume),
Last: util.MustParseFloat(stats.LastPrice), Last: util.MustParseFloat(stats.LastPrice),
Open: util.MustParseFloat(stats.OpenPrice), Open: util.MustParseFloat(stats.OpenPrice),
@ -100,9 +137,10 @@ func toGlobalTicker(stats *binance.PriceChangeStats) types.Ticker {
Buy: util.MustParseFloat(stats.BidPrice), Buy: util.MustParseFloat(stats.BidPrice),
Sell: util.MustParseFloat(stats.AskPrice), Sell: util.MustParseFloat(stats.AskPrice),
Time: time.Unix(0, stats.CloseTime*int64(time.Millisecond)), Time: time.Unix(0, stats.CloseTime*int64(time.Millisecond)),
} },nil
} }
func toLocalOrderType(orderType types.OrderType) (binance.OrderType, error) { func toLocalOrderType(orderType types.OrderType) (binance.OrderType, error) {
switch orderType { switch orderType {
@ -303,3 +341,27 @@ func convertSubscription(s types.Subscription) string {
return fmt.Sprintf("%s@%s", strings.ToLower(s.Symbol), s.Channel) return fmt.Sprintf("%s@%s", strings.ToLower(s.Symbol), s.Channel)
} }
func convertPremiumIndex(index *futures.PremiumIndex) (*types.PremiumIndex, error) {
markPrice, err := fixedpoint.NewFromString(index.MarkPrice)
if err != nil {
return nil, err
}
lastFundingRate, err := fixedpoint.NewFromString(index.LastFundingRate)
if err != nil {
return nil, err
}
nextFundingTime := time.Unix(0, index.NextFundingTime*int64(time.Millisecond))
t := time.Unix(0, index.Time*int64(time.Millisecond))
return &types.PremiumIndex{
Symbol: index.Symbol,
MarkPrice: markPrice,
NextFundingTime: nextFundingTime,
LastFundingRate: lastFundingRate,
Time: t,
}, nil
}

View File

@ -3,14 +3,14 @@ package binance
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/adshao/go-binance/v2/futures"
"golang.org/x/time/rate"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"golang.org/x/time/rate"
"github.com/adshao/go-binance/v2" "github.com/adshao/go-binance/v2"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -36,6 +36,7 @@ func init() {
_ = types.Exchange(&Exchange{}) _ = types.Exchange(&Exchange{})
_ = types.MarginExchange(&Exchange{}) _ = types.MarginExchange(&Exchange{})
// FIXME: this is not effected since dotenv is loaded in the rootCmd, not in the init function
if ok, _ := strconv.ParseBool(os.Getenv("DEBUG_BINANCE_STREAM")); ok { if ok, _ := strconv.ParseBool(os.Getenv("DEBUG_BINANCE_STREAM")); ok {
log.Level = logrus.DebugLevel log.Level = logrus.DebugLevel
} }
@ -74,8 +75,7 @@ func (e *Exchange) QueryTicker(ctx context.Context, symbol string) (*types.Ticke
return nil, err return nil, err
} }
ticker := toGlobalTicker(stats[0]) return toGlobalTicker(stats[0])
return &ticker, nil
} }
func (e *Exchange) QueryTickers(ctx context.Context, symbol ...string) (map[string]types.Ticker, error) { func (e *Exchange) QueryTickers(ctx context.Context, symbol ...string) (map[string]types.Ticker, error) {
@ -136,38 +136,7 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
markets := types.MarketMap{} markets := types.MarketMap{}
for _, symbol := range exchangeInfo.Symbols { for _, symbol := range exchangeInfo.Symbols {
market := types.Market{ markets[symbol.Symbol] = toGlobalMarket(symbol)
Symbol: symbol.Symbol,
LocalSymbol: symbol.Symbol,
PricePrecision: symbol.QuotePrecision,
VolumePrecision: symbol.BaseAssetPrecision,
QuoteCurrency: symbol.QuoteAsset,
BaseCurrency: symbol.BaseAsset,
}
if f := symbol.MinNotionalFilter(); f != nil {
market.MinNotional = util.MustParseFloat(f.MinNotional)
market.MinAmount = util.MustParseFloat(f.MinNotional)
}
// The LOT_SIZE filter defines the quantity (aka "lots" in auction terms) rules for a symbol.
// There are 3 parts:
// minQty defines the minimum quantity/icebergQty allowed.
// maxQty defines the maximum quantity/icebergQty allowed.
// stepSize defines the intervals that a quantity/icebergQty can be increased/decreased by.
if f := symbol.LotSizeFilter(); f != nil {
market.MinQuantity = util.MustParseFloat(f.MinQuantity)
market.MaxQuantity = util.MustParseFloat(f.MaxQuantity)
market.StepSize = util.MustParseFloat(f.StepSize)
}
if f := symbol.PriceFilter(); f != nil {
market.MaxPrice = util.MustParseFloat(f.MaxPrice)
market.MinPrice = util.MustParseFloat(f.MinPrice)
market.TickSize = util.MustParseFloat(f.TickSize)
}
markets[symbol.Symbol] = market
} }
return markets, nil return markets, nil
@ -211,15 +180,6 @@ func (e *Exchange) QueryIsolatedMarginAccount(ctx context.Context, symbols ...st
return toGlobalIsolatedMarginAccount(account), nil return toGlobalIsolatedMarginAccount(account), nil
} }
func (e *Exchange) getLaunchDate() (time.Time, error) {
// binance launch date 12:00 July 14th, 2017
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return time.Time{}, err
}
return time.Date(2017, time.July, 14, 0, 0, 0, 0, loc), nil
}
func (e *Exchange) Withdrawal(ctx context.Context, asset string, amount fixedpoint.Value, address string, options *types.WithdrawalOptions) error { func (e *Exchange) Withdrawal(ctx context.Context, asset string, amount fixedpoint.Value, address string, options *types.WithdrawalOptions) error {
req := e.Client.NewCreateWithdrawService() req := e.Client.NewCreateWithdrawService()
@ -250,7 +210,7 @@ func (e *Exchange) QueryWithdrawHistory(ctx context.Context, asset string, since
var emptyTime = time.Time{} var emptyTime = time.Time{}
if startTime == emptyTime { if startTime == emptyTime {
startTime, err = e.getLaunchDate() startTime, err = getLaunchDate()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -338,7 +298,7 @@ func (e *Exchange) QueryDepositHistory(ctx context.Context, asset string, since,
var emptyTime = time.Time{} var emptyTime = time.Time{}
if startTime == emptyTime { if startTime == emptyTime {
startTime, err = e.getLaunchDate() startTime, err = getLaunchDate()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -925,44 +885,7 @@ func (e *Exchange) BatchQueryKLines(ctx context.Context, symbol string, interval
return allKLines, nil return allKLines, nil
} }
type FundingRate struct { func (e *Exchange) QueryPremiumIndex(ctx context.Context, symbol string) (*types.PremiumIndex, error) {
FundingRate fixedpoint.Value
FundingTime time.Time
Time time.Time
}
type PremiumIndex struct {
Symbol string `json:"symbol"`
MarkPrice fixedpoint.Value `json:"markPrice"`
LastFundingRate fixedpoint.Value `json:"lastFundingRate"`
NextFundingTime time.Time `json:"nextFundingTime"`
Time time.Time `json:"time"`
}
func convertPremiumIndex(index *futures.PremiumIndex) (*PremiumIndex, error) {
markPrice, err := fixedpoint.NewFromString(index.MarkPrice)
if err != nil {
return nil, err
}
lastFundingRate, err := fixedpoint.NewFromString(index.LastFundingRate)
if err != nil {
return nil, err
}
nextFundingTime := time.Unix(0, index.NextFundingTime*int64(time.Millisecond))
t := time.Unix(0, index.Time*int64(time.Millisecond))
return &PremiumIndex{
Symbol: index.Symbol,
MarkPrice: markPrice,
NextFundingTime: nextFundingTime,
LastFundingRate: lastFundingRate,
Time: t,
}, nil
}
func (e *Exchange) QueryPremiumIndex(ctx context.Context, symbol string) (*PremiumIndex, error) {
futuresClient := binance.NewFuturesClient(e.key, e.secret) futuresClient := binance.NewFuturesClient(e.key, e.secret)
// when symbol is set, only one index will be returned. // when symbol is set, only one index will be returned.
@ -974,7 +897,7 @@ func (e *Exchange) QueryPremiumIndex(ctx context.Context, symbol string) (*Premi
return convertPremiumIndex(indexes[0]) return convertPremiumIndex(indexes[0])
} }
func (e *Exchange) QueryFundingRateHistory(ctx context.Context, symbol string) (*FundingRate, error) { func (e *Exchange) QueryFundingRateHistory(ctx context.Context, symbol string) (*types.FundingRate, error) {
futuresClient := binance.NewFuturesClient(e.key, e.secret) futuresClient := binance.NewFuturesClient(e.key, e.secret)
rates, err := futuresClient.NewFundingRateService(). rates, err := futuresClient.NewFundingRateService().
Symbol(symbol). Symbol(symbol).
@ -994,9 +917,19 @@ func (e *Exchange) QueryFundingRateHistory(ctx context.Context, symbol string) (
return nil, err return nil, err
} }
return &FundingRate{ return &types.FundingRate{
FundingRate: fundingRate, FundingRate: fundingRate,
FundingTime: time.Unix(0, rate.FundingTime*int64(time.Millisecond)), FundingTime: time.Unix(0, rate.FundingTime*int64(time.Millisecond)),
Time: time.Unix(0, rate.Time*int64(time.Millisecond)), Time: time.Unix(0, rate.Time*int64(time.Millisecond)),
}, nil }, nil
} }
func getLaunchDate() (time.Time, error) {
// binance launch date 12:00 July 14th, 2017
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return time.Time{}, err
}
return time.Date(2017, time.July, 14, 0, 0, 0, 0, loc), nil
}

View File

@ -3,6 +3,7 @@ package ftx
import ( import (
"context" "context"
"fmt" "fmt"
"golang.org/x/time/rate"
"net/http" "net/http"
"net/url" "net/url"
"sort" "sort"
@ -23,6 +24,9 @@ const (
var logger = logrus.WithField("exchange", "ftx") var logger = logrus.WithField("exchange", "ftx")
// POST https://ftx.com/api/orders 429, Success: false, err: Do not send more than 2 orders on this market per 200ms
var requestLimit = rate.NewLimiter(rate.Every(220*time.Millisecond), 2)
//go:generate go run generate_symbol_map.go //go:generate go run generate_symbol_map.go
type Exchange struct { type Exchange struct {
@ -338,6 +342,9 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
if so.TimeInForce != "GTC" && so.TimeInForce != "" { if so.TimeInForce != "GTC" && so.TimeInForce != "" {
return createdOrders, fmt.Errorf("unsupported TimeInForce %s. only support GTC", so.TimeInForce) return createdOrders, fmt.Errorf("unsupported TimeInForce %s. only support GTC", so.TimeInForce)
} }
if err := requestLimit.Wait(ctx); err != nil {
logrus.WithError(err).Error("rate limit error")
}
or, err := e.newRest().PlaceOrder(ctx, PlaceOrderPayload{ or, err := e.newRest().PlaceOrder(ctx, PlaceOrderPayload{
Market: toLocalSymbol(TrimUpperString(so.Symbol)), Market: toLocalSymbol(TrimUpperString(so.Symbol)),
Side: TrimLowerString(string(so.Side)), Side: TrimLowerString(string(so.Side)),
@ -437,6 +444,9 @@ func sortByCreatedASC(orders []order) {
func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) error { func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) error {
for _, o := range orders { for _, o := range orders {
rest := e.newRest() rest := e.newRest()
if err := requestLimit.Wait(ctx); err != nil {
logrus.WithError(err).Error("rate limit error")
}
if len(o.ClientOrderID) > 0 { if len(o.ClientOrderID) > 0 {
if _, err := rest.CancelOrderByClientID(ctx, o.ClientOrderID); err != nil { if _, err := rest.CancelOrderByClientID(ctx, o.ClientOrderID); err != nil {
return err return err

View File

@ -141,7 +141,9 @@ func (s *Stream) pollKLines(ctx context.Context) {
if len(klines) > 0 { if len(klines) > 0 {
// handle mutiple klines, get the latest one // handle mutiple klines, get the latest one
s.EmitKLineClosed(klines[len(klines)-1]) kline := klines[len(klines)-1]
s.EmitKLine(kline)
s.EmitKLineClosed(kline)
} }
} }

View File

@ -1,58 +1,62 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
package ftx package ftx
var symbolMap = map[string]string{ var symbolMap = map[string]string{
"1INCH-0625": "1INCH-0625", "1INCH-1231": "1INCH-1231",
"1INCH-PERP": "1INCH-PERP", "1INCH-PERP": "1INCH-PERP",
"1INCHUSD": "1INCH/USD", "1INCHUSD": "1INCH/USD",
"AAPL-0625": "AAPL-0625", "AAPL-1231": "AAPL-1231",
"AAPLUSD": "AAPL/USD", "AAPLUSD": "AAPL/USD",
"AAVE-0625": "AAVE-0625", "AAVE-1231": "AAVE-1231",
"AAVE-PERP": "AAVE-PERP", "AAVE-PERP": "AAVE-PERP",
"AAVEUSD": "AAVE/USD", "AAVEUSD": "AAVE/USD",
"AAVEUSDT": "AAVE/USDT", "AAVEUSDT": "AAVE/USDT",
"ABNB-0625": "ABNB-0625", "ABNB-1231": "ABNB-1231",
"ABNBUSD": "ABNB/USD", "ABNBUSD": "ABNB/USD",
"ACB-0625": "ACB-0625", "ACB-1231": "ACB-1231",
"ACBUSD": "ACB/USD", "ACBUSD": "ACB/USD",
"ADA-0625": "ADA-0625", "ADA-1231": "ADA-1231",
"ADA-PERP": "ADA-PERP", "ADA-PERP": "ADA-PERP",
"ADABEARUSD": "ADABEAR/USD", "ADABEARUSD": "ADABEAR/USD",
"ADABULLUSD": "ADABULL/USD", "ADABULLUSD": "ADABULL/USD",
"ADAHALFUSD": "ADAHALF/USD", "ADAHALFUSD": "ADAHALF/USD",
"ADAHEDGEUSD": "ADAHEDGE/USD", "ADAHEDGEUSD": "ADAHEDGE/USD",
"AGLD-PERP": "AGLD-PERP",
"AGLDUSD": "AGLD/USD",
"AKROUSD": "AKRO/USD", "AKROUSD": "AKRO/USD",
"AKROUSDT": "AKRO/USDT", "AKROUSDT": "AKRO/USDT",
"ALCX-PERP": "ALCX-PERP", "ALCX-PERP": "ALCX-PERP",
"ALCXUSD": "ALCX/USD", "ALCXUSD": "ALCX/USD",
"ALGO-0625": "ALGO-0625", "ALEPHUSD": "ALEPH/USD",
"ALGO-1231": "ALGO-1231",
"ALGO-PERP": "ALGO-PERP", "ALGO-PERP": "ALGO-PERP",
"ALGOBEARUSD": "ALGOBEAR/USD", "ALGOBEARUSD": "ALGOBEAR/USD",
"ALGOBULLUSD": "ALGOBULL/USD", "ALGOBULLUSD": "ALGOBULL/USD",
"ALGOHALFUSD": "ALGOHALF/USD", "ALGOHALFUSD": "ALGOHALF/USD",
"ALGOHEDGEUSD": "ALGOHEDGE/USD", "ALGOHEDGEUSD": "ALGOHEDGE/USD",
"ALICE-PERP": "ALICE-PERP",
"ALICEUSD": "ALICE/USD",
"ALPHA-PERP": "ALPHA-PERP", "ALPHA-PERP": "ALPHA-PERP",
"ALPHAUSD": "ALPHA/USD", "ALPHAUSD": "ALPHA/USD",
"ALT-0625": "ALT-0625", "ALT-1231": "ALT-1231",
"ALT-PERP": "ALT-PERP", "ALT-PERP": "ALT-PERP",
"ALTBEARUSD": "ALTBEAR/USD", "ALTBEARUSD": "ALTBEAR/USD",
"ALTBULLUSD": "ALTBULL/USD", "ALTBULLUSD": "ALTBULL/USD",
"ALTHALFUSD": "ALTHALF/USD", "ALTHALFUSD": "ALTHALF/USD",
"ALTHEDGEUSD": "ALTHEDGE/USD", "ALTHEDGEUSD": "ALTHEDGE/USD",
"AMC-0625": "AMC-0625", "AMC-1231": "AMC-1231",
"AMCUSD": "AMC/USD", "AMCUSD": "AMC/USD",
"AMD-0625": "AMD-0625", "AMD-1231": "AMD-1231",
"AMDUSD": "AMD/USD", "AMDUSD": "AMD/USD",
"AMPL-PERP": "AMPL-PERP", "AMPL-PERP": "AMPL-PERP",
"AMPLUSD": "AMPL/USD", "AMPLUSD": "AMPL/USD",
"AMPLUSDT": "AMPL/USDT", "AMPLUSDT": "AMPL/USDT",
"AMZN-0625": "AMZN-0625", "AMZN-1231": "AMZN-1231",
"AMZNUSD": "AMZN/USD", "AMZNUSD": "AMZN/USD",
"APHA-0625": "APHA-0625", "APHA-1231": "APHA-1231",
"APHAUSD": "APHA/USD", "APHAUSD": "APHA/USD",
"AR-PERP": "AR-PERP", "AR-PERP": "AR-PERP",
"ARKK-0625": "ARKK-0625", "ARKK-1231": "ARKK-1231",
"ARKKUSD": "ARKK/USD", "ARKKUSD": "ARKK/USD",
"ASD-0625": "ASD-0625",
"ASD-PERP": "ASD-PERP", "ASD-PERP": "ASD-PERP",
"ASDBEARUSD": "ASDBEAR/USD", "ASDBEARUSD": "ASDBEAR/USD",
"ASDBEARUSDT": "ASDBEAR/USDT", "ASDBEARUSDT": "ASDBEAR/USDT",
@ -61,7 +65,9 @@ var symbolMap = map[string]string{
"ASDHALFUSD": "ASDHALF/USD", "ASDHALFUSD": "ASDHALF/USD",
"ASDHEDGEUSD": "ASDHEDGE/USD", "ASDHEDGEUSD": "ASDHEDGE/USD",
"ASDUSD": "ASD/USD", "ASDUSD": "ASD/USD",
"ATOM-0625": "ATOM-0625", "ATLAS-PERP": "ATLAS-PERP",
"ATLASUSD": "ATLAS/USD",
"ATOM-1231": "ATOM-1231",
"ATOM-PERP": "ATOM-PERP", "ATOM-PERP": "ATOM-PERP",
"ATOMBEARUSD": "ATOMBEAR/USD", "ATOMBEARUSD": "ATOMBEAR/USD",
"ATOMBULLUSD": "ATOMBULL/USD", "ATOMBULLUSD": "ATOMBULL/USD",
@ -70,16 +76,16 @@ var symbolMap = map[string]string{
"AUDIO-PERP": "AUDIO-PERP", "AUDIO-PERP": "AUDIO-PERP",
"AUDIOUSD": "AUDIO/USD", "AUDIOUSD": "AUDIO/USD",
"AUDIOUSDT": "AUDIO/USDT", "AUDIOUSDT": "AUDIO/USDT",
"AUDUSD": "AUD/USD", "AURYUSD": "AURY/USD",
"AVAX-0625": "AVAX-0625", "AVAX-1231": "AVAX-1231",
"AVAX-PERP": "AVAX-PERP", "AVAX-PERP": "AVAX-PERP",
"AXS-PERP": "AXS-PERP", "AXS-PERP": "AXS-PERP",
"AXSUSD": "AXS/USD", "AXSUSD": "AXS/USD",
"BABA-0625": "BABA-0625", "BABA-1231": "BABA-1231",
"BABAUSD": "BABA/USD", "BABAUSD": "BABA/USD",
"BADGER-PERP": "BADGER-PERP", "BADGER-PERP": "BADGER-PERP",
"BADGERUSD": "BADGER/USD", "BADGERUSD": "BADGER/USD",
"BAL-0625": "BAL-0625", "BAL-1231": "BAL-1231",
"BAL-PERP": "BAL-PERP", "BAL-PERP": "BAL-PERP",
"BALBEARUSD": "BALBEAR/USD", "BALBEARUSD": "BALBEAR/USD",
"BALBEARUSDT": "BALBEAR/USDT", "BALBEARUSDT": "BALBEAR/USDT",
@ -93,11 +99,12 @@ var symbolMap = map[string]string{
"BANDUSD": "BAND/USD", "BANDUSD": "BAND/USD",
"BAO-PERP": "BAO-PERP", "BAO-PERP": "BAO-PERP",
"BAOUSD": "BAO/USD", "BAOUSD": "BAO/USD",
"BARUSD": "BAR/USD",
"BAT-PERP": "BAT-PERP", "BAT-PERP": "BAT-PERP",
"BATUSD": "BAT/USD", "BATUSD": "BAT/USD",
"BB-0625": "BB-0625", "BB-1231": "BB-1231",
"BBUSD": "BB/USD", "BBUSD": "BB/USD",
"BCH-0625": "BCH-0625", "BCH-1231": "BCH-1231",
"BCH-PERP": "BCH-PERP", "BCH-PERP": "BCH-PERP",
"BCHBEARUSD": "BCHBEAR/USD", "BCHBEARUSD": "BCHBEAR/USD",
"BCHBEARUSDT": "BCHBEAR/USDT", "BCHBEARUSDT": "BCHBEAR/USDT",
@ -111,11 +118,17 @@ var symbolMap = map[string]string{
"BEARSHITUSD": "BEARSHIT/USD", "BEARSHITUSD": "BEARSHIT/USD",
"BEARUSD": "BEAR/USD", "BEARUSD": "BEAR/USD",
"BEARUSDT": "BEAR/USDT", "BEARUSDT": "BEAR/USDT",
"BILI-0625": "BILI-0625", "BICOUSD": "BICO/USD",
"BILI-1231": "BILI-1231",
"BILIUSD": "BILI/USD", "BILIUSD": "BILI/USD",
"BITW-0625": "BITW-0625", "BIT-PERP": "BIT-PERP",
"BITO-1231": "BITO-1231",
"BITOUSD": "BITO/USD",
"BITUSD": "BIT/USD",
"BITW-1231": "BITW-1231",
"BITWUSD": "BITW/USD", "BITWUSD": "BITW/USD",
"BNB-0625": "BNB-0625", "BLTUSD": "BLT/USD",
"BNB-1231": "BNB-1231",
"BNB-PERP": "BNB-PERP", "BNB-PERP": "BNB-PERP",
"BNBBEARUSD": "BNBBEAR/USD", "BNBBEARUSD": "BNBBEAR/USD",
"BNBBEARUSDT": "BNBBEAR/USDT", "BNBBEARUSDT": "BNBBEAR/USDT",
@ -128,14 +141,15 @@ var symbolMap = map[string]string{
"BNBUSDT": "BNB/USDT", "BNBUSDT": "BNB/USDT",
"BNT-PERP": "BNT-PERP", "BNT-PERP": "BNT-PERP",
"BNTUSD": "BNT/USD", "BNTUSD": "BNT/USD",
"BNTX-0625": "BNTX-0625", "BNTX-1231": "BNTX-1231",
"BNTXUSD": "BNTX/USD", "BNTXUSD": "BNTX/USD",
"BOBA-PERP": "BOBA-PERP",
"BOBAUSD": "BOBA/USD",
"BOLSONARO2022": "BOLSONARO2022", "BOLSONARO2022": "BOLSONARO2022",
"BRZ-0625": "BRZ-0625",
"BRZ-PERP": "BRZ-PERP", "BRZ-PERP": "BRZ-PERP",
"BRZUSD": "BRZ/USD", "BRZUSD": "BRZ/USD",
"BRZUSDT": "BRZ/USDT", "BRZUSDT": "BRZ/USDT",
"BSV-0625": "BSV-0625", "BSV-1231": "BSV-1231",
"BSV-PERP": "BSV-PERP", "BSV-PERP": "BSV-PERP",
"BSVBEARUSD": "BSVBEAR/USD", "BSVBEARUSD": "BSVBEAR/USD",
"BSVBEARUSDT": "BSVBEAR/USDT", "BSVBEARUSDT": "BSVBEAR/USDT",
@ -143,20 +157,18 @@ var symbolMap = map[string]string{
"BSVBULLUSDT": "BSVBULL/USDT", "BSVBULLUSDT": "BSVBULL/USDT",
"BSVHALFUSD": "BSVHALF/USD", "BSVHALFUSD": "BSVHALF/USD",
"BSVHEDGEUSD": "BSVHEDGE/USD", "BSVHEDGEUSD": "BSVHEDGE/USD",
"BTC-0625": "BTC-0625", "BTC-0325": "BTC-0325",
"BTC-0924": "BTC-0924",
"BTC-1231": "BTC-1231", "BTC-1231": "BTC-1231",
"BTC-MOVE-0526": "BTC-MOVE-0526", "BTC-MOVE-1209": "BTC-MOVE-1209",
"BTC-MOVE-0527": "BTC-MOVE-0527", "BTC-MOVE-1210": "BTC-MOVE-1210",
"BTC-MOVE-2021Q2": "BTC-MOVE-2021Q2",
"BTC-MOVE-2021Q3": "BTC-MOVE-2021Q3",
"BTC-MOVE-2021Q4": "BTC-MOVE-2021Q4", "BTC-MOVE-2021Q4": "BTC-MOVE-2021Q4",
"BTC-MOVE-WK-0528": "BTC-MOVE-WK-0528", "BTC-MOVE-2022Q1": "BTC-MOVE-2022Q1",
"BTC-MOVE-WK-0604": "BTC-MOVE-WK-0604", "BTC-MOVE-2022Q2": "BTC-MOVE-2022Q2",
"BTC-MOVE-WK-0611": "BTC-MOVE-WK-0611", "BTC-MOVE-WK-1210": "BTC-MOVE-WK-1210",
"BTC-MOVE-WK-0618": "BTC-MOVE-WK-0618", "BTC-MOVE-WK-1217": "BTC-MOVE-WK-1217",
"BTC-MOVE-WK-1224": "BTC-MOVE-WK-1224",
"BTC-MOVE-WK-1231": "BTC-MOVE-WK-1231",
"BTC-PERP": "BTC-PERP", "BTC-PERP": "BTC-PERP",
"BTCAUD": "BTC/AUD",
"BTCBRZ": "BTC/BRZ", "BTCBRZ": "BTC/BRZ",
"BTCEUR": "BTC/EUR", "BTCEUR": "BTC/EUR",
"BTCTRYB": "BTC/TRYB", "BTCTRYB": "BTC/TRYB",
@ -169,20 +181,30 @@ var symbolMap = map[string]string{
"BVOLBTC": "BVOL/BTC", "BVOLBTC": "BVOL/BTC",
"BVOLUSD": "BVOL/USD", "BVOLUSD": "BVOL/USD",
"BVOLUSDT": "BVOL/USDT", "BVOLUSDT": "BVOL/USDT",
"BYND-0625": "BYND-0625", "BYND-1231": "BYND-1231",
"BYNDUSD": "BYND/USD", "BYNDUSD": "BYND/USD",
"C98-PERP": "C98-PERP",
"C98USD": "C98/USD",
"CADUSD": "CAD/USD", "CADUSD": "CAD/USD",
"CAKE-PERP": "CAKE-PERP", "CAKE-PERP": "CAKE-PERP",
"CEL-0625": "CEL-0625", "CEL-1231": "CEL-1231",
"CEL-PERP": "CEL-PERP",
"CELBTC": "CEL/BTC",
"CELO-PERP": "CELO-PERP",
"CELUSD": "CEL/USD", "CELUSD": "CEL/USD",
"CGC-0625": "CGC-0625", "CGC-1231": "CGC-1231",
"CGCUSD": "CGC/USD", "CGCUSD": "CGC/USD",
"CHZ-0625": "CHZ-0625", "CHR-PERP": "CHR-PERP",
"CHRUSD": "CHR/USD",
"CHZ-1231": "CHZ-1231",
"CHZ-PERP": "CHZ-PERP", "CHZ-PERP": "CHZ-PERP",
"CHZUSD": "CHZ/USD", "CHZUSD": "CHZ/USD",
"CHZUSDT": "CHZ/USDT", "CHZUSDT": "CHZ/USDT",
"CITYUSD": "CITY/USD",
"CLV-PERP": "CLV-PERP",
"CLVUSD": "CLV/USD",
"COINUSD": "COIN/USD", "COINUSD": "COIN/USD",
"COMP-0625": "COMP-0625", "COMP-1231": "COMP-1231",
"COMP-PERP": "COMP-PERP", "COMP-PERP": "COMP-PERP",
"COMPBEARUSD": "COMPBEAR/USD", "COMPBEARUSD": "COMPBEAR/USD",
"COMPBEARUSDT": "COMPBEAR/USDT", "COMPBEARUSDT": "COMPBEAR/USDT",
@ -195,12 +217,12 @@ var symbolMap = map[string]string{
"CONV-PERP": "CONV-PERP", "CONV-PERP": "CONV-PERP",
"CONVUSD": "CONV/USD", "CONVUSD": "CONV/USD",
"COPEUSD": "COPE/USD", "COPEUSD": "COPE/USD",
"CREAM-0625": "CREAM-0625", "CQTUSD": "CQT/USD",
"CREAM-PERP": "CREAM-PERP", "CREAM-PERP": "CREAM-PERP",
"CREAMUSD": "CREAM/USD", "CREAMUSD": "CREAM/USD",
"CREAMUSDT": "CREAM/USDT", "CREAMUSDT": "CREAM/USDT",
"CRO-PERP": "CRO-PERP", "CRO-PERP": "CRO-PERP",
"CRON-0625": "CRON-0625", "CRON-1231": "CRON-1231",
"CRONUSD": "CRON/USD", "CRONUSD": "CRON/USD",
"CROUSD": "CRO/USD", "CROUSD": "CRO/USD",
"CRV-PERP": "CRV-PERP", "CRV-PERP": "CRV-PERP",
@ -214,12 +236,14 @@ var symbolMap = map[string]string{
"CUSDTHEDGEUSD": "CUSDTHEDGE/USD", "CUSDTHEDGEUSD": "CUSDTHEDGE/USD",
"CUSDTUSD": "CUSDT/USD", "CUSDTUSD": "CUSDT/USD",
"CUSDTUSDT": "CUSDT/USDT", "CUSDTUSDT": "CUSDT/USDT",
"CVC-PERP": "CVC-PERP",
"CVCUSD": "CVC/USD",
"DAIUSD": "DAI/USD", "DAIUSD": "DAI/USD",
"DAIUSDT": "DAI/USDT", "DAIUSDT": "DAI/USDT",
"DASH-PERP": "DASH-PERP", "DASH-PERP": "DASH-PERP",
"DAWN-PERP": "DAWN-PERP", "DAWN-PERP": "DAWN-PERP",
"DAWNUSD": "DAWN/USD", "DAWNUSD": "DAWN/USD",
"DEFI-0625": "DEFI-0625", "DEFI-1231": "DEFI-1231",
"DEFI-PERP": "DEFI-PERP", "DEFI-PERP": "DEFI-PERP",
"DEFIBEARUSD": "DEFIBEAR/USD", "DEFIBEARUSD": "DEFIBEAR/USD",
"DEFIBEARUSDT": "DEFIBEAR/USDT", "DEFIBEARUSDT": "DEFIBEAR/USDT",
@ -229,12 +253,14 @@ var symbolMap = map[string]string{
"DEFIHEDGEUSD": "DEFIHEDGE/USD", "DEFIHEDGEUSD": "DEFIHEDGE/USD",
"DENT-PERP": "DENT-PERP", "DENT-PERP": "DENT-PERP",
"DENTUSD": "DENT/USD", "DENTUSD": "DENT/USD",
"DMG-PERP": "DMG-PERP", "DFLUSD": "DFL/USD",
"DKNG-1231": "DKNG-1231",
"DKNGUSD": "DKNG/USD",
"DMGUSD": "DMG/USD", "DMGUSD": "DMG/USD",
"DMGUSDT": "DMG/USDT", "DMGUSDT": "DMG/USDT",
"DODO-PERP": "DODO-PERP", "DODO-PERP": "DODO-PERP",
"DODOUSD": "DODO/USD", "DODOUSD": "DODO/USD",
"DOGE-0625": "DOGE-0625", "DOGE-1231": "DOGE-1231",
"DOGE-PERP": "DOGE-PERP", "DOGE-PERP": "DOGE-PERP",
"DOGEBEAR2021USD": "DOGEBEAR2021/USD", "DOGEBEAR2021USD": "DOGEBEAR2021/USD",
"DOGEBTC": "DOGE/BTC", "DOGEBTC": "DOGE/BTC",
@ -243,19 +269,26 @@ var symbolMap = map[string]string{
"DOGEHEDGEUSD": "DOGEHEDGE/USD", "DOGEHEDGEUSD": "DOGEHEDGE/USD",
"DOGEUSD": "DOGE/USD", "DOGEUSD": "DOGE/USD",
"DOGEUSDT": "DOGE/USDT", "DOGEUSDT": "DOGE/USDT",
"DOT-0625": "DOT-0625", "DOT-1231": "DOT-1231",
"DOT-PERP": "DOT-PERP", "DOT-PERP": "DOT-PERP",
"DRGN-0625": "DRGN-0625", "DRGN-1231": "DRGN-1231",
"DRGN-PERP": "DRGN-PERP", "DRGN-PERP": "DRGN-PERP",
"DRGNBEARUSD": "DRGNBEAR/USD", "DRGNBEARUSD": "DRGNBEAR/USD",
"DRGNBULLUSD": "DRGNBULL/USD", "DRGNBULLUSD": "DRGNBULL/USD",
"DRGNHALFUSD": "DRGNHALF/USD", "DRGNHALFUSD": "DRGNHALF/USD",
"DRGNHEDGEUSD": "DRGNHEDGE/USD", "DRGNHEDGEUSD": "DRGNHEDGE/USD",
"DYDX-PERP": "DYDX-PERP",
"DYDXUSD": "DYDX/USD",
"EDEN-1231": "EDEN-1231",
"EDEN-PERP": "EDEN-PERP",
"EDENUSD": "EDEN/USD",
"EGLD-PERP": "EGLD-PERP", "EGLD-PERP": "EGLD-PERP",
"EMBUSD": "EMB/USD", "EMBUSD": "EMB/USD",
"ENJ-PERP": "ENJ-PERP", "ENJ-PERP": "ENJ-PERP",
"ENJUSD": "ENJ/USD", "ENJUSD": "ENJ/USD",
"EOS-0625": "EOS-0625", "ENS-PERP": "ENS-PERP",
"ENSUSD": "ENS/USD",
"EOS-1231": "EOS-1231",
"EOS-PERP": "EOS-PERP", "EOS-PERP": "EOS-PERP",
"EOSBEARUSD": "EOSBEAR/USD", "EOSBEARUSD": "EOSBEAR/USD",
"EOSBEARUSDT": "EOSBEAR/USDT", "EOSBEARUSDT": "EOSBEAR/USDT",
@ -268,18 +301,16 @@ var symbolMap = map[string]string{
"ETCBULLUSD": "ETCBULL/USD", "ETCBULLUSD": "ETCBULL/USD",
"ETCHALFUSD": "ETCHALF/USD", "ETCHALFUSD": "ETCHALF/USD",
"ETCHEDGEUSD": "ETCHEDGE/USD", "ETCHEDGEUSD": "ETCHEDGE/USD",
"ETH-0625": "ETH-0625", "ETH-0325": "ETH-0325",
"ETH-0924": "ETH-0924",
"ETH-1231": "ETH-1231", "ETH-1231": "ETH-1231",
"ETH-PERP": "ETH-PERP", "ETH-PERP": "ETH-PERP",
"ETHAUD": "ETH/AUD",
"ETHBEARUSD": "ETHBEAR/USD", "ETHBEARUSD": "ETHBEAR/USD",
"ETHBEARUSDT": "ETHBEAR/USDT", "ETHBEARUSDT": "ETHBEAR/USDT",
"ETHBRZ": "ETH/BRZ", "ETHBRZ": "ETH/BRZ",
"ETHBTC": "ETH/BTC", "ETHBTC": "ETH/BTC",
"ETHBULLUSD": "ETHBULL/USD", "ETHBULLUSD": "ETHBULL/USD",
"ETHBULLUSDT": "ETHBULL/USDT", "ETHBULLUSDT": "ETHBULL/USDT",
"ETHE-0625": "ETHE-0625", "ETHE-1231": "ETHE-1231",
"ETHEUR": "ETH/EUR", "ETHEUR": "ETH/EUR",
"ETHEUSD": "ETHE/USD", "ETHEUSD": "ETHE/USD",
"ETHHALFUSD": "ETHHALF/USD", "ETHHALFUSD": "ETHHALF/USD",
@ -287,18 +318,18 @@ var symbolMap = map[string]string{
"ETHUSD": "ETH/USD", "ETHUSD": "ETH/USD",
"ETHUSDT": "ETH/USDT", "ETHUSDT": "ETH/USDT",
"EURUSD": "EUR/USD", "EURUSD": "EUR/USD",
"EXCH-0625": "EXCH-0625", "EXCH-1231": "EXCH-1231",
"EXCH-PERP": "EXCH-PERP", "EXCH-PERP": "EXCH-PERP",
"EXCHBEARUSD": "EXCHBEAR/USD", "EXCHBEARUSD": "EXCHBEAR/USD",
"EXCHBULLUSD": "EXCHBULL/USD", "EXCHBULLUSD": "EXCHBULL/USD",
"EXCHHALFUSD": "EXCHHALF/USD", "EXCHHALFUSD": "EXCHHALF/USD",
"EXCHHEDGEUSD": "EXCHHEDGE/USD", "EXCHHEDGEUSD": "EXCHHEDGE/USD",
"FB-0625": "FB-0625", "FB-1231": "FB-1231",
"FBUSD": "FB/USD", "FBUSD": "FB/USD",
"FIDA-PERP": "FIDA-PERP", "FIDA-PERP": "FIDA-PERP",
"FIDAUSD": "FIDA/USD", "FIDAUSD": "FIDA/USD",
"FIDAUSDT": "FIDA/USDT", "FIDAUSDT": "FIDA/USDT",
"FIL-0625": "FIL-0625", "FIL-1231": "FIL-1231",
"FIL-PERP": "FIL-PERP", "FIL-PERP": "FIL-PERP",
"FLM-PERP": "FLM-PERP", "FLM-PERP": "FLM-PERP",
"FLOW-PERP": "FLOW-PERP", "FLOW-PERP": "FLOW-PERP",
@ -310,21 +341,26 @@ var symbolMap = map[string]string{
"FTTBTC": "FTT/BTC", "FTTBTC": "FTT/BTC",
"FTTUSD": "FTT/USD", "FTTUSD": "FTT/USD",
"FTTUSDT": "FTT/USDT", "FTTUSDT": "FTT/USDT",
"GALA-PERP": "GALA-PERP",
"GALAUSD": "GALA/USD",
"GALUSD": "GAL/USD",
"GBPUSD": "GBP/USD", "GBPUSD": "GBP/USD",
"GBTC-0625": "GBTC-0625", "GBTC-1231": "GBTC-1231",
"GBTCUSD": "GBTC/USD", "GBTCUSD": "GBTC/USD",
"GDX-0625": "GDX-0625", "GDX-1231": "GDX-1231",
"GDXJ-0625": "GDXJ-0625", "GDXJ-1231": "GDXJ-1231",
"GDXJUSD": "GDXJ/USD", "GDXJUSD": "GDXJ/USD",
"GDXUSD": "GDX/USD", "GDXUSD": "GDX/USD",
"GLD-0625": "GLD-0625", "GENEUSD": "GENE/USD",
"GLD-1231": "GLD-1231",
"GLDUSD": "GLD/USD", "GLDUSD": "GLD/USD",
"GLXYUSD": "GLXY/USD", "GLXYUSD": "GLXY/USD",
"GME-0625": "GME-0625", "GME-1231": "GME-1231",
"GMEUSD": "GME/USD", "GMEUSD": "GME/USD",
"GOOGL-0625": "GOOGL-0625", "GODSUSD": "GODS/USD",
"GOOGL-1231": "GOOGL-1231",
"GOOGLUSD": "GOOGL/USD", "GOOGLUSD": "GOOGL/USD",
"GRT-0625": "GRT-0625", "GRT-1231": "GRT-1231",
"GRT-PERP": "GRT-PERP", "GRT-PERP": "GRT-PERP",
"GRTBEARUSD": "GRTBEAR/USD", "GRTBEARUSD": "GRTBEAR/USD",
"GRTBULLUSD": "GRTBULL/USD", "GRTBULLUSD": "GRTBULL/USD",
@ -337,6 +373,7 @@ var symbolMap = map[string]string{
"HEDGEUSD": "HEDGE/USD", "HEDGEUSD": "HEDGE/USD",
"HGETUSD": "HGET/USD", "HGETUSD": "HGET/USD",
"HGETUSDT": "HGET/USDT", "HGETUSDT": "HGET/USDT",
"HMTUSD": "HMT/USD",
"HNT-PERP": "HNT-PERP", "HNT-PERP": "HNT-PERP",
"HNTUSD": "HNT/USD", "HNTUSD": "HNT/USD",
"HNTUSDT": "HNT/USDT", "HNTUSDT": "HNT/USDT",
@ -358,7 +395,11 @@ var symbolMap = map[string]string{
"IBVOLUSD": "IBVOL/USD", "IBVOLUSD": "IBVOL/USD",
"IBVOLUSDT": "IBVOL/USDT", "IBVOLUSDT": "IBVOL/USDT",
"ICP-PERP": "ICP-PERP", "ICP-PERP": "ICP-PERP",
"ICX-PERP": "ICX-PERP",
"IMXUSD": "IMX/USD",
"INTERUSD": "INTER/USD",
"IOTA-PERP": "IOTA-PERP", "IOTA-PERP": "IOTA-PERP",
"JETUSD": "JET/USD",
"JSTUSD": "JST/USD", "JSTUSD": "JST/USD",
"KAVA-PERP": "KAVA-PERP", "KAVA-PERP": "KAVA-PERP",
"KIN-PERP": "KIN-PERP", "KIN-PERP": "KIN-PERP",
@ -372,8 +413,9 @@ var symbolMap = map[string]string{
"KNCHEDGEUSD": "KNCHEDGE/USD", "KNCHEDGEUSD": "KNCHEDGE/USD",
"KNCUSD": "KNC/USD", "KNCUSD": "KNC/USD",
"KNCUSDT": "KNC/USDT", "KNCUSDT": "KNC/USDT",
"KSHIB-PERP": "KSHIB-PERP",
"KSHIBUSD": "KSHIB/USD",
"KSM-PERP": "KSM-PERP", "KSM-PERP": "KSM-PERP",
"LB-0812": "LB-0812",
"LEO-PERP": "LEO-PERP", "LEO-PERP": "LEO-PERP",
"LEOBEARUSD": "LEOBEAR/USD", "LEOBEARUSD": "LEOBEAR/USD",
"LEOBULLUSD": "LEOBULL/USD", "LEOBULLUSD": "LEOBULL/USD",
@ -382,7 +424,7 @@ var symbolMap = map[string]string{
"LEOUSD": "LEO/USD", "LEOUSD": "LEO/USD",
"LINA-PERP": "LINA-PERP", "LINA-PERP": "LINA-PERP",
"LINAUSD": "LINA/USD", "LINAUSD": "LINA/USD",
"LINK-0625": "LINK-0625", "LINK-1231": "LINK-1231",
"LINK-PERP": "LINK-PERP", "LINK-PERP": "LINK-PERP",
"LINKBEARUSD": "LINKBEAR/USD", "LINKBEARUSD": "LINKBEAR/USD",
"LINKBEARUSDT": "LINKBEAR/USDT", "LINKBEARUSDT": "LINKBEAR/USDT",
@ -395,7 +437,7 @@ var symbolMap = map[string]string{
"LINKUSDT": "LINK/USDT", "LINKUSDT": "LINK/USDT",
"LRC-PERP": "LRC-PERP", "LRC-PERP": "LRC-PERP",
"LRCUSD": "LRC/USD", "LRCUSD": "LRC/USD",
"LTC-0625": "LTC-0625", "LTC-1231": "LTC-1231",
"LTC-PERP": "LTC-PERP", "LTC-PERP": "LTC-PERP",
"LTCBEARUSD": "LTCBEAR/USD", "LTCBEARUSD": "LTCBEAR/USD",
"LTCBEARUSDT": "LTCBEAR/USDT", "LTCBEARUSDT": "LTCBEAR/USDT",
@ -409,6 +451,8 @@ var symbolMap = map[string]string{
"LUAUSD": "LUA/USD", "LUAUSD": "LUA/USD",
"LUAUSDT": "LUA/USDT", "LUAUSDT": "LUA/USDT",
"LUNA-PERP": "LUNA-PERP", "LUNA-PERP": "LUNA-PERP",
"MANA-PERP": "MANA-PERP",
"MANAUSD": "MANA/USD",
"MAPS-PERP": "MAPS-PERP", "MAPS-PERP": "MAPS-PERP",
"MAPSUSD": "MAPS/USD", "MAPSUSD": "MAPS/USD",
"MAPSUSDT": "MAPS/USDT", "MAPSUSDT": "MAPS/USDT",
@ -416,15 +460,18 @@ var symbolMap = map[string]string{
"MATHUSDT": "MATH/USDT", "MATHUSDT": "MATH/USDT",
"MATIC-PERP": "MATIC-PERP", "MATIC-PERP": "MATIC-PERP",
"MATICBEAR2021USD": "MATICBEAR2021/USD", "MATICBEAR2021USD": "MATICBEAR2021/USD",
"MATICBTC": "MATIC/BTC",
"MATICBULLUSD": "MATICBULL/USD", "MATICBULLUSD": "MATICBULL/USD",
"MATICHALFUSD": "MATICHALF/USD", "MATICHALFUSD": "MATICHALF/USD",
"MATICHEDGEUSD": "MATICHEDGE/USD", "MATICHEDGEUSD": "MATICHEDGE/USD",
"MATICUSD": "MATIC/USD", "MATICUSD": "MATIC/USD",
"MCB-PERP": "MCB-PERP",
"MCBUSD": "MCB/USD",
"MEDIA-PERP": "MEDIA-PERP", "MEDIA-PERP": "MEDIA-PERP",
"MEDIAUSD": "MEDIA/USD", "MEDIAUSD": "MEDIA/USD",
"MER-PERP": "MER-PERP", "MER-PERP": "MER-PERP",
"MERUSD": "MER/USD", "MERUSD": "MER/USD",
"MID-0625": "MID-0625", "MID-1231": "MID-1231",
"MID-PERP": "MID-PERP", "MID-PERP": "MID-PERP",
"MIDBEARUSD": "MIDBEAR/USD", "MIDBEARUSD": "MIDBEAR/USD",
"MIDBULLUSD": "MIDBULL/USD", "MIDBULLUSD": "MIDBULL/USD",
@ -435,45 +482,49 @@ var symbolMap = map[string]string{
"MKRBULLUSD": "MKRBULL/USD", "MKRBULLUSD": "MKRBULL/USD",
"MKRUSD": "MKR/USD", "MKRUSD": "MKR/USD",
"MKRUSDT": "MKR/USDT", "MKRUSDT": "MKR/USDT",
"MNGO-PERP": "MNGO-PERP",
"MNGOUSD": "MNGO/USD",
"MOBUSD": "MOB/USD", "MOBUSD": "MOB/USD",
"MOBUSDT": "MOB/USDT", "MOBUSDT": "MOB/USDT",
"MRNA-0625": "MRNA-0625", "MRNA-1231": "MRNA-1231",
"MRNAUSD": "MRNA/USD", "MRNAUSD": "MRNA/USD",
"MSTR-0625": "MSTR-0625", "MSOLUSD": "MSOL/USD",
"MSTR-1231": "MSTR-1231",
"MSTRUSD": "MSTR/USD", "MSTRUSD": "MSTR/USD",
"MTA-PERP": "MTA-PERP", "MTA-PERP": "MTA-PERP",
"MTAUSD": "MTA/USD", "MTAUSD": "MTA/USD",
"MTAUSDT": "MTA/USDT", "MTAUSDT": "MTA/USDT",
"MTL-PERP": "MTL-PERP", "MTL-PERP": "MTL-PERP",
"MTLUSD": "MTL/USD", "MTLUSD": "MTL/USD",
"MVDA10-PERP": "MVDA10-PERP",
"MVDA25-PERP": "MVDA25-PERP",
"NEAR-PERP": "NEAR-PERP", "NEAR-PERP": "NEAR-PERP",
"NEO-PERP": "NEO-PERP", "NEO-PERP": "NEO-PERP",
"NFLX-0625": "NFLX-0625", "NFLX-1231": "NFLX-1231",
"NFLXUSD": "NFLX/USD", "NFLXUSD": "NFLX/USD",
"NIO-0625": "NIO-0625", "NIO-1231": "NIO-1231",
"NIOUSD": "NIO/USD", "NIOUSD": "NIO/USD",
"NOK-0625": "NOK-0625", "NOK-1231": "NOK-1231",
"NOKUSD": "NOK/USD", "NOKUSD": "NOK/USD",
"NVDA-0625": "NVDA-0625", "NVDA-1231": "NVDA-1231",
"NVDAUSD": "NVDA/USD", "NVDAUSD": "NVDA/USD",
"OKB-0625": "OKB-0625", "OKB-1231": "OKB-1231",
"OKB-PERP": "OKB-PERP", "OKB-PERP": "OKB-PERP",
"OKBBEARUSD": "OKBBEAR/USD", "OKBBEARUSD": "OKBBEAR/USD",
"OKBBULLUSD": "OKBBULL/USD", "OKBBULLUSD": "OKBBULL/USD",
"OKBHALFUSD": "OKBHALF/USD", "OKBHALFUSD": "OKBHALF/USD",
"OKBHEDGEUSD": "OKBHEDGE/USD", "OKBHEDGEUSD": "OKBHEDGE/USD",
"OKBUSD": "OKB/USD", "OKBUSD": "OKB/USD",
"OLY2021": "OLY2021", "OMG-1231": "OMG-1231",
"OMG-0625": "OMG-0625",
"OMG-PERP": "OMG-PERP", "OMG-PERP": "OMG-PERP",
"OMGUSD": "OMG/USD", "OMGUSD": "OMG/USD",
"ONE-PERP": "ONE-PERP",
"ONT-PERP": "ONT-PERP", "ONT-PERP": "ONT-PERP",
"ORBS-PERP": "ORBS-PERP", "ORBS-PERP": "ORBS-PERP",
"ORBSUSD": "ORBS/USD", "ORBSUSD": "ORBS/USD",
"OXY-PERP": "OXY-PERP", "OXY-PERP": "OXY-PERP",
"OXYUSD": "OXY/USD", "OXYUSD": "OXY/USD",
"OXYUSDT": "OXY/USDT", "OXYUSDT": "OXY/USDT",
"PAXG-0625": "PAXG-0625",
"PAXG-PERP": "PAXG-PERP", "PAXG-PERP": "PAXG-PERP",
"PAXGBEARUSD": "PAXGBEAR/USD", "PAXGBEARUSD": "PAXGBEAR/USD",
"PAXGBULLUSD": "PAXGBULL/USD", "PAXGBULLUSD": "PAXGBULL/USD",
@ -481,13 +532,16 @@ var symbolMap = map[string]string{
"PAXGHEDGEUSD": "PAXGHEDGE/USD", "PAXGHEDGEUSD": "PAXGHEDGE/USD",
"PAXGUSD": "PAXG/USD", "PAXGUSD": "PAXG/USD",
"PAXGUSDT": "PAXG/USDT", "PAXGUSDT": "PAXG/USDT",
"PENN-0625": "PENN-0625", "PENN-1231": "PENN-1231",
"PENNUSD": "PENN/USD", "PENNUSD": "PENN/USD",
"PERP-PERP": "PERP-PERP", "PERP-PERP": "PERP-PERP",
"PERPUSD": "PERP/USD", "PERPUSD": "PERP/USD",
"PFE-0625": "PFE-0625", "PFE-1231": "PFE-1231",
"PFEUSD": "PFE/USD", "PFEUSD": "PFE/USD",
"PRIV-0625": "PRIV-0625", "POLIS-PERP": "POLIS-PERP",
"POLISUSD": "POLIS/USD",
"PORTUSD": "PORT/USD",
"PRIV-1231": "PRIV-1231",
"PRIV-PERP": "PRIV-PERP", "PRIV-PERP": "PRIV-PERP",
"PRIVBEARUSD": "PRIVBEAR/USD", "PRIVBEARUSD": "PRIVBEAR/USD",
"PRIVBULLUSD": "PRIVBULL/USD", "PRIVBULLUSD": "PRIVBULL/USD",
@ -495,20 +549,24 @@ var symbolMap = map[string]string{
"PRIVHEDGEUSD": "PRIVHEDGE/USD", "PRIVHEDGEUSD": "PRIVHEDGE/USD",
"PROM-PERP": "PROM-PERP", "PROM-PERP": "PROM-PERP",
"PROMUSD": "PROM/USD", "PROMUSD": "PROM/USD",
"PSGUSD": "PSG/USD",
"PTUUSD": "PTU/USD",
"PUNDIX-PERP": "PUNDIX-PERP", "PUNDIX-PERP": "PUNDIX-PERP",
"PUNDIXUSD": "PUNDIX/USD", "PUNDIXUSD": "PUNDIX/USD",
"PYPL-0625": "PYPL-0625", "PYPL-1231": "PYPL-1231",
"PYPLUSD": "PYPL/USD", "PYPLUSD": "PYPL/USD",
"QTUM-PERP": "QTUM-PERP", "QTUM-PERP": "QTUM-PERP",
"RAMP-PERP": "RAMP-PERP", "RAMP-PERP": "RAMP-PERP",
"RAMPUSD": "RAMP/USD", "RAMPUSD": "RAMP/USD",
"RAY-PERP": "RAY-PERP", "RAY-PERP": "RAY-PERP",
"RAYUSD": "RAY/USD", "RAYUSD": "RAY/USD",
"REEF-0625": "REEF-0625", "REEF-1231": "REEF-1231",
"REEF-PERP": "REEF-PERP", "REEF-PERP": "REEF-PERP",
"REEFUSD": "REEF/USD", "REEFUSD": "REEF/USD",
"REN-PERP": "REN-PERP", "REN-PERP": "REN-PERP",
"RENUSD": "REN/USD", "RENUSD": "REN/USD",
"RNDR-PERP": "RNDR-PERP",
"RNDRUSD": "RNDR/USD",
"ROOK-PERP": "ROOK-PERP", "ROOK-PERP": "ROOK-PERP",
"ROOKUSD": "ROOK/USD", "ROOKUSD": "ROOK/USD",
"ROOKUSDT": "ROOK/USDT", "ROOKUSDT": "ROOK/USDT",
@ -522,61 +580,73 @@ var symbolMap = map[string]string{
"SC-PERP": "SC-PERP", "SC-PERP": "SC-PERP",
"SECO-PERP": "SECO-PERP", "SECO-PERP": "SECO-PERP",
"SECOUSD": "SECO/USD", "SECOUSD": "SECO/USD",
"SGDUSD": "SGD/USD",
"SHIB-PERP": "SHIB-PERP", "SHIB-PERP": "SHIB-PERP",
"SHIBUSD": "SHIB/USD", "SHIBUSD": "SHIB/USD",
"SHIT-0625": "SHIT-0625", "SHIT-1231": "SHIT-1231",
"SHIT-PERP": "SHIT-PERP", "SHIT-PERP": "SHIT-PERP",
"SKL-PERP": "SKL-PERP", "SKL-PERP": "SKL-PERP",
"SKLUSD": "SKL/USD", "SKLUSD": "SKL/USD",
"SLV-0625": "SLV-0625", "SLNDUSD": "SLND/USD",
"SLP-PERP": "SLP-PERP",
"SLPUSD": "SLP/USD",
"SLRSUSD": "SLRS/USD",
"SLV-1231": "SLV-1231",
"SLVUSD": "SLV/USD", "SLVUSD": "SLV/USD",
"SNX-PERP": "SNX-PERP", "SNX-PERP": "SNX-PERP",
"SNXUSD": "SNX/USD", "SNXUSD": "SNX/USD",
"SOL-0625": "SOL-0625", "SNYUSD": "SNY/USD",
"SOL-1231": "SOL-1231",
"SOL-PERP": "SOL-PERP", "SOL-PERP": "SOL-PERP",
"SOLBTC": "SOL/BTC", "SOLBTC": "SOL/BTC",
"SOLUSD": "SOL/USD", "SOLUSD": "SOL/USD",
"SOLUSDT": "SOL/USDT", "SOLUSDT": "SOL/USDT",
"SPY-0625": "SPY-0625", "SPELL-PERP": "SPELL-PERP",
"SPELLUSD": "SPELL/USD",
"SPY-1231": "SPY-1231",
"SPYUSD": "SPY/USD", "SPYUSD": "SPY/USD",
"SQ-0625": "SQ-0625", "SQ-1231": "SQ-1231",
"SQUSD": "SQ/USD", "SQUSD": "SQ/USD",
"SRM-PERP": "SRM-PERP", "SRM-PERP": "SRM-PERP",
"SRMUSD": "SRM/USD", "SRMUSD": "SRM/USD",
"SRMUSDT": "SRM/USDT", "SRMUSDT": "SRM/USDT",
"SRN-PERP": "SRN-PERP", "SRN-PERP": "SRN-PERP",
"STARSUSD": "STARS/USD",
"STEP-PERP": "STEP-PERP", "STEP-PERP": "STEP-PERP",
"STEPUSD": "STEP/USD", "STEPUSD": "STEP/USD",
"STETHUSD": "STETH/USD",
"STMX-PERP": "STMX-PERP", "STMX-PERP": "STMX-PERP",
"STMXUSD": "STMX/USD", "STMXUSD": "STMX/USD",
"STORJ-PERP": "STORJ-PERP", "STORJ-PERP": "STORJ-PERP",
"STORJUSD": "STORJ/USD", "STORJUSD": "STORJ/USD",
"STSOLUSD": "STSOL/USD",
"STX-PERP": "STX-PERP", "STX-PERP": "STX-PERP",
"SUNUSD": "SUN/USD", "SUNUSD": "SUN/USD",
"SUSHI-0625": "SUSHI-0625", "SUSHI-1231": "SUSHI-1231",
"SUSHI-PERP": "SUSHI-PERP", "SUSHI-PERP": "SUSHI-PERP",
"SUSHIBEARUSD": "SUSHIBEAR/USD", "SUSHIBEARUSD": "SUSHIBEAR/USD",
"SUSHIBTC": "SUSHI/BTC", "SUSHIBTC": "SUSHI/BTC",
"SUSHIBULLUSD": "SUSHIBULL/USD", "SUSHIBULLUSD": "SUSHIBULL/USD",
"SUSHIUSD": "SUSHI/USD", "SUSHIUSD": "SUSHI/USD",
"SUSHIUSDT": "SUSHI/USDT", "SUSHIUSDT": "SUSHI/USDT",
"SXP-0625": "SXP-0625", "SXP-1231": "SXP-1231",
"SXP-PERP": "SXP-PERP", "SXP-PERP": "SXP-PERP",
"SXPBEARUSD": "SXPBEAR/USD", "SXPBEARUSD": "SXPBEAR/USD",
"SXPBTC": "SXP/BTC",
"SXPBULLUSD": "SXPBULL/USD", "SXPBULLUSD": "SXPBULL/USD",
"SXPHALFUSD": "SXPHALF/USD", "SXPHALFUSD": "SXPHALF/USD",
"SXPHALFUSDT": "SXPHALF/USDT", "SXPHALFUSDT": "SXPHALF/USDT",
"SXPHEDGEUSD": "SXPHEDGE/USD", "SXPHEDGEUSD": "SXPHEDGE/USD",
"SXPUSD": "SXP/USD", "SXPUSD": "SXP/USD",
"SXPUSDT": "SXP/USDT", "SXPUSDT": "SXP/USDT",
"THETA-0625": "THETA-0625", "THETA-1231": "THETA-1231",
"THETA-PERP": "THETA-PERP", "THETA-PERP": "THETA-PERP",
"THETABEARUSD": "THETABEAR/USD", "THETABEARUSD": "THETABEAR/USD",
"THETABULLUSD": "THETABULL/USD", "THETABULLUSD": "THETABULL/USD",
"THETAHALFUSD": "THETAHALF/USD", "THETAHALFUSD": "THETAHALF/USD",
"THETAHEDGEUSD": "THETAHEDGE/USD", "THETAHEDGEUSD": "THETAHEDGE/USD",
"TLRY-0625": "TLRY-0625", "TLM-PERP": "TLM-PERP",
"TLMUSD": "TLM/USD",
"TLRY-1231": "TLRY-1231",
"TLRYUSD": "TLRY/USD", "TLRYUSD": "TLRY/USD",
"TOMO-PERP": "TOMO-PERP", "TOMO-PERP": "TOMO-PERP",
"TOMOBEAR2021USD": "TOMOBEAR2021/USD", "TOMOBEAR2021USD": "TOMOBEAR2021/USD",
@ -585,55 +655,58 @@ var symbolMap = map[string]string{
"TOMOHEDGEUSD": "TOMOHEDGE/USD", "TOMOHEDGEUSD": "TOMOHEDGE/USD",
"TOMOUSD": "TOMO/USD", "TOMOUSD": "TOMO/USD",
"TOMOUSDT": "TOMO/USDT", "TOMOUSDT": "TOMO/USDT",
"TRU-0625": "TRU-0625", "TONCOIN-PERP": "TONCOIN-PERP",
"TONCOINUSD": "TONCOIN/USD",
"TRU-PERP": "TRU-PERP", "TRU-PERP": "TRU-PERP",
"TRUMP2024": "TRUMP2024", "TRUMP2024": "TRUMP2024",
"TRUUSD": "TRU/USD", "TRUUSD": "TRU/USD",
"TRUUSDT": "TRU/USDT", "TRUUSDT": "TRU/USDT",
"TRX-0625": "TRX-0625", "TRX-1231": "TRX-1231",
"TRX-PERP": "TRX-PERP", "TRX-PERP": "TRX-PERP",
"TRXBEARUSD": "TRXBEAR/USD", "TRXBEARUSD": "TRXBEAR/USD",
"TRXBTC": "TRX/BTC",
"TRXBULLUSD": "TRXBULL/USD", "TRXBULLUSD": "TRXBULL/USD",
"TRXHALFUSD": "TRXHALF/USD", "TRXHALFUSD": "TRXHALF/USD",
"TRXHEDGEUSD": "TRXHEDGE/USD", "TRXHEDGEUSD": "TRXHEDGE/USD",
"TRXUSD": "TRX/USD", "TRXUSD": "TRX/USD",
"TRXUSDT": "TRX/USDT", "TRXUSDT": "TRX/USDT",
"TRYB-0625": "TRYB-0625",
"TRYB-PERP": "TRYB-PERP", "TRYB-PERP": "TRYB-PERP",
"TRYBBEARUSD": "TRYBBEAR/USD", "TRYBBEARUSD": "TRYBBEAR/USD",
"TRYBBULLUSD": "TRYBBULL/USD", "TRYBBULLUSD": "TRYBBULL/USD",
"TRYBHALFUSD": "TRYBHALF/USD", "TRYBHALFUSD": "TRYBHALF/USD",
"TRYBHEDGEUSD": "TRYBHEDGE/USD", "TRYBHEDGEUSD": "TRYBHEDGE/USD",
"TRYBUSD": "TRYB/USD", "TRYBUSD": "TRYB/USD",
"TSLA-0625": "TSLA-0625", "TSLA-1231": "TSLA-1231",
"TSLABTC": "TSLA/BTC", "TSLABTC": "TSLA/BTC",
"TSLADOGE": "TSLA/DOGE", "TSLADOGE": "TSLA/DOGE",
"TSLAUSD": "TSLA/USD", "TSLAUSD": "TSLA/USD",
"TSM-0625": "TSM-0625", "TSM-1231": "TSM-1231",
"TSMUSD": "TSM/USD", "TSMUSD": "TSM/USD",
"TWTR-0625": "TWTR-0625", "TULIP-PERP": "TULIP-PERP",
"TULIPUSD": "TULIP/USD",
"TWTR-1231": "TWTR-1231",
"TWTRUSD": "TWTR/USD", "TWTRUSD": "TWTR/USD",
"UBER-0625": "UBER-0625", "UBER-1231": "UBER-1231",
"UBERUSD": "UBER/USD", "UBERUSD": "UBER/USD",
"UBXTUSD": "UBXT/USD", "UBXTUSD": "UBXT/USD",
"UBXTUSDT": "UBXT/USDT", "UBXTUSDT": "UBXT/USDT",
"UNI-0625": "UNI-0625", "UNI-1231": "UNI-1231",
"UNI-PERP": "UNI-PERP", "UNI-PERP": "UNI-PERP",
"UNIBTC": "UNI/BTC", "UNIBTC": "UNI/BTC",
"UNISWAP-0625": "UNISWAP-0625", "UNISWAP-1231": "UNISWAP-1231",
"UNISWAP-PERP": "UNISWAP-PERP", "UNISWAP-PERP": "UNISWAP-PERP",
"UNISWAPBEARUSD": "UNISWAPBEAR/USD", "UNISWAPBEARUSD": "UNISWAPBEAR/USD",
"UNISWAPBULLUSD": "UNISWAPBULL/USD", "UNISWAPBULLUSD": "UNISWAPBULL/USD",
"UNIUSD": "UNI/USD", "UNIUSD": "UNI/USD",
"UNIUSDT": "UNI/USDT", "UNIUSDT": "UNI/USDT",
"USDT-0625": "USDT-0625", "USDT-1231": "USDT-1231",
"USDT-PERP": "USDT-PERP", "USDT-PERP": "USDT-PERP",
"USDTBEARUSD": "USDTBEAR/USD", "USDTBEARUSD": "USDTBEAR/USD",
"USDTBULLUSD": "USDTBULL/USD", "USDTBULLUSD": "USDTBULL/USD",
"USDTHALFUSD": "USDTHALF/USD", "USDTHALFUSD": "USDTHALF/USD",
"USDTHEDGEUSD": "USDTHEDGE/USD", "USDTHEDGEUSD": "USDTHEDGE/USD",
"USDTUSD": "USDT/USD", "USDTUSD": "USDT/USD",
"USO-0625": "USO-0625", "USO-1231": "USO-1231",
"USOUSD": "USO/USD", "USOUSD": "USO/USD",
"VET-PERP": "VET-PERP", "VET-PERP": "VET-PERP",
"VETBEARUSD": "VETBEAR/USD", "VETBEARUSD": "VETBEAR/USD",
@ -641,14 +714,17 @@ var symbolMap = map[string]string{
"VETBULLUSD": "VETBULL/USD", "VETBULLUSD": "VETBULL/USD",
"VETBULLUSDT": "VETBULL/USDT", "VETBULLUSDT": "VETBULL/USDT",
"VETHEDGEUSD": "VETHEDGE/USD", "VETHEDGEUSD": "VETHEDGE/USD",
"WAVES-0625": "WAVES-0625", "VGXUSD": "VGX/USD",
"WAVES-1231": "WAVES-1231",
"WAVES-PERP": "WAVES-PERP", "WAVES-PERP": "WAVES-PERP",
"WAVESUSD": "WAVES/USD", "WAVESUSD": "WAVES/USD",
"WBTCBTC": "WBTC/BTC",
"WBTCUSD": "WBTC/USD", "WBTCUSD": "WBTC/USD",
"WNDRUSD": "WNDR/USD",
"WRXUSD": "WRX/USD", "WRXUSD": "WRX/USD",
"WRXUSDT": "WRX/USDT", "WRXUSDT": "WRX/USDT",
"WSB-0625": "WSB-0625", "WSB-1231": "WSB-1231",
"XAUT-0625": "XAUT-0625", "XAUT-1231": "XAUT-1231",
"XAUT-PERP": "XAUT-PERP", "XAUT-PERP": "XAUT-PERP",
"XAUTBEARUSD": "XAUTBEAR/USD", "XAUTBEARUSD": "XAUTBEAR/USD",
"XAUTBULLUSD": "XAUTBULL/USD", "XAUTBULLUSD": "XAUTBULL/USD",
@ -661,7 +737,7 @@ var symbolMap = map[string]string{
"XLMBEARUSD": "XLMBEAR/USD", "XLMBEARUSD": "XLMBEAR/USD",
"XLMBULLUSD": "XLMBULL/USD", "XLMBULLUSD": "XLMBULL/USD",
"XMR-PERP": "XMR-PERP", "XMR-PERP": "XMR-PERP",
"XRP-0625": "XRP-0625", "XRP-1231": "XRP-1231",
"XRP-PERP": "XRP-PERP", "XRP-PERP": "XRP-PERP",
"XRPBEARUSD": "XRPBEAR/USD", "XRPBEARUSD": "XRPBEAR/USD",
"XRPBEARUSDT": "XRPBEAR/USDT", "XRPBEARUSDT": "XRPBEAR/USDT",
@ -672,7 +748,7 @@ var symbolMap = map[string]string{
"XRPHEDGEUSD": "XRPHEDGE/USD", "XRPHEDGEUSD": "XRPHEDGE/USD",
"XRPUSD": "XRP/USD", "XRPUSD": "XRP/USD",
"XRPUSDT": "XRP/USDT", "XRPUSDT": "XRP/USDT",
"XTZ-0625": "XTZ-0625", "XTZ-1231": "XTZ-1231",
"XTZ-PERP": "XTZ-PERP", "XTZ-PERP": "XTZ-PERP",
"XTZBEARUSD": "XTZBEAR/USD", "XTZBEARUSD": "XTZBEAR/USD",
"XTZBEARUSDT": "XTZBEAR/USDT", "XTZBEARUSDT": "XTZBEAR/USDT",
@ -680,7 +756,7 @@ var symbolMap = map[string]string{
"XTZBULLUSDT": "XTZBULL/USDT", "XTZBULLUSDT": "XTZBULL/USDT",
"XTZHALFUSD": "XTZHALF/USD", "XTZHALFUSD": "XTZHALF/USD",
"XTZHEDGEUSD": "XTZHEDGE/USD", "XTZHEDGEUSD": "XTZHEDGE/USD",
"YFI-0625": "YFI-0625", "YFI-1231": "YFI-1231",
"YFI-PERP": "YFI-PERP", "YFI-PERP": "YFI-PERP",
"YFIBTC": "YFI/BTC", "YFIBTC": "YFI/BTC",
"YFII-PERP": "YFII-PERP", "YFII-PERP": "YFII-PERP",
@ -691,7 +767,7 @@ var symbolMap = map[string]string{
"ZECBEARUSD": "ZECBEAR/USD", "ZECBEARUSD": "ZECBEAR/USD",
"ZECBULLUSD": "ZECBULL/USD", "ZECBULLUSD": "ZECBULL/USD",
"ZIL-PERP": "ZIL-PERP", "ZIL-PERP": "ZIL-PERP",
"ZM-0625": "ZM-0625", "ZM-1231": "ZM-1231",
"ZMUSD": "ZM/USD", "ZMUSD": "ZM/USD",
"ZRX-PERP": "ZRX-PERP", "ZRX-PERP": "ZRX-PERP",
"ZRXUSD": "ZRX/USD", "ZRXUSD": "ZRX/USD",

View File

@ -28,7 +28,10 @@ type Exchange struct {
func New(key, secret, passphrase string) *Exchange { func New(key, secret, passphrase string) *Exchange {
client := okexapi.NewClient() client := okexapi.NewClient()
client.Auth(key, secret, passphrase)
if len(key) > 0 && len(secret) > 0 {
client.Auth(key, secret, passphrase)
}
return &Exchange{ return &Exchange{
key: key, key: key,

View File

@ -0,0 +1,44 @@
package mysql
import (
"context"
"github.com/c9s/rockhopper"
)
func init() {
AddMigration(upAddIsFuturesColumn, downAddIsFuturesColumn)
}
func upAddIsFuturesColumn(ctx context.Context, tx rockhopper.SQLExecutor) (err error) {
// This code is executed when the migration is applied.
_, err = tx.ExecContext(ctx, "ALTER TABLE `trades` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;")
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "ALTER TABLE `orders` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;")
if err != nil {
return err
}
return err
}
func downAddIsFuturesColumn(ctx context.Context, tx rockhopper.SQLExecutor) (err error) {
// This code is executed when the migration is rolled back.
_, err = tx.ExecContext(ctx, "ALTER TABLE `trades` DROP COLUMN `is_futures`;")
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "ALTER TABLE `orders` DROP COLUMN `is_futures`;")
if err != nil {
return err
}
return err
}

View File

@ -0,0 +1,44 @@
package sqlite3
import (
"context"
"github.com/c9s/rockhopper"
)
func init() {
AddMigration(upAddIsFuturesColumn, downAddIsFuturesColumn)
}
func upAddIsFuturesColumn(ctx context.Context, tx rockhopper.SQLExecutor) (err error) {
// This code is executed when the migration is applied.
_, err = tx.ExecContext(ctx, "ALTER TABLE `trades` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;")
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "ALTER TABLE `orders` ADD COLUMN `is_futures` BOOLEAN NOT NULL DEFAULT FALSE;")
if err != nil {
return err
}
return err
}
func downAddIsFuturesColumn(ctx context.Context, tx rockhopper.SQLExecutor) (err error) {
// This code is executed when the migration is rolled back.
_, err = tx.ExecContext(ctx, "ALTER TABLE `trades` RENAME COLUMN `is_futures` TO `is_futures_deleted`;")
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "ALTER TABLE `orders` RENAME COLUMN `is_futures` TO `is_futures_deleted`;")
if err != nil {
return err
}
return err
}

View File

@ -20,7 +20,9 @@ type OrderService struct {
func (s *OrderService) Sync(ctx context.Context, exchange types.Exchange, symbol string, startTime time.Time) error { func (s *OrderService) Sync(ctx context.Context, exchange types.Exchange, symbol string, startTime time.Time) error {
isMargin := false isMargin := false
isFutures := false
isIsolated := false isIsolated := false
if marginExchange, ok := exchange.(types.MarginExchange); ok { if marginExchange, ok := exchange.(types.MarginExchange); ok {
marginSettings := marginExchange.GetMarginSettings() marginSettings := marginExchange.GetMarginSettings()
isMargin = marginSettings.IsMargin isMargin = marginSettings.IsMargin
@ -30,7 +32,17 @@ func (s *OrderService) Sync(ctx context.Context, exchange types.Exchange, symbol
} }
} }
records, err := s.QueryLast(exchange.Name(), symbol, isMargin, isIsolated, 50) if futuresExchange, ok := exchange.(types.FuturesExchange); ok {
futuresSettings := futuresExchange.GetFuturesSettings()
isFutures = futuresSettings.IsFutures
isIsolated = futuresSettings.IsIsolatedFutures
if futuresSettings.IsIsolatedFutures {
symbol = futuresSettings.IsolatedFuturesSymbol
}
}
records, err := s.QueryLast(exchange.Name(), symbol, isMargin, isFutures, isIsolated, 50)
if err != nil { if err != nil {
return err return err
} }
@ -78,14 +90,15 @@ func (s *OrderService) Sync(ctx context.Context, exchange types.Exchange, symbol
// QueryLast queries the last order from the database // QueryLast queries the last order from the database
func (s *OrderService) QueryLast(ex types.ExchangeName, symbol string, isMargin, isIsolated bool, limit int) ([]types.Order, error) { func (s *OrderService) QueryLast(ex types.ExchangeName, symbol string, isMargin, isFutures, isIsolated bool, limit int) ([]types.Order, error) {
log.Infof("querying last order exchange = %s AND symbol = %s AND is_margin = %v AND is_isolated = %v", ex, symbol, isMargin, isIsolated) log.Infof("querying last order exchange = %s AND symbol = %s AND is_margin = %v AND is_futures = %v AND is_isolated = %v", ex, symbol, isMargin, isFutures, isIsolated)
sql := `SELECT * FROM orders WHERE exchange = :exchange AND symbol = :symbol AND is_margin = :is_margin AND is_isolated = :is_isolated ORDER BY gid DESC LIMIT :limit` sql := `SELECT * FROM orders WHERE exchange = :exchange AND symbol = :symbol AND is_margin = :is_margin AND is_futures = :is_futures AND is_isolated = :is_isolated ORDER BY gid DESC LIMIT :limit`
rows, err := s.DB.NamedQuery(sql, map[string]interface{}{ rows, err := s.DB.NamedQuery(sql, map[string]interface{}{
"exchange": ex, "exchange": ex,
"symbol": symbol, "symbol": symbol,
"is_margin": isMargin, "is_margin": isMargin,
"is_futures": isFutures,
"is_isolated": isIsolated, "is_isolated": isIsolated,
"limit": limit, "limit": limit,
}) })
@ -195,15 +208,15 @@ func (s *OrderService) scanRows(rows *sqlx.Rows) (orders []types.Order, err erro
func (s *OrderService) Insert(order types.Order) (err error) { func (s *OrderService) Insert(order types.Order) (err error) {
if s.DB.DriverName() == "mysql" { if s.DB.DriverName() == "mysql" {
_, err = s.DB.NamedExec(` _, err = s.DB.NamedExec(`
INSERT INTO orders (exchange, order_id, client_order_id, order_type, status, symbol, price, stop_price, quantity, executed_quantity, side, is_working, time_in_force, created_at, updated_at, is_margin, is_isolated) INSERT INTO orders (exchange, order_id, client_order_id, order_type, status, symbol, price, stop_price, quantity, executed_quantity, side, is_working, time_in_force, created_at, updated_at, is_margin, is_futures, is_isolated)
VALUES (:exchange, :order_id, :client_order_id, :order_type, :status, :symbol, :price, :stop_price, :quantity, :executed_quantity, :side, :is_working, :time_in_force, :created_at, :updated_at, :is_margin, :is_isolated) VALUES (:exchange, :order_id, :client_order_id, :order_type, :status, :symbol, :price, :stop_price, :quantity, :executed_quantity, :side, :is_working, :time_in_force, :created_at, :updated_at, :is_margin, :is_futures, :is_isolated)
ON DUPLICATE KEY UPDATE status=:status, executed_quantity=:executed_quantity, is_working=:is_working, updated_at=:updated_at`, order) ON DUPLICATE KEY UPDATE status=:status, executed_quantity=:executed_quantity, is_working=:is_working, updated_at=:updated_at`, order)
return err return err
} }
_, err = s.DB.NamedExec(` _, err = s.DB.NamedExec(`
INSERT INTO orders (exchange, order_id, client_order_id, order_type, status, symbol, price, stop_price, quantity, executed_quantity, side, is_working, time_in_force, created_at, updated_at, is_margin, is_isolated) INSERT INTO orders (exchange, order_id, client_order_id, order_type, status, symbol, price, stop_price, quantity, executed_quantity, side, is_working, time_in_force, created_at, updated_at, is_margin, is_futures, is_isolated)
VALUES (:exchange, :order_id, :client_order_id, :order_type, :status, :symbol, :price, :stop_price, :quantity, :executed_quantity, :side, :is_working, :time_in_force, :created_at, :updated_at, :is_margin, :is_isolated) VALUES (:exchange, :order_id, :client_order_id, :order_type, :status, :symbol, :price, :stop_price, :quantity, :executed_quantity, :side, :is_working, :time_in_force, :created_at, :updated_at, :is_margin, :is_futures, :is_isolated)
`, order) `, order)
return err return err

View File

@ -52,7 +52,9 @@ func NewTradeService(db *sqlx.DB) *TradeService {
func (s *TradeService) Sync(ctx context.Context, exchange types.Exchange, symbol string) error { func (s *TradeService) Sync(ctx context.Context, exchange types.Exchange, symbol string) error {
isMargin := false isMargin := false
isFutures := false
isIsolated := false isIsolated := false
if marginExchange, ok := exchange.(types.MarginExchange); ok { if marginExchange, ok := exchange.(types.MarginExchange); ok {
marginSettings := marginExchange.GetMarginSettings() marginSettings := marginExchange.GetMarginSettings()
isMargin = marginSettings.IsMargin isMargin = marginSettings.IsMargin
@ -62,8 +64,18 @@ func (s *TradeService) Sync(ctx context.Context, exchange types.Exchange, symbol
} }
} }
if futuresExchange, ok := exchange.(types.FuturesExchange); ok {
futuresSettings := futuresExchange.GetFuturesSettings()
isFutures = futuresSettings.IsFutures
isIsolated = futuresSettings.IsIsolatedFutures
if futuresSettings.IsIsolatedFutures {
symbol = futuresSettings.IsolatedFuturesSymbol
}
}
// records descending ordered // records descending ordered
records, err := s.QueryLast(exchange.Name(), symbol, isMargin, isIsolated, 50) records, err := s.QueryLast(exchange.Name(), symbol, isMargin, isFutures, isIsolated, 50)
if err != nil { if err != nil {
return err return err
} }
@ -265,14 +277,15 @@ func generateMysqlTradingVolumeQuerySQL(options TradingVolumeQueryOptions) strin
} }
// QueryLast queries the last trade from the database // QueryLast queries the last trade from the database
func (s *TradeService) QueryLast(ex types.ExchangeName, symbol string, isMargin, isIsolated bool, limit int) ([]types.Trade, error) { func (s *TradeService) QueryLast(ex types.ExchangeName, symbol string, isMargin, isFutures, isIsolated bool, limit int) ([]types.Trade, error) {
log.Debugf("querying last trade exchange = %s AND symbol = %s AND is_margin = %v AND is_isolated = %v", ex, symbol, isMargin, isIsolated) log.Debugf("querying last trade exchange = %s AND symbol = %s AND is_margin = %v AND is_futures = %v AND is_isolated = %v", ex, symbol, isMargin, isFutures, isIsolated)
sql := "SELECT * FROM trades WHERE exchange = :exchange AND symbol = :symbol AND is_margin = :is_margin AND is_isolated = :is_isolated ORDER BY gid DESC LIMIT :limit" sql := "SELECT * FROM trades WHERE exchange = :exchange AND symbol = :symbol AND is_margin = :is_margin AND is_futures = :is_futures AND is_isolated = :is_isolated ORDER BY gid DESC LIMIT :limit"
rows, err := s.DB.NamedQuery(sql, map[string]interface{}{ rows, err := s.DB.NamedQuery(sql, map[string]interface{}{
"symbol": symbol, "symbol": symbol,
"exchange": ex, "exchange": ex,
"is_margin": isMargin, "is_margin": isMargin,
"is_futures": isFutures,
"is_isolated": isIsolated, "is_isolated": isIsolated,
"limit": limit, "limit": limit,
}) })
@ -439,8 +452,8 @@ func (s *TradeService) scanRows(rows *sqlx.Rows) (trades []types.Trade, err erro
func (s *TradeService) Insert(trade types.Trade) error { func (s *TradeService) Insert(trade types.Trade) error {
_, err := s.DB.NamedExec(` _, err := s.DB.NamedExec(`
INSERT INTO trades (id, exchange, order_id, symbol, price, quantity, quote_quantity, side, is_buyer, is_maker, fee, fee_currency, traded_at, is_margin, is_isolated) INSERT INTO trades (id, exchange, order_id, symbol, price, quantity, quote_quantity, side, is_buyer, is_maker, fee, fee_currency, traded_at, is_margin, is_futures, is_isolated)
VALUES (:id, :exchange, :order_id, :symbol, :price, :quantity, :quote_quantity, :side, :is_buyer, :is_maker, :fee, :fee_currency, :traded_at, :is_margin, :is_isolated)`, VALUES (:id, :exchange, :order_id, :symbol, :price, :quantity, :quote_quantity, :side, :is_buyer, :is_maker, :fee, :fee_currency, :traded_at, :is_margin, :is_futures, :is_isolated)`,
trade) trade)
return err return err
} }

View File

@ -4,13 +4,14 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/c9s/bbgo/pkg/exchange/binance"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/sirupsen/logrus"
"math" "math"
"strings" "strings"
"time" "time"
"github.com/c9s/bbgo/pkg/exchange/binance"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
@ -87,7 +88,7 @@ func (s *Strategy) Validate() error {
} }
func (s *Strategy) listenToFundingRate(ctx context.Context, exchange *binance.Exchange) { func (s *Strategy) listenToFundingRate(ctx context.Context, exchange *binance.Exchange) {
var previousIndex, fundingRate24HoursLowIndex *binance.PremiumIndex var previousIndex, fundingRate24HoursLowIndex *types.PremiumIndex
fundingRateTicker := time.NewTicker(1 * time.Hour) fundingRateTicker := time.NewTicker(1 * time.Hour)
defer fundingRateTicker.Stop() defer fundingRateTicker.Stop()

View File

@ -225,7 +225,7 @@ func (a *Account) Balance(currency string) (balance Balance, ok bool) {
return balance, ok return balance, ok
} }
func (a *Account) AddBalance(currency string, fund fixedpoint.Value) error { func (a *Account) AddBalance(currency string, fund fixedpoint.Value) {
a.Lock() a.Lock()
defer a.Unlock() defer a.Unlock()
@ -233,7 +233,7 @@ func (a *Account) AddBalance(currency string, fund fixedpoint.Value) error {
if ok { if ok {
balance.Available += fund balance.Available += fund
a.balances[currency] = balance a.balances[currency] = balance
return nil return
} }
a.balances[currency] = Balance{ a.balances[currency] = Balance{
@ -241,7 +241,6 @@ func (a *Account) AddBalance(currency string, fund fixedpoint.Value) error {
Available: fund, Available: fund,
Locked: 0, Locked: 0,
} }
return nil
} }
func (a *Account) UseLockedBalance(currency string, fund fixedpoint.Value) error { func (a *Account) UseLockedBalance(currency string, fund fixedpoint.Value) error {

View File

@ -10,9 +10,9 @@ import (
func TestAccountLockAndUnlock(t *testing.T) { func TestAccountLockAndUnlock(t *testing.T) {
a := NewAccount() a := NewAccount()
err := a.AddBalance("USDT", 1000) a.AddBalance("USDT", 1000)
assert.NoError(t, err)
var err error
balance, ok := a.Balance("USDT") balance, ok := a.Balance("USDT")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, balance.Available, fixedpoint.Value(1000)) assert.Equal(t, balance.Available, fixedpoint.Value(1000))
@ -36,9 +36,9 @@ func TestAccountLockAndUnlock(t *testing.T) {
func TestAccountLockAndUse(t *testing.T) { func TestAccountLockAndUse(t *testing.T) {
a := NewAccount() a := NewAccount()
err := a.AddBalance("USDT", 1000) a.AddBalance("USDT", 1000)
assert.NoError(t, err)
var err error
balance, ok := a.Balance("USDT") balance, ok := a.Balance("USDT")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, balance.Available, fixedpoint.Value(1000)) assert.Equal(t, balance.Available, fixedpoint.Value(1000))

13
pkg/types/fundingrate.go Normal file
View File

@ -0,0 +1,13 @@
package types
import (
"time"
"github.com/c9s/bbgo/pkg/fixedpoint"
)
type FundingRate struct {
FundingRate fixedpoint.Value
FundingTime time.Time
Time time.Time
}

View File

@ -114,6 +114,10 @@ type SubmitOrder struct {
GroupID uint32 `json:"groupID,omitempty"` GroupID uint32 `json:"groupID,omitempty"`
MarginSideEffect MarginOrderSideEffectType `json:"marginSideEffect,omitempty"` // AUTO_REPAY = repay, MARGIN_BUY = borrow, defaults to NO_SIDE_EFFECT MarginSideEffect MarginOrderSideEffectType `json:"marginSideEffect,omitempty"` // AUTO_REPAY = repay, MARGIN_BUY = borrow, defaults to NO_SIDE_EFFECT
// futures order fields
ReduceOnly bool `json:"reduceOnly" db:"reduce_only"`
ClosePosition bool `json:"closePosition" db:"close_position"`
} }
func (o *SubmitOrder) String() string { func (o *SubmitOrder) String() string {

15
pkg/types/premiumindex.go Normal file
View File

@ -0,0 +1,15 @@
package types
import (
"time"
"github.com/c9s/bbgo/pkg/fixedpoint"
)
type PremiumIndex struct {
Symbol string `json:"symbol"`
MarkPrice fixedpoint.Value `json:"markPrice"`
LastFundingRate fixedpoint.Value `json:"lastFundingRate"`
NextFundingTime time.Time `json:"nextFundingTime"`
Time time.Time `json:"time"`
}

View File

@ -66,6 +66,7 @@ type Trade struct {
FeeCurrency string `json:"feeCurrency" db:"fee_currency"` FeeCurrency string `json:"feeCurrency" db:"fee_currency"`
IsMargin bool `json:"isMargin" db:"is_margin"` IsMargin bool `json:"isMargin" db:"is_margin"`
IsFutures bool `json:"isFutures" db:"is_futures"`
IsIsolated bool `json:"isIsolated" db:"is_isolated"` IsIsolated bool `json:"isIsolated" db:"is_isolated"`
StrategyID sql.NullString `json:"strategyID" db:"strategy"` StrategyID sql.NullString `json:"strategyID" db:"strategy"`

View File

@ -2,7 +2,7 @@
package version package version
const Version = "v1.19.4-5b11ef81" const Version = "v1.20.0-c1e69194"
const VersionGitRef = "5b11ef81" const VersionGitRef = "c1e69194"

View File

@ -7,6 +7,10 @@ if [[ -z $VERSION ]] ; then
VERSION=$(git describe --tags) VERSION=$(git describe --tags)
fi fi
if [[ -n $VERSION_SUFFIX ]] ; then
VERSION=${VERSION}${VERSION_SUFFIX}
fi
cat <<END cat <<END
// +build release // +build release