f7ea2e7cf9
Motivation: 新增巴菲特激进/稳健策略(PUTEJJ/PUTEWJ)的跟单推送能力。该类信号仅有 rawMessage、无 action 字段,需按原文直接推送,并过滤启动文案、本地化时间字段。 Changes: * 支持无 action 的纯 rawMessage 信号,映射为 trade.message 事件 * 新增 not_contains 条件操作符,用于过滤启动文案 * 新增 replace 模板函数,将 Time: 替换为推送时间: * 补充巴菲特策略模板、规则与渠道配置文档
89 lines
2.0 KiB
Go
89 lines
2.0 KiB
Go
package tradesignal
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"aiaa-notification-service/internal/config"
|
|
)
|
|
|
|
var ErrInvalidSignal = errors.New("invalid signal")
|
|
|
|
type Converter struct {
|
|
overrides map[string]config.StrategyOverride
|
|
positions *Tracker
|
|
}
|
|
|
|
func NewConverter(overrides map[string]config.StrategyOverride) *Converter {
|
|
return &Converter{
|
|
overrides: overrides,
|
|
positions: NewTracker(),
|
|
}
|
|
}
|
|
|
|
func (c *Converter) Convert(body []byte) (string, map[string]interface{}, error) {
|
|
var sig Signal
|
|
if err := json.Unmarshal(body, &sig); err != nil {
|
|
return "", nil, fmt.Errorf("%w: %v", ErrInvalidSignal, err)
|
|
}
|
|
if strings.TrimSpace(sig.Action) == "" {
|
|
if strings.TrimSpace(sig.RawMessage) == "" {
|
|
return "", nil, fmt.Errorf("%w: missing action", ErrInvalidSignal)
|
|
}
|
|
data, err := toData(body, &sig)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
return "trade.message", data, nil
|
|
}
|
|
out := Apply(&sig, c.overrideFor(sig.StrategyCode))
|
|
snap := c.positions.Apply(out)
|
|
var opts FormatOptions
|
|
if snap.HasAvg {
|
|
avg := snap.AvgPrice
|
|
opts.AvgPrice = &avg
|
|
}
|
|
text := Format(out, opts)
|
|
data, err := toData(body, out)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
data["formatted"] = text
|
|
if snap.HasAvg {
|
|
data["avgPrice"] = snap.AvgPrice
|
|
}
|
|
return "trade." + strings.ToLower(out.Action), data, nil
|
|
}
|
|
|
|
func (c *Converter) overrideFor(code string) *config.StrategyOverride {
|
|
if c == nil || len(c.overrides) == 0 || code == "" {
|
|
return nil
|
|
}
|
|
override, ok := c.overrides[code]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return &override
|
|
}
|
|
|
|
func toData(body []byte, sig *Signal) (map[string]interface{}, error) {
|
|
data := make(map[string]interface{})
|
|
if err := json.Unmarshal(body, &data); err != nil {
|
|
return nil, err
|
|
}
|
|
raw, err := json.Marshal(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
overlay := make(map[string]interface{})
|
|
if err := json.Unmarshal(raw, &overlay); err != nil {
|
|
return nil, err
|
|
}
|
|
for k, v := range overlay {
|
|
data[k] = v
|
|
}
|
|
return data, nil
|
|
}
|