feat(crypto策略): 支持 AI 信号止盈止损事件与多字段渲染
Motivation: 区分止盈与止损事件,并为 AI crypto signals 策略提供更丰富的推送内容渲染能力(杠杆、止盈档位、收益、持仓周期等)。 Changes: * 开仓(多/空,含原 isSale 空单)统一映射为 trade.open,不再发 trade.sell * 新增 trade.gain 事件用于止盈,止损仍映射为 trade.close;HLSS 策略保持原有事件映射 * 新增 leverageText、tp1~tp5、closeAction、revenueDisplay、holdPeriod 等渲染字段 * 新增收益符号、止盈档位、持仓周期等格式化逻辑 * 补充 AI crypto signals 的模板与规则文档及单元测试
This commit is contained in:
@@ -38,6 +38,7 @@ type payload struct {
|
||||
type remark struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Revenue string `json:"revenue"`
|
||||
Period string `json:"period"`
|
||||
}
|
||||
|
||||
type Converter struct{}
|
||||
@@ -99,12 +100,28 @@ func Convert(body []byte) (string, map[string]interface{}, error) {
|
||||
if p.TotalGainTarget != 0 {
|
||||
data["totalGainTarget"] = p.TotalGainTarget
|
||||
}
|
||||
if r := parseRemark(p.Remark); r.OrderID != "" || r.Revenue != "" {
|
||||
if p.Leverage > 0 {
|
||||
data["leverageText"] = fmt.Sprintf("%dx", p.Leverage)
|
||||
}
|
||||
for i, price := range splitPrices(p.GainPrices) {
|
||||
if i >= 5 {
|
||||
break
|
||||
}
|
||||
data[fmt.Sprintf("tp%d", i+1)] = compactPrice(price)
|
||||
}
|
||||
if p.IsGain {
|
||||
data["closeAction"] = formatTPAction(p.GainTarget)
|
||||
}
|
||||
if r := parseRemark(p.Remark); r.OrderID != "" || r.Revenue != "" || r.Period != "" {
|
||||
if r.OrderID != "" {
|
||||
data["orderId"] = r.OrderID
|
||||
}
|
||||
if r.Revenue != "" {
|
||||
data["revenue"] = r.Revenue
|
||||
data["revenueDisplay"] = formatRevenue(r.Revenue, p.IsGain)
|
||||
}
|
||||
if hp := formatHoldPeriod(r.Period); hp != "" {
|
||||
data["holdPeriod"] = hp
|
||||
}
|
||||
}
|
||||
return event, data, nil
|
||||
@@ -153,13 +170,23 @@ func mergePayloadFields(data map[string]interface{}, payloadJSON []byte) {
|
||||
}
|
||||
|
||||
func inferAction(p payload) string {
|
||||
if strings.EqualFold(strings.TrimSpace(p.StrategyCode), "HLSS") {
|
||||
switch {
|
||||
case p.IsClose:
|
||||
return "CLOSE"
|
||||
case p.IsGain:
|
||||
return "GAIN"
|
||||
case p.IsSale:
|
||||
return "SELL"
|
||||
default:
|
||||
return "OPEN"
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case p.IsClose:
|
||||
return "CLOSE"
|
||||
case p.IsGain:
|
||||
return "GAIN"
|
||||
case p.IsSale:
|
||||
return "SELL"
|
||||
case p.IsClose:
|
||||
return "CLOSE"
|
||||
default:
|
||||
return "OPEN"
|
||||
}
|
||||
@@ -320,3 +347,67 @@ func firstNonEmpty(a, b string) string {
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func formatTPAction(gainTarget float64) string {
|
||||
n := int(gainTarget)
|
||||
names := []string{"", "第一", "第二", "第三", "第四", "第五"}
|
||||
if n >= 1 && n < len(names) {
|
||||
return fmt.Sprintf("到达%s止盈 (TP%d)", names[n], n)
|
||||
}
|
||||
return "到达止盈"
|
||||
}
|
||||
|
||||
func formatRevenue(raw string, isGain bool) string {
|
||||
s := strings.ReplaceAll(strings.TrimSpace(raw), "%", "")
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(s, "+"), strings.HasPrefix(s, "-"):
|
||||
return s + "%"
|
||||
case isGain:
|
||||
return "+" + s + "%"
|
||||
default:
|
||||
return "-" + s + "%"
|
||||
}
|
||||
}
|
||||
|
||||
func formatHoldPeriod(raw string) string {
|
||||
s := strings.TrimSpace(raw)
|
||||
if s == "" || strings.EqualFold(s, "signal") {
|
||||
return ""
|
||||
}
|
||||
if strings.Contains(s, "小时") || strings.Contains(s, "分钟") {
|
||||
return s
|
||||
}
|
||||
lower := strings.ToLower(s)
|
||||
if n, ok := parseTrailingNumber(strings.TrimSpace(strings.TrimSuffix(lower, "min"))); ok {
|
||||
return formatMinutes(n)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func parseTrailingNumber(s string) (int, bool) {
|
||||
s = strings.TrimSpace(s)
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
func formatMinutes(n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
}
|
||||
h, m := n/60, n%60
|
||||
switch {
|
||||
case h > 0 && m > 0:
|
||||
return fmt.Sprintf("%d小时%d分钟", h, m)
|
||||
case h > 0:
|
||||
return fmt.Sprintf("%d小时", h)
|
||||
default:
|
||||
return fmt.Sprintf("%d分钟", n)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user