package tradesignal import ( "fmt" "strings" "time" ) type FormatOptions struct { AvgPrice *float64 } func Format(signal *Signal, opts ...FormatOptions) string { var opt FormatOptions if len(opts) > 0 { opt = opts[0] } title := actionTitle(signal.Side, signal.Action) symbol := trimQuote(signal.Symbol) lines := []string{title} lines = append(lines, fmt.Sprintf("交易品种: %s", symbol)) if p := strings.TrimSpace(signal.Period); p != "" { lines = append(lines, fmt.Sprintf("周期: %s", p)) } action := strings.ToUpper(signal.Action) switch action { case "OPEN": lines = append(lines, fmt.Sprintf("开仓价格: %.2f", signal.Price)) if line := sizeLine("OPEN", signal.Quantity, signal.AmountMarginRatio); line != "" { lines = append(lines, line) } lines = appendAvgPrice(lines, opt.AvgPrice) if signal.Leverage > 0 { lines = append(lines, fmt.Sprintf("杠杆: %dx", signal.Leverage)) } if signal.TakeProfitPrice != nil { lines = append(lines, fmt.Sprintf("止盈价格: %.2f", *signal.TakeProfitPrice)) } if signal.StopLossPrice != nil { lines = append(lines, fmt.Sprintf("止损价格: %.2f", *signal.StopLossPrice)) } case "CLOSE": lines = append(lines, fmt.Sprintf("平仓价格: %.2f", signal.Price)) lines = append(lines, closeSizeLine(signal.Quantity, signal.PosMarginRatio)) lines = appendAvgPrice(lines, opt.AvgPrice) if signal.PnL != nil { lines = append(lines, fmt.Sprintf("平仓盈亏: %.2f", *signal.PnL)) } if signal.AccountBalance != nil { lines = append(lines, fmt.Sprintf("账户余额:%.2f", *signal.AccountBalance)) } case "ADD": lines = append(lines, fmt.Sprintf("加仓价格: %.2f", signal.Price)) if line := sizeLine("ADD", signal.Quantity, signal.AmountMarginRatio); line != "" { lines = append(lines, line) } lines = appendAvgPrice(lines, opt.AvgPrice) if signal.Leverage > 0 { lines = append(lines, fmt.Sprintf("杠杆: %dx", signal.Leverage)) } case "REDUCE": lines = append(lines, fmt.Sprintf("减仓价格: %.2f", signal.Price)) if line := sizeLine("REDUCE", signal.Quantity, signal.PosMarginRatio); line != "" { lines = append(lines, line) } lines = appendAvgPrice(lines, opt.AvgPrice) if signal.PnL != nil { lines = append(lines, fmt.Sprintf("减仓盈亏: %.2f", *signal.PnL)) } if signal.AccountBalance != nil { lines = append(lines, fmt.Sprintf("账户余额:%.2f", *signal.AccountBalance)) } default: lines = append(lines, fmt.Sprintf("价格: %.2f", signal.Price)) if line := sizeLine("", signal.Quantity, signal.AmountMarginRatio); line != "" { lines = append(lines, line) } lines = appendAvgPrice(lines, opt.AvgPrice) } if signal.StrategyCode != "" { lines = append(lines, fmt.Sprintf("策略: %s", signal.StrategyCode)) } eventTime := signal.ParsedEventTime().In(time.Local) lines = append(lines, fmt.Sprintf("Time: %s", eventTime.Format("2006.01.02 15:04:05"))) return strings.Join(lines, "\n") } func actionTitle(side, action string) string { side = strings.ToUpper(side) action = strings.ToUpper(action) var pos string switch side { case "LONG": pos = "多单" case "SHORT": pos = "空单" default: pos = side } var act string switch action { case "OPEN": act = "开仓" case "ADD": act = "加仓" case "CLOSE": act = "平仓" case "REDUCE": act = "减仓" default: act = action } return pos + act } func appendAvgPrice(lines []string, avgPrice *float64) []string { if avgPrice == nil || *avgPrice <= 0 { return lines } return append(lines, fmt.Sprintf("平均单价: %.2f", *avgPrice)) } func closeSizeLine(quantity, posMarginRatio *float64) string { if quantity != nil && *quantity > 0 { return fmt.Sprintf("平仓数量: %.2f", *quantity) } ratio := 1.0 if posMarginRatio != nil { ratio = *posMarginRatio } return fmt.Sprintf("平仓比例: %s", formatPercent(ratio)) } func sizeLine(action string, quantity, marginRatio *float64) string { if quantity != nil && *quantity > 0 { return fmt.Sprintf("%s: %.2f", quantityLabel(action), *quantity) } if marginRatio != nil { return fmt.Sprintf("%s: %s", ratioLabel(action), formatPercent(*marginRatio)) } if quantity != nil { return fmt.Sprintf("%s: %.2f", quantityLabel(action), *quantity) } return "" } func quantityLabel(action string) string { switch strings.ToUpper(action) { case "OPEN": return "开仓数量" case "ADD": return "加仓数量" case "CLOSE": return "平仓数量" case "REDUCE": return "减仓数量" default: return "数量" } } func ratioLabel(action string) string { switch strings.ToUpper(action) { case "OPEN": return "开仓比例" case "ADD": return "加仓比例" case "CLOSE": return "平仓比例" case "REDUCE": return "减仓比例" default: return "仓位比例" } } func formatPercent(ratio float64) string { return fmt.Sprintf("%.2f%%", ratio*100) } func trimQuote(symbol string) string { symbol = strings.ToUpper(symbol) for _, suffix := range []string{"USDT", "USDC", "BUSD", "USD"} { if strings.HasSuffix(symbol, suffix) && len(symbol) > len(suffix) { return symbol[:len(symbol)-len(suffix)] } } return symbol }