seventh version
This commit is contained in:
@@ -492,7 +492,7 @@ func (s *Scheduler) placeEntryOrders(ctx context.Context, now time.Time) error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
pre, err := s.preTradeCheck(ctx, now, sig.InstrumentUID, portfolio, projectedOpenPositions, tradingStatus, book.ReceivedAt)
|
||||
pre, err := s.preTradeCheck(ctx, now, sig.InstrumentUID, portfolio, projectedOpenPositions, false, tradingStatus, book.ReceivedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -567,7 +567,11 @@ func (s *Scheduler) monitorEntryOrders(ctx context.Context, now time.Time) error
|
||||
return err
|
||||
}
|
||||
if monitored.FilledLots > order.FilledLots || monitored.Commission.GreaterThan(order.Commission) {
|
||||
if err := s.recordEntryFill(ctx, instrument, monitored); err != nil {
|
||||
fill := entryFillDelta(order, monitored)
|
||||
if fill.FilledLots <= 0 && fill.Commission.IsZero() {
|
||||
continue
|
||||
}
|
||||
if err := s.recordEntryFill(ctx, instrument, fill); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -660,7 +664,7 @@ func (s *Scheduler) placeExitOrders(ctx context.Context, now time.Time) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pre, err := s.preTradeCheck(ctx, now, pos.InstrumentUID, portfolio, len(positionsList), tradingStatus, book.ReceivedAt)
|
||||
pre, err := s.preTradeCheck(ctx, now, pos.InstrumentUID, portfolio, len(positionsList), true, tradingStatus, book.ReceivedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1173,7 +1177,7 @@ func (s Scheduler) repostPreTradeCheck(ctx context.Context, now time.Time, order
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pre, err := s.preTradeCheck(ctx, now, order.InstrumentUID, portfolio, len(openPositions), tradingStatus, book.ReceivedAt)
|
||||
pre, err := s.preTradeCheck(ctx, now, order.InstrumentUID, portfolio, len(openPositions), order.Side == domain.SideSell, tradingStatus, book.ReceivedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1194,7 +1198,7 @@ func (s Scheduler) checkEntryInstrumentBeforeOrder(instrument domain.Instrument,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Scheduler) preTradeCheck(ctx context.Context, now time.Time, instrumentUID string, portfolio domain.Portfolio, openPositions int, tradingStatus domain.TradingStatus, quoteReceivedAt time.Time) (risk.PreTradeResult, error) {
|
||||
func (s Scheduler) preTradeCheck(ctx context.Context, now time.Time, instrumentUID string, portfolio domain.Portfolio, openPositions int, closingPosition bool, tradingStatus domain.TradingStatus, quoteReceivedAt time.Time) (risk.PreTradeResult, error) {
|
||||
metrics, err := s.riskMetrics(ctx, now, portfolio)
|
||||
if err != nil {
|
||||
if haltErr := s.halt(ctx, "database_unavailable", fmt.Sprintf("pre-trade risk metrics unavailable: %s", err), instrumentUID); haltErr != nil {
|
||||
@@ -1209,6 +1213,7 @@ func (s Scheduler) preTradeCheck(ctx context.Context, now time.Time, instrumentU
|
||||
result := s.svc.Risk.PreTradeCheck(risk.PreTradeInput{
|
||||
Portfolio: portfolio,
|
||||
OpenPositions: openPositions,
|
||||
ClosingPosition: closingPosition,
|
||||
DailyPnL: metrics.dailyPnL,
|
||||
WeeklyPnL: metrics.weeklyPnL,
|
||||
MonthlyDrawdownPct: metrics.monthlyDrawdownPct,
|
||||
@@ -1509,6 +1514,28 @@ func (s Scheduler) logWarn(msg string, args ...any) {
|
||||
}
|
||||
}
|
||||
|
||||
func entryFillDelta(previous, current domain.Order) domain.Order {
|
||||
fill := current
|
||||
fill.FilledLots = current.FilledLots - previous.FilledLots
|
||||
if fill.FilledLots < 0 {
|
||||
fill.FilledLots = 0
|
||||
}
|
||||
fill.Commission = current.Commission.Sub(previous.Commission)
|
||||
if fill.Commission.IsNegative() {
|
||||
fill.Commission = decimal.Zero
|
||||
}
|
||||
if fill.FilledLots > 0 {
|
||||
currentValue := current.AvgFillPrice.Mul(decimal.NewFromInt(current.FilledLots))
|
||||
previousValue := previous.AvgFillPrice.Mul(decimal.NewFromInt(previous.FilledLots))
|
||||
fill.AvgFillPrice = currentValue.Sub(previousValue).Div(decimal.NewFromInt(fill.FilledLots))
|
||||
}
|
||||
fill.QuantityLots = current.QuantityLots - previous.FilledLots
|
||||
if fill.QuantityLots < 0 {
|
||||
fill.QuantityLots = 0
|
||||
}
|
||||
return fill
|
||||
}
|
||||
|
||||
func exitFillDelta(previous, current domain.Order) domain.Order {
|
||||
fill := current
|
||||
fill.FilledLots = current.FilledLots - previous.FilledLots
|
||||
|
||||
@@ -162,6 +162,36 @@ func TestExitFillDeltaUsesOnlyNewlyExecutedLots(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntryFillDeltaUsesOnlyNewlyExecutedLots(t *testing.T) {
|
||||
previous := domain.Order{
|
||||
QuantityLots: 10,
|
||||
FilledLots: 4,
|
||||
AvgFillPrice: decimal.NewFromInt(100),
|
||||
Commission: decimal.NewFromFloat(0.40),
|
||||
InstrumentUID: "uid",
|
||||
}
|
||||
current := domain.Order{
|
||||
QuantityLots: 10,
|
||||
FilledLots: 10,
|
||||
AvgFillPrice: decimal.NewFromInt(106),
|
||||
Commission: decimal.NewFromFloat(1.00),
|
||||
InstrumentUID: "uid",
|
||||
}
|
||||
fill := entryFillDelta(previous, current)
|
||||
if fill.FilledLots != 6 {
|
||||
t.Fatalf("delta filled lots=%d, want 6", fill.FilledLots)
|
||||
}
|
||||
if fill.QuantityLots != 6 {
|
||||
t.Fatalf("delta quantity lots=%d, want 6 remaining target", fill.QuantityLots)
|
||||
}
|
||||
if !fill.AvgFillPrice.Equal(decimal.NewFromInt(110)) {
|
||||
t.Fatalf("delta avg fill price=%s, want 110", fill.AvgFillPrice)
|
||||
}
|
||||
if !fill.Commission.Equal(decimal.NewFromFloat(0.60)) {
|
||||
t.Fatalf("delta commission=%s, want 0.60", fill.Commission)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHardDeadlineMarksOpenPositionFailedAndHalts(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo := testutil.NewMemoryRepository()
|
||||
@@ -344,7 +374,7 @@ func TestPreTradeDailyLossBreachHalts(t *testing.T) {
|
||||
_, err := s.preTradeCheck(ctx, now, "uid", domain.Portfolio{
|
||||
Equity: decimal.NewFromInt(10000),
|
||||
Cash: decimal.NewFromInt(10000),
|
||||
}, 0, domain.TradingStatusNormal, now)
|
||||
}, 0, false, domain.TradingStatusNormal, now)
|
||||
if !errors.Is(err, statemachine.ErrSystemHalted) {
|
||||
t.Fatalf("err=%v, want ErrSystemHalted", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user