first version
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package signal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"overnight-trading-bot/internal/domain"
|
||||
)
|
||||
|
||||
func sd(raw string) decimal.Decimal {
|
||||
v, err := decimal.NewFromString(raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func baseCandidate() Candidate {
|
||||
return Candidate{
|
||||
TradeDate: time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC),
|
||||
Instrument: domain.Instrument{
|
||||
InstrumentUID: "uid",
|
||||
Ticker: "TRUR",
|
||||
ClassCode: "TQTF",
|
||||
Lot: 1,
|
||||
MinPriceIncrement: sd("0.01"),
|
||||
Currency: "RUB",
|
||||
Enabled: true,
|
||||
},
|
||||
Features: domain.FeatureSet{
|
||||
MuOn60: sd("0.002"),
|
||||
MuOn252: sd("0.001"),
|
||||
SigmaOn60: sd("0.01"),
|
||||
TStatOn60: sd("2"),
|
||||
WinOn60: sd("0.60"),
|
||||
NetEdgeBps: sd("20"),
|
||||
SpreadBps: sd("5"),
|
||||
TickBps: sd("1"),
|
||||
ADV20: sd("10000000"),
|
||||
},
|
||||
TradingStatus: domain.TradingStatusNormal,
|
||||
FreeOrderOK: true,
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineEnter(t *testing.T) {
|
||||
engine := New(Config{
|
||||
MinTStat60: sd("1.25"),
|
||||
MinWinRate60: sd("0.55"),
|
||||
MinNetEdgeBps: sd("10"),
|
||||
MinADVRUB: sd("5000000"),
|
||||
MaxSpreadBpsDefault: sd("20"),
|
||||
MaxSpreadBpsMoneyMarket: sd("5"),
|
||||
MaxSpreadBpsBondFunds: sd("10"),
|
||||
MaxSpreadBpsEquityFunds: sd("25"),
|
||||
MaxTickBps: sd("10"),
|
||||
RequireZeroCommission: true,
|
||||
MaxPositions: 5,
|
||||
})
|
||||
sig := engine.Evaluate(baseCandidate())
|
||||
if sig.Decision != domain.DecisionEnter || sig.RejectReason != "" {
|
||||
t.Fatalf("unexpected signal: %+v", sig)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineFirstRejectReason(t *testing.T) {
|
||||
engine := New(Config{MinTStat60: sd("1.25"), MinWinRate60: sd("0.55"), MinNetEdgeBps: sd("10"), MinADVRUB: sd("5000000"), MaxSpreadBpsDefault: sd("20"), MaxTickBps: sd("10"), RequireZeroCommission: true})
|
||||
c := baseCandidate()
|
||||
c.Features.MuOn60 = decimal.Zero
|
||||
c.Features.NetEdgeBps = decimal.Zero
|
||||
sig := engine.Evaluate(c)
|
||||
if sig.RejectReason != ReasonMuShort {
|
||||
t.Fatalf("reason=%s", sig.RejectReason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineUsesSkipForCapacityReasons(t *testing.T) {
|
||||
engine := New(Config{MinTStat60: sd("1.25"), MinWinRate60: sd("0.55"), MinNetEdgeBps: sd("10"), MinADVRUB: sd("5000000"), MaxSpreadBpsDefault: sd("20"), MaxTickBps: sd("10"), RequireZeroCommission: true, MaxPositions: 1})
|
||||
c := baseCandidate()
|
||||
c.OpenPositions = 1
|
||||
sig := engine.Evaluate(c)
|
||||
if sig.Decision != domain.DecisionSkip || sig.RejectReason != ReasonMaxPositions {
|
||||
t.Fatalf("unexpected skip signal: %+v", sig)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user