update strategy dev doc

This commit is contained in:
c9s 2022-07-04 11:58:11 +08:00
parent 6fe980a2a3
commit 9fbe2e859e
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54

View File

@ -12,6 +12,89 @@ it.
In general, strategies are Go struct, placed in Go package.
## Quick Start
To add your first strategy, the fastest way is to add the built-in strategy.
Simply edit `pkg/cmd/builtin.go` and import your strategy package there.
When BBGO starts, the strategy will be imported as a package, and register its struct to the engine.
You can also create a new file called `pkg/cmd/builtin_short.go` and import your strategy package.
```
import (
_ "github.com/c9s/bbgo/pkg/strategy/short"
)
```
Create a directory for your new strategy in the BBGO source code repository:
```shell
mkdir -p pkg/strategy/short
```
Open a new file at `pkg/strategy/short/strategy.go` and paste the simplest strategy code:
```
package short
import (
"context"
"fmt"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/types"
)
const ID = "short"
func init() {
// Register our struct type to BBGO
// Note that you don't need to field the fields.
// BBGO uses reflect to parse your type information.
bbgo.RegisterStrategy(ID, &Strategy{})
}
type Strategy struct {
Symbol string `json:"symbol"`
Interval types.Interval `json:"interval"`
}
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval})
}
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
session.MarketDataStream.OnKLineClosed(func(k types.KLine) {
fmt.Println(k)
})
return nil
}
```
This is the most simple strategy with only ~30 lines code, it subscribes to the kline channel with the given symbol from the config,
And when the kline is closed, it prints the kline to the console.
Note that, when Run() is executed, the user data stream is not connected to the exchange yet, but the history market data is already loaded,
so if you need to submit an order on start, be sure to write your order submit code inside the event closures like `OnKLineClosed` or `OnStart`.
Now you can prepare your config file, create a file called `bbgo.yaml` with the following content:
```yaml
exchangeStrategies:
- on: binance
short:
symbol: ETHUSDT
interval: 1m
```
And then, you should be able to run this strategy by running the following command:
```shell
go run ./cmd/bbgo run
```
## The Strategy Struct
BBGO loads the YAML config file and re-unmarshal the settings into your struct as JSON string, so you can define the
@ -68,8 +151,76 @@ Note that you don't need to fill the fields in the struct, BBGO just need to kno
(BBGO use reflect to parse the fields from the given struct and allocate a new struct object from the given struct type internally)
## Market Data Stream and User Data Stream
## Built-in Strategy
When BBGO connects to the exchange, it allocates two stream objects for different purposes.
They are:
- MarketDataStream receives market data from the exchange, for example, KLine data (candlestick, or bars), market public trades.
- UserDataStream receives your personal trading data, for example, orders, executed trades, balance updates and other private information.
To add your market data subscription to the `MarketDataStream`, you can register your subscription in the `Subscribe` of the strategy code,
for example:
```
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: "1m"})
}
```
Since the back-test engine is a kline-based engine, to subscribe market trades, you need to check if you're in the back-test environment:
```
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
if !bbgo.IsBackTesting {
session.Subscribe(types.MarketTradeChannel, s.Symbol, types.SubscribeOptions{})
}
}
```
To receive the market data from the market data stream, you need to register the event callback:
```
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
// handle closed kline event here
})
session.MarketDataStream.OnMarketTrade(func(trade types.Trade) {
// handle market trade event here
})
}
```
In the above example, we register our event callback to the market data stream of the current exchange session, The
market data stream object here is a session-wide market data stream, so it's shared with other strategies that are also
using the same exchange session, you might receive kline with different symbol or interval.
so it's better to add a condition to filter the kline events:
```
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
if kline.Symbol != s.Symbol || kline.Interval != s.Interval {
return
}
// handle your kline here
})
}
```
You can also use the KLineWith method to wrap your kline closure with the filter condition:
```
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
session.MarketDataStream.OnKLineClosed(types.KLineWith("BTCUSDT", types.Interval1m, func(kline types.KLine) {
// handle your kline here
})
}
```
Note that, when the Run() method is executed, the user data stream and market data stream are not connected yet.