diff --git a/internal/trading/backtest/trading_plan_backtester.go b/internal/trading/backtest/trading_plan_backtester.go index 32055df..fab64fe 100644 --- a/internal/trading/backtest/trading_plan_backtester.go +++ b/internal/trading/backtest/trading_plan_backtester.go @@ -31,9 +31,7 @@ type TradingPlanBacktester struct { sigStrategyInput types.Input tradeStrategyInput types.Input - closeStrategy trade.ICloseStrategy - riskStrategy trade.IRiskStrategy - tradeStrategy trade.ITradeStrategy + tradeAnalyzer trade.ITradeAnalyzer instanceIntervalSigStrategyContext strategy.IInstanceIntervalSigStrategyContext } @@ -84,12 +82,8 @@ func (b *TradingPlanBacktester) Init(cash float64, plan entity.TradePlan, sr *pb if err = tradeStrate.Init(b.tradeStrategyInput); err != nil { return } - // 平仓策略 - b.closeStrategy = tradeStrate - // 风险管理策略 - b.riskStrategy = tradeStrate - // 交易策略 - b.tradeStrategy = tradeStrate + // 交易分析器 + b.tradeAnalyzer = tradeStrate return } @@ -236,7 +230,7 @@ func (b *TradingPlanBacktester) forceCloseAllHoldingPosition() (err error) { // closeByKlineInterval1m k线更新时检查平仓 func (b *TradingPlanBacktester) closeByKlineInterval1m(instId string, k types.Kline) (err error) { price := decimals.MustToFloat64(k.Close) - closeTickets, err := b.closeStrategy.CloseAssessOnPrice(b.instanceIntervalSigStrategyContext, b.account, instId, price) + closeTickets, err := b.tradeAnalyzer.ExitAnalysisOnPrice(b.instanceIntervalSigStrategyContext, b.account, instId, price) if err != nil { return } @@ -255,7 +249,7 @@ func (b *TradingPlanBacktester) closeByKlineInterval1m(instId string, k types.Kl // closeBySigSingal 交易信号出现时检查平仓 func (b *TradingPlanBacktester) closeBySigSingal(instId string, sigSide types.Side, _ types.Kline) (err error) { - closeTickets, err := b.closeStrategy.CloseAssessOnSig(b.instanceIntervalSigStrategyContext, b.account, instId, sigSide) + closeTickets, err := b.tradeAnalyzer.ExitAnalysisOnSig(b.instanceIntervalSigStrategyContext, b.account, instId, sigSide) if err != nil { return } @@ -275,16 +269,8 @@ func (b *TradingPlanBacktester) closeBySigSingal(instId string, sigSide types.Si // onSideSingal 出现买卖信号 func (b *TradingPlanBacktester) onSideSingal(instId string, sigSide types.Side, _ types.Kline) (err error) { - // 买卖信号交易风险分析 - doTrade, causes, err := b.riskStrategy.RishAssess(b.instanceIntervalSigStrategyContext, b.account, instId, sigSide) - if err != nil { - return - } - if !doTrade { - _ = causes // todo 记录信号不交易原因分析 log db analyze - return - } - tickets, err := b.tradeStrategy.TradeAssess(b.instanceIntervalSigStrategyContext, b.account, instId, sigSide) + tickets, skipCause, err := b.tradeAnalyzer.EntryAnalysis(b.instanceIntervalSigStrategyContext, b.account, instId, sigSide) + _ = skipCause // todo 记录信号不交易原因 log db analyze if err != nil || len(tickets) == 0 { return } diff --git a/pkg/trade/itrade_analyzer.go b/pkg/trade/itrade_analyzer.go new file mode 100644 index 0000000..2e3e310 --- /dev/null +++ b/pkg/trade/itrade_analyzer.go @@ -0,0 +1,60 @@ +package trade + +import ( + "sig-pub/pkg/strategy" + "sig-pub/pkg/types" + + "github.com/govalues/decimal" +) + +// ITradeAnalyzer 建仓到平仓分析器 +type ITradeAnalyzer interface { + IEntryAnalyzer + IExitAnalyzer + + // Init 校验参数, 并根据参数初始化策略 + Init(input types.Input) (err error) +} + +// IEntryAnalyzer 建仓下单策略 +type IEntryAnalyzer interface { + strategy.ISigStrategy + + // 需要的各周期最小数据k线数 + CandlePeriods(ctx strategy.IInstanceIntervalSigStrategyContext) (tradeInsts []string, iPeriods *types.IntervalState[int16]) + + // EntryAnalysis 生成下单参数(交易量/方向/杠杆) 建仓分析:确定是否入场、下单价格及仓位大小 + // 币种持仓中不能改变杠杆 + // tickets 建仓单信息 + // skipCause 跳过交易信号原因 + EntryAnalysis(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, sigInstId string, sigSide types.Side) (tickets []TradeTicket, skipCause Cause, err error) +} + +// IExitAnalyzer 平仓评估评估、止盈止损策略(trading service 管理) +type IExitAnalyzer interface { + strategy.ISigStrategy + + // 需要的各周期最小数据k线数 + CandlePeriods(ctx strategy.IInstanceIntervalSigStrategyContext) (tradeInsts []string, iPeriods *types.IntervalState[int16]) + + // ExitAnalysisOnPrice 价格更新评估是否平仓 + // @return closeTicket平仓单信息 + ExitAnalysisOnPrice(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, price float64) (closeTickets []TradeTicket, err error) + + // ExitAnalysisOnSig 信号触发时评估是否平仓 + ExitAnalysisOnSig(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, sigSide types.Side) (closeTickets []TradeTicket, err error) +} + +type TradeTicket struct { + TradeType TradeType + InstId string + Side types.Side // 开仓方向 + Price float64 // 开仓价格 + Leverage int32 // 杠杆倍数 + Qty decimal.Decimal // 交易量 qty为交易产品数量 todo decimal + Interval string // k线周期 + Ktime int64 // k线时间 + Ctime int64 // 创建时间(ctime-ktime=信号延迟) + Cause Cause + TradesId []int64 // 关联交易订单id (仅平仓使用) +} diff --git a/pkg/trade/kelly_trade_strategy.go b/pkg/trade/kelly_trade_strategy.go new file mode 100644 index 0000000..b97c61a --- /dev/null +++ b/pkg/trade/kelly_trade_strategy.go @@ -0,0 +1,17 @@ +package trade + +// 1. 基于风险敞口的策略 (Risk-Based Sizing) +// 1.1 固定风险金额模型 (Fixed Fractional Risk) +// 1.2 波动率倒数模型 (Volatility Targeting / ATR Sizing) +// 2. 基于资金占比的策略 (Equity-Based Sizing) +// 2.1 固定资金比例 (Fixed Percentage of Capital) +// 2.2 最大持仓限制 (Max Position Constraints) +// 3. 基于胜率与信号的策略 (Probability & Signal Based) +// 3.1 凯利公式 (The Kelly Criterion) +// 3.2 信号强度加权 (Signal Strength Weighting) + +// 策略名称,适用场景,优点,缺点,推荐指数 +// 固定风险金额,趋势跟踪、波段交易,风险恒定,极度稳健,防守性强,计算稍繁琐,需严格设止损,⭐⭐⭐⭐⭐ (首选) +// ATR波动率模型,多品种CTA、资产组合,平滑不同品种间的波动差异,对ATR参数敏感,⭐⭐⭐⭐⭐ (机构首选) +// 固定资金比例,股票长线投资,简单直观,便于执行,忽略了品种的风险差异,⭐⭐⭐ +// 凯利公式,高频交易、高胜率策略,理论收益最大化,风险极高,极易破产,⭐⭐ (仅限高手) diff --git a/pkg/trade/sig_close_strategy.go b/pkg/trade/sig_trade_exit_strategy.go similarity index 89% rename from pkg/trade/sig_close_strategy.go rename to pkg/trade/sig_trade_exit_strategy.go index 2884841..0baae6a 100644 --- a/pkg/trade/sig_close_strategy.go +++ b/pkg/trade/sig_trade_exit_strategy.go @@ -7,9 +7,9 @@ import ( "time" ) -// CloseAssess 价格更新评估是否平仓 +// ExitAnalysis 价格更新评估是否平仓 // @return closeTicket平仓单信息 -func (s *SigTradeStrategy) CloseAssessOnPrice(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, price float64) (closeTickets []TradeTicket, err error) { +func (s *SigTradeStrategy) ExitAnalysisOnPrice(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, price float64) (closeTickets []TradeTicket, err error) { openTrades := account.GetOpenTrades(instId) if len(openTrades) == 0 { return @@ -120,8 +120,8 @@ func (s *SigTradeStrategy) closeTradeOnPrice(price float64, trd *TradeOrder) (cl return } -// CloseAssessOnSig 信号触发时评估是否平仓 -func (s *SigTradeStrategy) CloseAssessOnSig(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, sigSide types.Side) (closeTickets []TradeTicket, err error) { +// ExitAnalysis 信号触发时评估是否平仓 +func (s *SigTradeStrategy) ExitAnalysisOnSig(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, sigSide types.Side) (closeTickets []TradeTicket, err error) { if !s.closeParam.CloseOnSideReverse { return } diff --git a/pkg/trade/sig_trade_strategy.go b/pkg/trade/sig_trade_strategy.go index 9e6dedb..1fe515b 100644 --- a/pkg/trade/sig_trade_strategy.go +++ b/pkg/trade/sig_trade_strategy.go @@ -64,16 +64,12 @@ func (s *SigTradeStrategy) CandlePeriods(ctx strategy.IInstanceIntervalSigStrate return } -// RishAssess 信号风险评估, 是否进行交易 -func (s *SigTradeStrategy) RishAssess(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, sigInstId string, sigSide types.Side) (doTrade bool, cause Cause, err error) { - - return true, 0, nil -} - -// TradeAssess 生成下单参数(交易量/方向/杠杆) +// EntryAnalysis 生成下单参数(交易量/方向/杠杆) // 控制滑点, 仓位管理 // 持仓中币种不能改变杠杆 -func (s *SigTradeStrategy) TradeAssess(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, sigInstId string, sigSide types.Side) (tickets []TradeTicket, err error) { +func (s *SigTradeStrategy) EntryAnalysis(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, sigInstId string, sigSide types.Side) (tickets []TradeTicket, skipCause Cause, err error) { + // todo 买卖信号交易风险分析 + if pos := account.GetPosition(sigInstId); pos != nil { // 已持仓不能下反方向单, todo 副账户做反方向单,对冲(viceAccount) if pos.Side != sigSide { diff --git a/pkg/trade/trade_strategy.go b/pkg/trade/trade_strategy.go deleted file mode 100644 index c23f580..0000000 --- a/pkg/trade/trade_strategy.go +++ /dev/null @@ -1,61 +0,0 @@ -package trade - -import ( - "sig-pub/pkg/strategy" - "sig-pub/pkg/types" - - "github.com/govalues/decimal" -) - -// IRiskStrategy 风险控制接口 -type IRiskStrategy interface { - strategy.ISigStrategy - - // 需要的各周期最小数据k线数 - CandlePeriods(ctx strategy.IInstanceIntervalSigStrategyContext) (tradeInsts []string, iPeriods *types.IntervalState[int16]) - - // RishAssess 信号风险评估, 是否进行交易 - RishAssess(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, sigInstId string, sigSide types.Side) (ok bool, cause Cause, err error) -} - -// ITradeStrategy 下单策略 -type ITradeStrategy interface { - strategy.ISigStrategy - - // 需要的各周期最小数据k线数 - CandlePeriods(ctx strategy.IInstanceIntervalSigStrategyContext) (tradeInsts []string, iPeriods *types.IntervalState[int16]) - - // TradeAssess 生成下单参数(交易量/方向/杠杆) - // 控制滑点, 仓位管理 - // 持仓中币种不能改变杠杆 - TradeAssess(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, sigInstId string, sigSide types.Side) (tickets []TradeTicket, err error) -} - -// Exit 止盈止损策略(trading service 管理) -type ICloseStrategy interface { - strategy.ISigStrategy - - // 需要的各周期最小数据k线数 - CandlePeriods(ctx strategy.IInstanceIntervalSigStrategyContext) (tradeInsts []string, iPeriods *types.IntervalState[int16]) - - // CloseAssess 价格更新评估是否平仓 - // @return closeTicket平仓单信息 - CloseAssessOnPrice(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, price float64) (closeTickets []TradeTicket, err error) - - // CloseAssessOnSig 信号触发时评估是否平仓 - CloseAssessOnSig(ctx strategy.IInstanceIntervalSigStrategyContext, account ITradeAccount, instId string, sigSide types.Side) (closeTickets []TradeTicket, err error) -} - -type TradeTicket struct { - TradeType TradeType - InstId string - Side types.Side // 开仓方向 - Price float64 // 开仓价格 - Leverage int32 // 杠杆倍数 - Qty decimal.Decimal // 交易量 qty为交易产品数量 todo decimal - Interval string // k线周期 - Ktime int64 // k线时间 - Ctime int64 // 创建时间(ctime-ktime=信号延迟) - Cause Cause - TradesId []int64 // 关联交易订单id (仅平仓使用) -}