package cryptostrategy import ( "bytes" "encoding/json" "fmt" "strings" "time" ) type envelope struct { EventType string `json:"eventType"` CorrelationID string `json:"correlationId"` Symbol string `json:"symbol"` Direction string `json:"direction"` Payload json.RawMessage `json:"payload"` EventTime int64 `json:"eventTime"` } type payload struct { StrategyCode string `json:"strategyCode"` Period string `json:"period"` Currency string `json:"currency"` IsSale bool `json:"isSale"` IsClose bool `json:"isClose"` IsGain bool `json:"isGain"` GainTarget float64 `json:"gainTarget"` Price float64 `json:"price"` LossPrice float64 `json:"lossPrice"` GainPrices string `json:"gainPrices"` Remark string `json:"remark"` TotalGainTarget float64 `json:"totalGainTarget"` Leverage int `json:"leverage"` } type remark struct { OrderID string `json:"orderId"` Revenue string `json:"revenue"` } type Converter struct{} func NewConverter() *Converter { return &Converter{} } func (c *Converter) Convert(body []byte) (string, map[string]interface{}, error) { return Convert(body) } func Convert(body []byte) (string, map[string]interface{}, error) { var env envelope if err := json.Unmarshal(body, &env); err != nil { return "", nil, fmt.Errorf("invalid envelope: %w", err) } p, err := parsePayload(body, env.Payload) if err != nil { return "", nil, err } action := inferAction(p) event := "trade." + strings.ToLower(action) text := format(env, p, action) data := map[string]interface{}{ "eventType": env.EventType, "correlationId": env.CorrelationID, "symbol": firstNonEmpty(env.Symbol, p.Currency), "direction": env.Direction, "side": strings.ToUpper(env.Direction), "action": action, "eventTime": env.EventTime, "strategyCode": p.StrategyCode, "period": p.Period, "currency": p.Currency, "isSale": p.IsSale, "isClose": p.IsClose, "isGain": p.IsGain, "gainTarget": p.GainTarget, "price": p.Price, "lossPrice": p.LossPrice, "gainPrices": p.GainPrices, "leverage": p.Leverage, "formatted": text, "stopLossPrice": p.LossPrice, "takeProfitPrice": takeProfitPrice(p), "totalAvgPx": "", } if p.TotalGainTarget != 0 { data["totalGainTarget"] = p.TotalGainTarget } if r := parseRemark(p.Remark); r.OrderID != "" || r.Revenue != "" { if r.OrderID != "" { data["orderId"] = r.OrderID } if r.Revenue != "" { data["revenue"] = r.Revenue } } return event, data, nil } func parsePayload(body []byte, raw json.RawMessage) (payload, error) { var p payload raw = bytes.TrimSpace(raw) if len(raw) == 0 || string(raw) == "null" { if err := json.Unmarshal(body, &p); err != nil { return p, fmt.Errorf("invalid payload: %w", err) } return p, nil } var asString string if err := json.Unmarshal(raw, &asString); err == nil { asString = strings.TrimSpace(asString) if asString == "" { if err := json.Unmarshal(body, &p); err != nil { return p, fmt.Errorf("invalid payload: %w", err) } return p, nil } raw = []byte(asString) } if err := json.Unmarshal(raw, &p); err != nil { return p, fmt.Errorf("invalid payload: %w", err) } return p, nil } func inferAction(p payload) string { switch { case p.IsClose: return "CLOSE" case p.IsGain: return "GAIN" case p.IsSale: return "SELL" default: return "OPEN" } } func takeProfitPrice(p payload) interface{} { if gp := strings.TrimSpace(p.GainPrices); gp != "" { return gp } if p.IsGain && p.Price > 0 { return p.Price } return "" } func parseRemark(raw string) remark { raw = strings.TrimSpace(raw) if raw == "" { return remark{} } var r remark if err := json.Unmarshal([]byte(raw), &r); err != nil { return remark{} } return r } func format(env envelope, p payload, action string) string { symbol := firstNonEmpty(env.Symbol, p.Currency) lines := []string{actionTitle(env.Direction, action)} if symbol != "" { lines = append(lines, fmt.Sprintf("交易品种: %s", symbol)) } if p.Period != "" { lines = append(lines, fmt.Sprintf("周期: %s", p.Period)) } switch action { case "CLOSE": if p.Price > 0 { lines = append(lines, fmt.Sprintf("平仓价格: %.2f", p.Price)) } case "GAIN": if p.Price > 0 { lines = append(lines, fmt.Sprintf("止盈价格: %.2f", p.Price)) } case "SELL": if p.Price > 0 { lines = append(lines, fmt.Sprintf("卖出价格: %.2f", p.Price)) } default: if p.Price > 0 { lines = append(lines, fmt.Sprintf("开仓价格: %.2f", p.Price)) } } if p.LossPrice > 0 { lines = append(lines, fmt.Sprintf("止损价格: %.2f", p.LossPrice)) } if gp := strings.TrimSpace(p.GainPrices); gp != "" && action != "GAIN" { lines = append(lines, fmt.Sprintf("止盈价格: %s", strings.Join(splitPrices(gp), ", "))) } if p.GainTarget != 0 { lines = append(lines, fmt.Sprintf("止盈目标: %g", p.GainTarget)) } if p.Leverage > 0 { lines = append(lines, fmt.Sprintf("杠杆: %dx", p.Leverage)) } if p.StrategyCode != "" { lines = append(lines, fmt.Sprintf("策略: %s", p.StrategyCode)) } if env.EventTime > 0 { t := time.UnixMilli(env.EventTime).In(time.Local) lines = append(lines, fmt.Sprintf("Time: %s", t.Format("2006.01.02 15:04:05"))) } return strings.Join(lines, "\n") } func actionTitle(direction, action string) string { var pos string switch strings.ToUpper(direction) { case "LONG": pos = "多单" case "SHORT": pos = "空单" default: pos = direction } var act string switch action { case "OPEN": act = "开仓" case "CLOSE": act = "平仓" case "GAIN": act = "止盈" case "SELL": act = "卖出" default: act = action } return pos + act } func splitPrices(s string) []string { parts := strings.Split(s, ",") out := make([]string, 0, len(parts)) for _, p := range parts { p = strings.TrimSpace(p) if p != "" { out = append(out, p) } } return out } func firstNonEmpty(a, b string) string { if strings.TrimSpace(a) != "" { return a } return b }