eighth version
This commit is contained in:
@@ -86,16 +86,24 @@ func (l Loader) LatestQuote(ctx context.Context, instrumentUID string, depth int
|
||||
if err != nil {
|
||||
return domain.OrderBook{}, err
|
||||
}
|
||||
if book.ReceivedAt.IsZero() {
|
||||
return domain.OrderBook{}, fmt.Errorf("quote received timestamp is missing")
|
||||
quoteTs := quoteTimestamp(book)
|
||||
if quoteTs.IsZero() {
|
||||
return domain.OrderBook{}, fmt.Errorf("quote timestamp is missing")
|
||||
}
|
||||
age := l.nowUTC().Sub(book.ReceivedAt)
|
||||
age := l.nowUTC().Sub(quoteTs)
|
||||
if maxAge > 0 && age > maxAge {
|
||||
return domain.OrderBook{}, fmt.Errorf("quote age %s exceeds %s", age, maxAge)
|
||||
}
|
||||
return book, nil
|
||||
}
|
||||
|
||||
func quoteTimestamp(book domain.OrderBook) time.Time {
|
||||
if !book.Time.IsZero() {
|
||||
return book.Time.UTC()
|
||||
}
|
||||
return book.ReceivedAt.UTC()
|
||||
}
|
||||
|
||||
func (l Loader) nowUTC() time.Time {
|
||||
if l.clock == nil {
|
||||
return time.Now().UTC()
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package marketdata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"overnight-trading-bot/internal/domain"
|
||||
"overnight-trading-bot/internal/tinvest"
|
||||
)
|
||||
|
||||
type fixedClock struct {
|
||||
now time.Time
|
||||
}
|
||||
|
||||
func (c fixedClock) Now() time.Time {
|
||||
return c.now
|
||||
}
|
||||
|
||||
func (c fixedClock) Sleep(<-chan struct{}, time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestLatestQuoteUsesExchangeTimestampForFreshness(t *testing.T) {
|
||||
now := time.Date(2026, 6, 8, 18, 20, 0, 0, time.UTC)
|
||||
gateway := tinvest.NewFakeGateway()
|
||||
gateway.OrderBooks["uid"] = domain.OrderBook{
|
||||
InstrumentUID: "uid",
|
||||
Time: now.Add(-2 * time.Second),
|
||||
ReceivedAt: now,
|
||||
Bids: []domain.OrderBookLevel{{Price: decimal.NewFromInt(99), QuantityLots: 10}},
|
||||
Asks: []domain.OrderBookLevel{{Price: decimal.NewFromInt(101), QuantityLots: 10}},
|
||||
}
|
||||
loader := NewLoader(nil, gateway)
|
||||
loader.SetClock(fixedClock{now: now})
|
||||
_, err := loader.LatestQuote(context.Background(), "uid", 20, time.Second)
|
||||
if err == nil || !strings.Contains(err.Error(), "quote age") {
|
||||
t.Fatalf("LatestQuote err=%v, want stale exchange timestamp rejection", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user