mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Add pricealert strategy for demonstrating notification
This commit is contained in:
parent
ab43de3efd
commit
b3eaf832af
|
@ -6,7 +6,7 @@ imports:
|
|||
notifications:
|
||||
slack:
|
||||
defaultChannel: "#dev-bbgo"
|
||||
errorChannel: "#error"
|
||||
errorChannel: "#bbgo-error"
|
||||
|
||||
# if you want to route channel by symbol
|
||||
symbolChannels:
|
||||
|
|
29
config/pricealert.yaml
Normal file
29
config/pricealert.yaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
notifications:
|
||||
slack:
|
||||
defaultChannel: "#dev-bbgo"
|
||||
errorChannel: "#bbgo-error"
|
||||
|
||||
# if you want to route channel by symbol
|
||||
symbolChannels:
|
||||
"^BTC": "#btc"
|
||||
"^ETH": "#eth"
|
||||
|
||||
# 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: 0.01
|
|
@ -97,12 +97,14 @@ func NewPatternChannelRouter(routes map[string]string) *PatternChannelRouter {
|
|||
return router
|
||||
}
|
||||
|
||||
func (router *PatternChannelRouter) AddRoute(routes map[string]string) *PatternChannelRouter {
|
||||
func (router *PatternChannelRouter) AddRoute(routes map[string]string) {
|
||||
if routes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for pattern, channel := range routes {
|
||||
router.routes[regexp.MustCompile(pattern)] = channel
|
||||
}
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
func (router *PatternChannelRouter) Route(text string) (channel string, ok bool) {
|
||||
|
@ -126,9 +128,8 @@ func NewObjectChannelRouter() *ObjectChannelRouter {
|
|||
return &ObjectChannelRouter{}
|
||||
}
|
||||
|
||||
func (router *ObjectChannelRouter) AddRoute(f ObjectChannelHandler) *ObjectChannelRouter {
|
||||
func (router *ObjectChannelRouter) AddRoute(f ObjectChannelHandler) {
|
||||
router.routes = append(router.routes, f)
|
||||
return router
|
||||
}
|
||||
|
||||
func (router *ObjectChannelRouter) Route(obj interface{}) (channel string, ok bool) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package bbgo
|
|||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
|
@ -57,7 +58,6 @@ func (trader *Trader) ReportTrade() *TradeReporter {
|
|||
return trader.tradeReporter
|
||||
}
|
||||
|
||||
|
||||
// AttachStrategyOn attaches the single exchange strategy on an exchange session.
|
||||
// Single exchange strategy is the default behavior.
|
||||
func (trader *Trader) AttachStrategyOn(session string, strategies ...SingleExchangeStrategy) *Trader {
|
||||
|
@ -113,7 +113,7 @@ func (trader *Trader) Run(ctx context.Context) error {
|
|||
var orderExecutor OrderExecutor = baseOrderExecutor
|
||||
|
||||
// Since the risk controls are loaded from the config file
|
||||
if riskControls := trader.riskControls ; riskControls != nil {
|
||||
if riskControls := trader.riskControls; riskControls != nil {
|
||||
if trader.riskControls.SessionBasedRiskControl != nil {
|
||||
control, ok := trader.riskControls.SessionBasedRiskControl[sessionName]
|
||||
if ok {
|
||||
|
@ -128,6 +128,19 @@ func (trader *Trader) Run(ctx context.Context) error {
|
|||
}
|
||||
|
||||
for _, strategy := range strategies {
|
||||
rs := reflect.ValueOf(strategy)
|
||||
if rs.Elem().Kind() == reflect.Struct {
|
||||
rs = rs.Elem()
|
||||
field := rs.FieldByName("Notifiability")
|
||||
if field.IsValid() {
|
||||
log.Infof("found Notifiability in strategy %T, configuring...", strategy)
|
||||
if !field.CanSet() {
|
||||
log.Panicf("strategy %T field Notifiability can not be set", strategy)
|
||||
}
|
||||
field.Set(reflect.ValueOf(trader.Notifiability))
|
||||
}
|
||||
}
|
||||
|
||||
err := strategy.Run(ctx, orderExecutor, session)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -262,8 +275,8 @@ func (trader *Trader) reportPnL() {
|
|||
*/
|
||||
|
||||
// ReportPnL configure and set the PnLReporter with the given notifier
|
||||
func (trader *Trader) ReportPnL(notifier Notifier) *PnLReporterManager {
|
||||
return NewPnLReporter(notifier)
|
||||
func (trader *Trader) ReportPnL() *PnLReporterManager {
|
||||
return NewPnLReporter(&trader.Notifiability)
|
||||
}
|
||||
|
||||
type OrderExecutor interface {
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
// import built-in strategies
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/buyandhold"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
|
||||
_ "github.com/c9s/bbgo/pkg/strategy/xpuremaker"
|
||||
)
|
||||
|
||||
|
@ -105,7 +106,7 @@ func runConfig(ctx context.Context, userConfig *bbgo.Config) error {
|
|||
trader := bbgo.NewTrader(environ)
|
||||
|
||||
// configure notifiers
|
||||
notifierSet := &bbgo.Notifiability{
|
||||
trader.Notifiability = bbgo.Notifiability{
|
||||
SymbolChannelRouter: bbgo.NewPatternChannelRouter(nil),
|
||||
SessionChannelRouter: bbgo.NewPatternChannelRouter(nil),
|
||||
ObjectChannelRouter: bbgo.NewObjectChannelRouter(),
|
||||
|
@ -122,7 +123,7 @@ func runConfig(ctx context.Context, userConfig *bbgo.Config) error {
|
|||
|
||||
log.Infof("adding slack notifier...")
|
||||
var notifier = slacknotifier.New(slackToken, conf.DefaultChannel)
|
||||
notifierSet.AddNotifier(notifier)
|
||||
trader.AddNotifier(notifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,50 +131,48 @@ func runConfig(ctx context.Context, userConfig *bbgo.Config) error {
|
|||
if conf := userConfig.Notifications; conf != nil {
|
||||
// configure routing here
|
||||
if conf.SymbolChannels != nil {
|
||||
notifierSet.SymbolChannelRouter.AddRoute(conf.SymbolChannels)
|
||||
trader.SymbolChannelRouter.AddRoute(conf.SymbolChannels)
|
||||
}
|
||||
if conf.SessionChannels != nil {
|
||||
notifierSet.SessionChannelRouter.AddRoute(conf.SessionChannels)
|
||||
trader.SessionChannelRouter.AddRoute(conf.SessionChannels)
|
||||
}
|
||||
|
||||
if conf.Routing != nil {
|
||||
if conf.Routing.Trade == "$symbol" {
|
||||
notifierSet.ObjectChannelRouter.Route(func(obj interface{}) (channel string, ok bool) {
|
||||
trader.ObjectChannelRouter.Route(func(obj interface{}) (channel string, ok bool) {
|
||||
trade, matched := obj.(*types.Trade)
|
||||
if !matched {
|
||||
return
|
||||
}
|
||||
channel, ok = notifierSet.SymbolChannelRouter.Route(trade.Symbol)
|
||||
channel, ok = trader.SymbolChannelRouter.Route(trade.Symbol)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
if conf.Routing.Order == "$symbol" {
|
||||
notifierSet.ObjectChannelRouter.Route(func(obj interface{}) (channel string, ok bool) {
|
||||
trader.ObjectChannelRouter.Route(func(obj interface{}) (channel string, ok bool) {
|
||||
order, matched := obj.(*types.Order)
|
||||
if !matched {
|
||||
return
|
||||
}
|
||||
channel, ok = notifierSet.SymbolChannelRouter.Route(order.Symbol)
|
||||
channel, ok = trader.SymbolChannelRouter.Route(order.Symbol)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
if conf.Routing.PnL == "$symbol" {
|
||||
notifierSet.ObjectChannelRouter.Route(func(obj interface{}) (channel string, ok bool) {
|
||||
trader.ObjectChannelRouter.Route(func(obj interface{}) (channel string, ok bool) {
|
||||
report, matched := obj.(*pnl.AverageCostPnlReport)
|
||||
if !matched {
|
||||
return
|
||||
}
|
||||
channel, ok = notifierSet.SymbolChannelRouter.Route(report.Symbol)
|
||||
channel, ok = trader.SymbolChannelRouter.Route(report.Symbol)
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader.AddNotifier(notifierSet)
|
||||
|
||||
trader.ReportTrade()
|
||||
|
||||
if userConfig.RiskControls != nil {
|
||||
|
@ -196,7 +195,7 @@ func runConfig(ctx context.Context, userConfig *bbgo.Config) error {
|
|||
if len(report.AverageCostBySymbols) > 0 {
|
||||
|
||||
log.Infof("setting up average cost pnl reporter on symbols: %v", report.AverageCostBySymbols)
|
||||
trader.ReportPnL(notifierSet).
|
||||
trader.ReportPnL().
|
||||
AverageCostBySymbols(report.AverageCostBySymbols...).
|
||||
Of(report.Of...).
|
||||
When(report.When...)
|
||||
|
|
40
pkg/strategy/pricealert/strategy.go
Normal file
40
pkg/strategy/pricealert/strategy.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package pricealert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bbgo.RegisterExchangeStrategy("pricealert", &Strategy{})
|
||||
}
|
||||
|
||||
type Strategy struct {
|
||||
bbgo.Notifiability
|
||||
|
||||
Symbol string `json:"symbol"`
|
||||
Interval string `json:"interval"`
|
||||
MinChange float64 `json:"minChange"`
|
||||
}
|
||||
|
||||
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval})
|
||||
session.Stream.OnKLine(func(kline types.KLine) {
|
||||
market, ok := session.Market(kline.Symbol)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if math.Abs(kline.GetChange()) > s.MinChange {
|
||||
if channel, ok := s.RouteSymbol(s.Symbol); ok {
|
||||
_ = s.NotifyTo(channel, "%s hit price %s, change %f", s.Symbol, market.FormatPrice(kline.Close), kline.GetChange())
|
||||
} else {
|
||||
_ = s.Notify("%s hit price %s, change %f", s.Symbol, market.FormatPrice(kline.Close), kline.GetChange())
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
|
@ -17,12 +17,6 @@ type Strategy struct {
|
|||
Symbol string `json:"symbol"`
|
||||
}
|
||||
|
||||
func New(symbol string) *Strategy {
|
||||
return &Strategy{
|
||||
Symbol: symbol,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: "1m"})
|
||||
session.Stream.OnKLineClosed(func(kline types.KLine) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user