feat: subscribe to RabbitMQ trade signals and notify by rules
Consume configurable queues, format signals (including period), share NotifyService with HTTP, and drop duplicate bodies within 1h.
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user