6f846a0a3c
Consume configurable queues, format signals (including period), share NotifyService with HTTP, and drop duplicate bodies within 1h.
199 lines
5.1 KiB
Go
199 lines
5.1 KiB
Go
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
|
|
}
|