mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-13 15:57:01 +08:00
Sync manual closes into position history
This commit is contained in:
@@ -42,3 +42,95 @@ func TestGetOpenPositionBySymbolMatchesSideCaseInsensitively(t *testing.T) {
|
||||
t.Fatalf("entry time mismatch: got %d want %d", got.EntryTime, entryTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClosedPositionsByTraderFiltersIncludesLegacyAutopilotIDs(t *testing.T) {
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("open in-memory sqlite: %v", err)
|
||||
}
|
||||
|
||||
positions := NewPositionStore(db)
|
||||
if err := positions.InitTables(); err != nil {
|
||||
t.Fatalf("init position table: %v", err)
|
||||
}
|
||||
|
||||
now := time.Now().UnixMilli()
|
||||
rows := []*TraderPosition{
|
||||
{
|
||||
TraderID: "current-trader",
|
||||
Symbol: "xyz:SP500",
|
||||
Side: "LONG",
|
||||
Quantity: 1,
|
||||
EntryPrice: 100,
|
||||
EntryTime: now - 3000,
|
||||
ExitPrice: 101,
|
||||
ExitTime: now - 2000,
|
||||
RealizedPnL: 1,
|
||||
Status: "CLOSED",
|
||||
CreatedAt: now - 3000,
|
||||
UpdatedAt: now - 2000,
|
||||
CloseReason: "sync",
|
||||
ExchangeType: "hyperliquid",
|
||||
},
|
||||
{
|
||||
TraderID: "exchange_user-123_claw402_111",
|
||||
Symbol: "AAVEUSDT",
|
||||
Side: "LONG",
|
||||
Quantity: 2,
|
||||
EntryPrice: 50,
|
||||
EntryTime: now - 5000,
|
||||
ExitPrice: 49,
|
||||
ExitTime: now - 4000,
|
||||
RealizedPnL: -2,
|
||||
Status: "CLOSED",
|
||||
CreatedAt: now - 5000,
|
||||
UpdatedAt: now - 4000,
|
||||
CloseReason: "sync",
|
||||
ExchangeType: "hyperliquid",
|
||||
},
|
||||
{
|
||||
TraderID: "exchange_other-user_claw402_222",
|
||||
Symbol: "LITUSDT",
|
||||
Side: "LONG",
|
||||
Quantity: 3,
|
||||
EntryPrice: 10,
|
||||
EntryTime: now - 7000,
|
||||
ExitPrice: 12,
|
||||
ExitTime: now - 6000,
|
||||
RealizedPnL: 6,
|
||||
Status: "CLOSED",
|
||||
CreatedAt: now - 7000,
|
||||
UpdatedAt: now - 6000,
|
||||
CloseReason: "sync",
|
||||
ExchangeType: "hyperliquid",
|
||||
},
|
||||
}
|
||||
for _, row := range rows {
|
||||
if err := db.Create(row).Error; err != nil {
|
||||
t.Fatalf("create position: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
got, err := positions.GetClosedPositionsByTraderFilters(
|
||||
[]string{"current-trader"},
|
||||
[]string{"%_user-123_claw402_%"},
|
||||
100,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("get closed positions: %v", err)
|
||||
}
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("expected current + same-user legacy positions, got %d", len(got))
|
||||
}
|
||||
|
||||
stats, err := positions.GetFullStatsByTraderFilters(
|
||||
[]string{"current-trader"},
|
||||
[]string{"%_user-123_claw402_%"},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("get stats: %v", err)
|
||||
}
|
||||
if stats.TotalTrades != 2 || stats.TotalPnL != -1 {
|
||||
t.Fatalf("unexpected stats: trades=%d pnl=%.2f", stats.TotalTrades, stats.TotalPnL)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user