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.
 
 

66 lines
1.9 KiB

package backtest
import (
"math"
"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%
TradeId int64
}
func NewTradeSimulator(feePct, slippagePct float64) *TradeSimulator {
return &TradeSimulator{FeePct: feePct, SlippagePct: slippagePct}
}
// ExecuteMarket 执行市价单,使用kline信息决定成交价(使用close以及滑点)
func (s *TradeSimulator) ExecuteMarket(side types.Side, qty float64, closePrice float64, ts int64) (trd *Trade, ok bool) {
// base price use close
base := closePrice
slippage := s.SlippagePct
switch side {
case types.SideLong:
// buy: worse price higher
base = base * (1 + slippage)
case types.SideShort:
// sell: worse price lower
base = base * (1 - slippage)
default:
return
}
fee := math.Abs(base*qty) * s.FeePct
trd = &Trade{Side: side, Qty: qty, Price: base, Fee: fee, Time: ts}
s.TradeId++
trd.Id = s.TradeId
ok = true
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
}