all: add livenote and livenoteposter, move all example strategy into pkg/strategy/example

This commit is contained in:
c9s 2024-11-05 16:02:34 +08:00
parent d8150a17b9
commit e310ea9e4e
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
10 changed files with 101 additions and 7 deletions

View File

@ -392,10 +392,10 @@ persistence:
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). See
- `pricealert` strategy demonstrates how to use the notification system [pricealert](pkg/strategy/example/pricealert). See
[document](./doc/strategy/pricealert.md).
- `buyandhold` strategy demonstrates how to subscribe kline events and submit market
order [buyandhold](pkg/strategy/pricedrop)
order [buyandhold](pkg/strategy/example/pricedrop)
- `bollgrid` strategy implements a basic grid strategy with the built-in bollinger
indicator [bollgrid](pkg/strategy/bollgrid)
- `grid` strategy implements the fixed price band grid strategy [grid](pkg/strategy/grid). See

View File

@ -370,8 +370,8 @@ persistence:
查看策略目錄 [strategy](pkg/strategy) 以獲得所有內置策略:
- `pricealert` 策略演示如何使用通知系統 [pricealert](pkg/strategy/pricealert)。參見[文件](./doc/strategy/pricealert.md).
- `buyandhold` 策略演示如何訂閱 kline 事件並提交市場訂單 [buyandhold](pkg/strategy/pricedrop)
- `pricealert` 策略演示如何使用通知系統 [pricealert](pkg/strategy/example/pricealert)。參見[文件](./doc/strategy/pricealert.md).
- `buyandhold` 策略演示如何訂閱 kline 事件並提交市場訂單 [buyandhold](pkg/strategy/example/pricedrop)
- `bollgrid` 策略實現了一個基本的網格策略,使用內置的布林通道指標 [bollgrid](pkg/strategy/bollgrid)
- `grid` 策略實現了固定價格帶網格策略 [grid](pkg/strategy/grid)。參見[文件](./doc/strategy/grid.md).
- `supertrend` 策略使用 Supertrend 指標作為趨勢,並使用 DEMA 指標作為噪聲

21
pkg/bbgo/livenote.go Normal file
View File

@ -0,0 +1,21 @@
package bbgo
import (
"github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/types"
)
// PostLiveNote posts a live note to slack or other services
// The MessageID will be set after the message is posted if it's not set.
func PostLiveNote(note *types.LiveNote) {
for _, poster := range Notification.liveNotePosters {
if err := poster.PostLiveNote(note); err != nil {
logrus.WithError(err).Errorf("unable to post live note: %+v", note)
}
}
}
type LiveNotePoster interface {
PostLiveNote(note *types.LiveNote) error
}

View File

@ -49,6 +49,8 @@ func (n *NullNotifier) SendPhotoTo(channel string, buffer *bytes.Buffer) {}
type Notifiability struct {
notifiers []Notifier
liveNotePosters []LiveNotePoster
SessionChannelRouter *PatternChannelRouter `json:"-"`
SymbolChannelRouter *PatternChannelRouter `json:"-"`
ObjectChannelRouter *ObjectChannelRouter `json:"-"`
@ -81,6 +83,10 @@ func (m *Notifiability) RouteObject(obj interface{}) (channel string, ok bool) {
// AddNotifier adds the notifier that implements the Notifier interface.
func (m *Notifiability) AddNotifier(notifier Notifier) {
m.notifiers = append(m.notifiers, notifier)
if poster, ok := notifier.(LiveNotePoster); ok {
m.liveNotePosters = append(m.liveNotePosters, poster)
}
}
func (m *Notifiability) Notify(obj interface{}, args ...interface{}) {

View File

@ -54,7 +54,8 @@ func (n *Notifier) worker() {
return
case task := <-n.taskC:
limiter.Wait(ctx)
// ignore the wait error
_ = limiter.Wait(ctx)
_, _, err := n.client.PostMessageContext(ctx, task.Channel, task.Opts...)
if err != nil {
@ -66,7 +67,40 @@ func (n *Notifier) worker() {
}
}
func (n *Notifier) PostLiveNote(note *types.LiveNote) error {
ctx := context.Background()
channel := note.ChannelID
if channel == "" {
channel = n.channel
}
if note.MessageID != "" {
// UpdateMessageContext returns channel, timestamp, text, err
_, _, _, err := n.client.UpdateMessageContext(ctx, channel, note.MessageID, slack.MsgOptionText(note.ObjectID(), true))
if err != nil {
return err
}
} else {
respCh, respTs, err := n.client.PostMessageContext(ctx, channel)
if err != nil {
log.WithError(err).
WithField("channel", n.channel).
Errorf("slack api error: %s", err.Error())
return err
}
note.SetChannelID(respCh)
note.SetMessageID(respTs)
}
return nil
}
func (n *Notifier) Notify(obj interface{}, args ...interface{}) {
// TODO: filter args for the channel option
n.NotifyTo(n.channel, obj, args...)
}

33
pkg/types/livenote.go Normal file
View File

@ -0,0 +1,33 @@
package types
type LiveNoteObject interface {
ObjectID() string
}
type LiveNote struct {
// MessageID is the unique identifier of the message
// for slack, it's the timestamp of the message
MessageID string `json:"messageId"`
ChannelID string `json:"channelId"`
Object LiveNoteObject
}
func NewLiveNote(object LiveNoteObject) *LiveNote {
return &LiveNote{
Object: object,
}
}
func (n *LiveNote) ObjectID() string {
return n.Object.ObjectID()
}
func (n *LiveNote) SetMessageID(messageID string) {
n.MessageID = messageID
}
func (n *LiveNote) SetChannelID(channelID string) {
n.ChannelID = channelID
}