mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 08:45:16 +00:00
Merge pull request #181 from c9s/ftx/market
This commit is contained in:
commit
f84b3a5177
74
pkg/cmd/market.go
Normal file
74
pkg/cmd/market.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
marketCmd.Flags().String("session", "", "the exchange session name for querying information")
|
||||
RootCmd.AddCommand(marketCmd)
|
||||
}
|
||||
|
||||
// go run ./cmd/bbgo market --session=ftx --config=config/bbgo.yaml
|
||||
var marketCmd = &cobra.Command{
|
||||
Use: "market",
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
configFile, err := cmd.Flags().GetString("config")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(configFile) == 0 {
|
||||
return errors.New("--config option is required")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
userConfig, err := bbgo.Load(configFile, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
environ := bbgo.NewEnvironment()
|
||||
if err := environ.ConfigureDatabase(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := environ.ConfigureExchangeSessions(userConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sessionName, err := cmd.Flags().GetString("session")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session, ok := environ.Session(sessionName)
|
||||
if !ok {
|
||||
return fmt.Errorf("session %s not found", sessionName)
|
||||
}
|
||||
|
||||
markets, err := session.Exchange.QueryMarkets(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, m := range markets {
|
||||
log.Infof("market: %+v", m)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
|
@ -61,7 +61,40 @@ func (e *Exchange) NewStream() types.Stream {
|
|||
}
|
||||
|
||||
func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
|
||||
panic("implement me")
|
||||
resp, err := e.newRest().Markets(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("ftx returns querying markets failure")
|
||||
}
|
||||
|
||||
markets := types.MarketMap{}
|
||||
for _, m := range resp.Result {
|
||||
symbol := toGlobalSymbol(m.Name)
|
||||
|
||||
market := types.Market{
|
||||
Symbol: symbol,
|
||||
// The max precision is length(DefaultPow). For example, currently fixedpoint.DefaultPow
|
||||
// is 1e8, so the max precision will be 8.
|
||||
PricePrecision: fixedpoint.NumFractionalDigits(fixedpoint.NewFromFloat(m.PriceIncrement)),
|
||||
VolumePrecision: fixedpoint.NumFractionalDigits(fixedpoint.NewFromFloat(m.SizeIncrement)),
|
||||
QuoteCurrency: toGlobalCurrency(m.QuoteCurrency),
|
||||
BaseCurrency: toGlobalCurrency(m.BaseCurrency),
|
||||
// FTX only limit your order by `MinProvideSize`, so I assign zero value to unsupported fields:
|
||||
// MinNotional, MinAmount, MaxQuantity, MinPrice and MaxPrice.
|
||||
MinNotional: 0,
|
||||
MinAmount: 0,
|
||||
MinQuantity: m.MinProvideSize,
|
||||
MaxQuantity: 0,
|
||||
StepSize: m.SizeIncrement,
|
||||
MinPrice: 0,
|
||||
MaxPrice: 0,
|
||||
TickSize: m.PriceIncrement,
|
||||
}
|
||||
markets[symbol] = market
|
||||
}
|
||||
return markets, nil
|
||||
}
|
||||
|
||||
func (e *Exchange) QueryAccount(ctx context.Context) (*types.Account, error) {
|
||||
|
|
|
@ -368,3 +368,58 @@ func TestExchange_QueryAccount(t *testing.T) {
|
|||
assert.Equal(t, fixedpoint.NewFromFloat(0.0002), resp.MakerCommission)
|
||||
assert.Equal(t, fixedpoint.NewFromFloat(0.0005), resp.TakerCommission)
|
||||
}
|
||||
|
||||
func TestExchange_QueryMarkets(t *testing.T) {
|
||||
respJSON := `{
|
||||
"success": true,
|
||||
"result": [
|
||||
{
|
||||
"name": "BTC/USD",
|
||||
"enabled": true,
|
||||
"postOnly": false,
|
||||
"priceIncrement": 1.0,
|
||||
"sizeIncrement": 0.0001,
|
||||
"minProvideSize": 0.001,
|
||||
"last": 59039.0,
|
||||
"bid": 59038.0,
|
||||
"ask": 59040.0,
|
||||
"price": 59039.0,
|
||||
"type": "spot",
|
||||
"baseCurrency": "BTC",
|
||||
"quoteCurrency": "USD",
|
||||
"underlying": null,
|
||||
"restricted": false,
|
||||
"highLeverageFeeExempt": true,
|
||||
"change1h": 0.0015777151969599294,
|
||||
"change24h": 0.05475756601279165,
|
||||
"changeBod": -0.0035107262814994852,
|
||||
"quoteVolume24h": 316493675.5463,
|
||||
"volumeUsd24h": 316493675.5463
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, respJSON)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ex := NewExchange("", "", "")
|
||||
serverURL, err := url.Parse(ts.URL)
|
||||
assert.NoError(t, err)
|
||||
ex.restEndpoint = serverURL
|
||||
resp, err := ex.QueryMarkets(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, resp, 1)
|
||||
assert.Equal(t, types.Market{
|
||||
Symbol: "BTC/USD",
|
||||
PricePrecision: 0,
|
||||
VolumePrecision: 4,
|
||||
QuoteCurrency: "USD",
|
||||
BaseCurrency: "BTC",
|
||||
MinQuantity: 0.001,
|
||||
StepSize: 0.0001,
|
||||
TickSize: 1,
|
||||
}, resp["BTC/USD"])
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ type restRequest struct {
|
|||
*balanceRequest
|
||||
*orderRequest
|
||||
*accountRequest
|
||||
*marketRequest
|
||||
|
||||
key, secret string
|
||||
// Optional sub-account name
|
||||
|
@ -43,6 +44,7 @@ func newRestRequest(c *http.Client, baseURL *url.URL) *restRequest {
|
|||
baseURL: baseURL,
|
||||
}
|
||||
|
||||
r.marketRequest = &marketRequest{restRequest: r}
|
||||
r.accountRequest = &accountRequest{restRequest: r}
|
||||
r.balanceRequest = &balanceRequest{restRequest: r}
|
||||
r.orderRequest = &orderRequest{restRequest: r}
|
||||
|
|
29
pkg/exchange/ftx/rest_market_request.go
Normal file
29
pkg/exchange/ftx/rest_market_request.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package ftx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type marketRequest struct {
|
||||
*restRequest
|
||||
}
|
||||
|
||||
func (r *marketRequest) Markets(ctx context.Context) (marketsResponse, error) {
|
||||
resp, err := r.
|
||||
Method("GET").
|
||||
ReferenceURL("api/markets").
|
||||
DoAuthenticatedRequest(ctx)
|
||||
|
||||
if err != nil {
|
||||
return marketsResponse{}, err
|
||||
}
|
||||
|
||||
var m marketsResponse
|
||||
if err := json.Unmarshal(resp.Body, &m); err != nil {
|
||||
return marketsResponse{}, fmt.Errorf("failed to unmarshal market response body to json: %w", err)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
|
@ -102,6 +102,62 @@ type balances struct {
|
|||
} `json:"result"`
|
||||
}
|
||||
|
||||
/*
|
||||
[
|
||||
{
|
||||
"name": "BTC/USD",
|
||||
"enabled": true,
|
||||
"postOnly": false,
|
||||
"priceIncrement": 1.0,
|
||||
"sizeIncrement": 0.0001,
|
||||
"minProvideSize": 0.0001,
|
||||
"last": 59039.0,
|
||||
"bid": 59038.0,
|
||||
"ask": 59040.0,
|
||||
"price": 59039.0,
|
||||
"type": "spot",
|
||||
"baseCurrency": "BTC",
|
||||
"quoteCurrency": "USD",
|
||||
"underlying": null,
|
||||
"restricted": false,
|
||||
"highLeverageFeeExempt": true,
|
||||
"change1h": 0.0015777151969599294,
|
||||
"change24h": 0.05475756601279165,
|
||||
"changeBod": -0.0035107262814994852,
|
||||
"quoteVolume24h": 316493675.5463,
|
||||
"volumeUsd24h": 316493675.5463
|
||||
}
|
||||
]
|
||||
*/
|
||||
type marketsResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Result []market `json:"result"`
|
||||
}
|
||||
|
||||
type market struct {
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
PriceIncrement float64 `json:"priceIncrement"`
|
||||
SizeIncrement float64 `json:"sizeIncrement"`
|
||||
MinProvideSize float64 `json:"minProvideSize"`
|
||||
Last float64 `json:"last"`
|
||||
Bid float64 `json:"bid"`
|
||||
Ask float64 `json:"ask"`
|
||||
Price float64 `json:"price"`
|
||||
Type string `json:"type"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
Underlying string `json:"underlying"`
|
||||
Restricted bool `json:"restricted"`
|
||||
HighLeverageFeeExempt bool `json:"highLeverageFeeExempt"`
|
||||
Change1h float64 `json:"change1h"`
|
||||
Change24h float64 `json:"change24h"`
|
||||
ChangeBod float64 `json:"changeBod"`
|
||||
QuoteVolume24h float64 `json:"quoteVolume24h"`
|
||||
VolumeUsd24h float64 `json:"volumeUsd24h"`
|
||||
}
|
||||
|
||||
type ordersHistoryResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Result []order `json:"result"`
|
||||
|
|
Loading…
Reference in New Issue
Block a user