package trade import ( "fmt" "sig-pub/pkg/types" "sig-pub/pkg/types/decimals" ) // Exit 止盈止损策略(trading service 管理) type ICloseStrategy interface { OnKline(k types.Kline, pos *Position) (closePos bool, cause Cause) OnPrice(price float64, pos *Position) (closePos bool, cause Cause) OnSigStrategySingal(sigSide types.Side, pos *Position) (closePos bool, cause Cause) } // 平仓策略参数 type CloseStrategyParam struct { StopLossPct float64 `json:"stopLossPct"` // 固定止损 static stoploss TakeProfitPct float64 `json:"takeProfitPct"` // 固定止盈 static take profit ProfitRetracePcts [][]float64 `json:"profitRetracePcts"` // 基于最高利润回撤触发平仓 (例如 [[0.01, 0.3], [0.02, 0.2]] 最高利润超过1%时30%回撤则触发平仓,最高利润超过2%时20%回撤就触发平仓) CloseOnSideReverse bool `json:"closeOnSideReverse"` // 交易信号和持单方向相反时是否进行平仓 Fee bool `json:"fee"` // 计算止盈止损时是否包含手续费 } // CloseStrategy 平仓策略 type CloseStrategy struct { CloseStrategyParam } func NewCloseStrategy(param CloseStrategyParam) (cs *CloseStrategy, err error) { if param.StopLossPct < 0 { err = fmt.Errorf("stopLossPct can't less zero") return } cs = &CloseStrategy{CloseStrategyParam: param} return } // Update 当k线更新判断是否关闭仓位 func (s *CloseStrategy) OnKline(k types.Kline, pos *Position) (closePos bool, cause Cause) { closePrice := decimals.MustToFloat64(k.Close) return s.OnPrice(closePrice, pos) } // OnPrice 当k线更新判断是否关闭仓位 func (s *CloseStrategy) OnPrice(price float64, pos *Position) (closePos bool, cause Cause) { if !pos.Side.IsValid() { return } // update peak px if pos.Side == types.SideLong && (price > pos.PeakPx) { pos.PeakPx = price } if pos.Side == types.SideShort && (price < pos.PeakPx) { pos.PeakPx = price } entry := pos.EntryPx // side long: if pos.Side == types.SideLong { // 固定止损 if s.StopLossPct > 0 && price <= entry*(1-s.StopLossPct) { return true, CauseCloseStoploss } // 固定止盈 if s.TakeProfitPct > 0 && price >= entry*(1+s.TakeProfitPct) { return true, CauseCloseTakeprofit } // 基于最高利润动态止盈 if len(s.ProfitRetracePcts) > 0 { // peak profit fraction peakProfit := (pos.PeakPx - entry) / entry minProfitToTrail, trailingPct := float64(0), float64(0) for _, profit := range s.ProfitRetracePcts { if len(profit) != 2 { continue } _minProfitToTrail := profit[0] // 启动最高利润回撤的最小盈利阈值 _trailingPct := profit[1] // 基于最高利润回撤触发平仓 if peakProfit >= _minProfitToTrail && _minProfitToTrail > minProfitToTrail { minProfitToTrail = _minProfitToTrail trailingPct = _trailingPct } } if minProfitToTrail > 0 && trailingPct > 0 { trail := entry + (pos.PeakPx-entry)*(1-trailingPct) if price <= trail { return true, CauseCloseTrailing } } } return } // side short: if s.StopLossPct > 0 && price >= pos.EntryPx*(1+s.StopLossPct) { return true, CauseCloseStoploss } if s.TakeProfitPct > 0 && price <= pos.EntryPx*(1-s.TakeProfitPct) { return true, CauseCloseTakeprofit } // 基于最高利润动态止盈 if len(s.ProfitRetracePcts) > 0 { // peak profit fraction peakProfit := (entry - pos.PeakPx) / entry minProfitToTrail, trailingPct := float64(0), float64(0) for _, profit := range s.ProfitRetracePcts { if len(profit) != 2 { continue } _minProfitToTrail := profit[0] // 启动最高利润回撤的最小盈利阈值 _trailingPct := profit[1] // 基于最高利润回撤触发平仓 if peakProfit >= _minProfitToTrail && _minProfitToTrail >= minProfitToTrail { minProfitToTrail = _minProfitToTrail trailingPct = _trailingPct } } if minProfitToTrail > 0 && trailingPct > 0 { trail := entry - (entry-pos.PeakPx)*(1+trailingPct) if price >= trail { return true, CauseCloseTrailing } } } return } // OnSigStrategySingal 根据策略信号尝试平掉相反方向的仓位。例如策略返回 SELL 时,平掉 BUY 持仓 func (s *CloseStrategy) OnSigStrategySingal(sigSide types.Side, pos *Position) (closePos bool, cause Cause) { if !s.CloseOnSideReverse { return } return sigSide != pos.Side, CauseCloseReverseSingal }