You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
2.2 KiB
77 lines
2.2 KiB
package backtest |
|
|
|
import ( |
|
"math" |
|
"sig-pub/pkg/data" |
|
"sig-pub/pkg/trade" |
|
"sig-pub/pkg/types" |
|
"sig-pub/pkg/types/decimals" |
|
) |
|
|
|
type TradeSimulator struct { |
|
FeePct float64 // e.g. 0.0005 = 0.05% |
|
SlippagePct float64 // e.g. 0.001 = 0.1% |
|
|
|
} |
|
|
|
func NewTradeSimulator(feePct, slippagePct float64) *TradeSimulator { |
|
return &TradeSimulator{FeePct: feePct, SlippagePct: slippagePct} |
|
} |
|
|
|
// ExecuteMarket 执行市价单,使用kline信息决定成交价(使用close以及滑点) |
|
func (s *TradeSimulator) ExecuteMarket(tradeId int64, symbol string, ticket trade.TradeTicket) (order *trade.TradeOrder) { |
|
price := ticket.Price |
|
slippage := s.SlippagePct |
|
switch ticket.Side { |
|
default: |
|
panic("invalid trade side") |
|
case types.SideLong: |
|
// buy: worse price higher |
|
price = price * (1 + slippage) |
|
case types.SideShort: |
|
// sell: worse price lower |
|
price = price * (1 - slippage) |
|
} |
|
ticketQty := decimals.MustToFloat64(ticket.Qty) |
|
fee := math.Abs(price*ticketQty) * s.FeePct |
|
order = &trade.TradeOrder{ |
|
TradeId: tradeId, |
|
TradeType: ticket.TradeType, |
|
InstId: symbol, |
|
Side: ticket.Side, |
|
Qty: ticket.Qty, |
|
Price: price, |
|
Fee: fee, |
|
Leverage: ticket.Leverage, |
|
Ctime: ticket.Ktime, |
|
Status: data.StatusOk, |
|
PeakPx: ticket.Price, |
|
CloseCause: ticket.Cause, |
|
} |
|
return |
|
} |
|
|
|
// ExecuteLimit 简单实现: 如果limit价格被kline的high/low包含则成交 |
|
func (s *TradeSimulator) ExecuteLimit(side types.Side, qty float64, limitPx float64, k types.Kline, ts int64) (trd *Trade, filled bool) { |
|
h := decimals.MustToFloat64(k.High) |
|
l := decimals.MustToFloat64(k.Low) |
|
switch side { |
|
case types.SideLong: |
|
// buy limit: filled if low <= price |
|
if l <= limitPx { |
|
// assume filled at min(limitPx, open) |
|
px := math.Min(limitPx, decimals.MustToFloat64(k.Open)) |
|
fee := math.Abs(px*qty) * s.FeePct |
|
trd = &Trade{Side: side, Qty: qty, Price: px * (1 + s.SlippagePct), Fee: fee, Time: ts} |
|
return trd, true |
|
} |
|
case types.SideShort: |
|
if h >= limitPx { |
|
px := math.Max(limitPx, decimals.MustToFloat64(k.Open)) |
|
fee := math.Abs(px*qty) * s.FeePct |
|
trd = &Trade{Side: side, Qty: qty, Price: px * (1 - s.SlippagePct), Fee: fee, Time: ts} |
|
return trd, true |
|
} |
|
} |
|
return nil, false |
|
}
|
|
|