diff --git a/api/server.go b/api/server.go index 401d9ef0..5825df06 100644 --- a/api/server.go +++ b/api/server.go @@ -1452,9 +1452,9 @@ func (s *Server) recordClosePositionOrder(traderID, exchangeID, exchangeType, sy FilledQuantity: quantity, AvgFillPrice: exitPrice, Commission: fee, - FilledAt: time.Now(), - CreatedAt: time.Now(), - UpdatedAt: time.Now(), + FilledAt: time.Now().UTC(), + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), } if err := s.store.Order().CreateOrder(orderRecord); err != nil { @@ -1482,7 +1482,7 @@ func (s *Server) recordClosePositionOrder(traderID, exchangeID, exchangeType, sy CommissionAsset: "USDT", RealizedPnL: 0, IsMaker: false, - CreatedAt: time.Now(), + CreatedAt: time.Now().UTC(), } if err := s.store.Order().CreateFill(fillRecord); err != nil { @@ -1557,7 +1557,7 @@ func (s *Server) pollAndUpdateOrderStatus(orderRecordID int64, traderID, exchang CommissionAsset: "USDT", RealizedPnL: 0, IsMaker: false, - CreatedAt: time.Now(), + CreatedAt: time.Now().UTC(), } if err := s.store.Order().CreateFill(fillRecord); err != nil { diff --git a/store/ai_model.go b/store/ai_model.go index 9950acba..a9047866 100644 --- a/store/ai_model.go +++ b/store/ai_model.go @@ -149,7 +149,7 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI "enabled": enabled, "custom_api_url": customAPIURL, "custom_model_name": customModelName, - "updated_at": time.Now(), + "updated_at": time.Now().UTC(), } // If apiKey is not empty, update it (encryption handled by crypto.EncryptedString) if apiKey != "" { @@ -167,7 +167,7 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI "enabled": enabled, "custom_api_url": customAPIURL, "custom_model_name": customModelName, - "updated_at": time.Now(), + "updated_at": time.Now().UTC(), } if apiKey != "" { updates["api_key"] = crypto.EncryptedString(apiKey) diff --git a/store/exchange.go b/store/exchange.go index cb210355..e3b30e2f 100644 --- a/store/exchange.go +++ b/store/exchange.go @@ -236,7 +236,7 @@ func (s *ExchangeStore) Update(userID, id string, enabled bool, apiKey, secretKe "aster_signer": asterSigner, "lighter_wallet_addr": lighterWalletAddr, "lighter_api_key_index": lighterApiKeyIndex, - "updated_at": time.Now(), + "updated_at": time.Now().UTC(), } // Only update encrypted fields if not empty @@ -275,7 +275,7 @@ func (s *ExchangeStore) UpdateAccountName(userID, id, accountName string) error Where("id = ? AND user_id = ?", id, userID). Updates(map[string]interface{}{ "account_name": accountName, - "updated_at": time.Now(), + "updated_at": time.Now().UTC(), }) if result.Error != nil { return result.Error diff --git a/store/gorm.go b/store/gorm.go index f3340275..a5ad8f28 100644 --- a/store/gorm.go +++ b/store/gorm.go @@ -2,6 +2,7 @@ package store import ( "fmt" + "time" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" @@ -21,6 +22,10 @@ func DB() *gorm.DB { func InitGorm(dbPath string) (*gorm.DB, error) { db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), + // Use UTC for all auto-generated timestamps (autoCreateTime, autoUpdateTime) + NowFunc: func() time.Time { + return time.Now().UTC() + }, }) if err != nil { return nil, fmt.Errorf("failed to open SQLite database: %w", err) @@ -53,6 +58,10 @@ func InitGormPostgres(host string, port int, user, password, dbname, sslmode str db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), + // Use UTC for all auto-generated timestamps (autoCreateTime, autoUpdateTime) + NowFunc: func() time.Time { + return time.Now().UTC() + }, }) if err != nil { return nil, fmt.Errorf("failed to open PostgreSQL database: %w", err) diff --git a/store/position_builder.go b/store/position_builder.go index 9b30b517..880928fe 100644 --- a/store/position_builder.go +++ b/store/position_builder.go @@ -69,8 +69,8 @@ func (pb *PositionBuilder) handleOpen( Status: "OPEN", Source: "sync", Fee: fee, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), } return pb.positionStore.CreateOpenPosition(position) } diff --git a/store/strategy.go b/store/strategy.go index 719d53d9..1b3f5b11 100644 --- a/store/strategy.go +++ b/store/strategy.go @@ -328,7 +328,7 @@ func (s *StrategyStore) Update(strategy *Strategy) error { "config": strategy.Config, "is_public": strategy.IsPublic, "config_visible": strategy.ConfigVisible, - "updated_at": time.Now(), + "updated_at": time.Now().UTC(), }).Error } diff --git a/store/user.go b/store/user.go index b840f22c..9c878757 100644 --- a/store/user.go +++ b/store/user.go @@ -123,7 +123,7 @@ func (s *UserStore) UpdateOTPVerified(userID string, verified bool) error { func (s *UserStore) UpdatePassword(userID, passwordHash string) error { return s.db.Model(&User{}).Where("id = ?", userID).Updates(map[string]interface{}{ "password_hash": passwordHash, - "updated_at": time.Now(), + "updated_at": time.Now().UTC(), }).Error } diff --git a/trader/aster_order_sync.go b/trader/aster_order_sync.go index 42f264a8..e993138c 100644 --- a/trader/aster_order_sync.go +++ b/trader/aster_order_sync.go @@ -68,7 +68,8 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex // Normalize side for storage side := strings.ToUpper(trade.Side) - // Create order record + // Create order record - use UTC time to avoid timezone issues + tradeTimeUTC := trade.Time.UTC() orderRecord := &store.TraderOrder{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -85,9 +86,9 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex FilledQuantity: trade.Quantity, AvgFillPrice: trade.Price, Commission: trade.Fee, - FilledAt: trade.Time, - CreatedAt: trade.Time, - UpdatedAt: trade.Time, + FilledAt: tradeTimeUTC, + CreatedAt: tradeTimeUTC, + UpdatedAt: tradeTimeUTC, } // Insert order record @@ -96,7 +97,7 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex continue } - // Create fill record + // Create fill record - use UTC time fillRecord := &store.TraderFill{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -113,7 +114,7 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex CommissionAsset: "USDT", RealizedPnL: trade.RealizedPnL, IsMaker: false, - CreatedAt: trade.Time, + CreatedAt: tradeTimeUTC, } if err := orderStore.CreateFill(fillRecord); err != nil { diff --git a/trader/aster_trader.go b/trader/aster_trader.go index a07ffbe9..b75c9579 100644 --- a/trader/aster_trader.go +++ b/trader/aster_trader.go @@ -1407,7 +1407,7 @@ func (t *AsterTrader) GetTrades(startTime time.Time, limit int) ([]TradeRecord, Quantity: qty, RealizedPnL: pnl, Fee: fee, - Time: time.UnixMilli(at.Time), + Time: time.UnixMilli(at.Time).UTC(), } result = append(result, trade) } diff --git a/trader/auto_trader.go b/trader/auto_trader.go index 328152d8..e95bb8a6 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -637,7 +637,7 @@ func (at *AutoTrader) runCycle() error { TakeProfit: d.TakeProfit, Confidence: d.Confidence, Reasoning: d.Reasoning, - Timestamp: time.Now(), + Timestamp: time.Now().UTC(), Success: false, } @@ -1976,7 +1976,7 @@ func (at *AutoTrader) recordPositionChange(orderID, symbol, side, action string, Quantity: quantity, EntryPrice: price, EntryOrderID: orderID, - EntryTime: time.Now(), + EntryTime: time.Now().UTC(), Leverage: leverage, Status: "OPEN", } @@ -1996,7 +1996,7 @@ func (at *AutoTrader) recordPositionChange(orderID, symbol, side, action string, at.id, at.exchangeID, at.exchange, symbol, side, action, quantity, price, fee, 0, // realizedPnL will be calculated - time.Now(), orderID, + time.Now().UTC(), orderID, ); err != nil { logger.Infof(" ⚠️ Failed to process close position: %v", err) } else { @@ -2049,8 +2049,8 @@ func (at *AutoTrader) createOrderRecord(orderID, symbol, action, positionSide st ReduceOnly: reduceOnly, ClosePosition: reduceOnly, OrderAction: orderAction, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), } } @@ -2091,7 +2091,7 @@ func (at *AutoTrader) recordOrderFill(orderRecordID int64, exchangeOrderID, symb CommissionAsset: "USDT", RealizedPnL: 0, // Will be calculated for close orders IsMaker: false, // Market orders are usually taker - CreatedAt: time.Now(), + CreatedAt: time.Now().UTC(), } // Calculate realized PnL for close orders diff --git a/trader/binance_futures.go b/trader/binance_futures.go index c169f82e..72d1de67 100644 --- a/trader/binance_futures.go +++ b/trader/binance_futures.go @@ -1122,7 +1122,7 @@ func (t *FuturesTrader) GetTrades(startTime time.Time, limit int) ([]TradeRecord TradeID: strconv.FormatInt(income.TranID, 10), Symbol: income.Symbol, RealizedPnL: pnl, - Time: time.UnixMilli(income.Time), + Time: time.UnixMilli(income.Time).UTC(), // Note: Income API doesn't provide price, quantity, side, fee // For accurate data, use GetTradesForSymbol with specific symbol } @@ -1167,7 +1167,7 @@ func (t *FuturesTrader) GetTradesForSymbol(symbol string, startTime time.Time, l Quantity: qty, RealizedPnL: pnl, Fee: fee, - Time: time.UnixMilli(at.Time), + Time: time.UnixMilli(at.Time).UTC(), } trades = append(trades, trade) } @@ -1210,7 +1210,7 @@ func (t *FuturesTrader) GetTradesForSymbolFromID(symbol string, fromID int64, li Quantity: qty, RealizedPnL: pnl, Fee: fee, - Time: time.UnixMilli(at.Time), + Time: time.UnixMilli(at.Time).UTC(), } trades = append(trades, trade) } diff --git a/trader/binance_order_sync.go b/trader/binance_order_sync.go index 40819574..6fe685fe 100644 --- a/trader/binance_order_sync.go +++ b/trader/binance_order_sync.go @@ -145,7 +145,8 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string // Normalize side side := strings.ToUpper(trade.Side) - // Create order record + // Create order record - use UTC time to avoid timezone issues + tradeTimeUTC := trade.Time.UTC() orderRecord := &store.TraderOrder{ TraderID: traderID, ExchangeID: exchangeID, @@ -162,9 +163,9 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string FilledQuantity: trade.Quantity, AvgFillPrice: trade.Price, Commission: trade.Fee, - FilledAt: trade.Time, - CreatedAt: trade.Time, - UpdatedAt: trade.Time, + FilledAt: tradeTimeUTC, + CreatedAt: tradeTimeUTC, + UpdatedAt: tradeTimeUTC, } // Insert order record @@ -173,7 +174,7 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string continue } - // Create fill record + // Create fill record - use UTC time fillRecord := &store.TraderFill{ TraderID: traderID, ExchangeID: exchangeID, @@ -190,7 +191,7 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string CommissionAsset: "USDT", RealizedPnL: trade.RealizedPnL, IsMaker: false, - CreatedAt: trade.Time, + CreatedAt: tradeTimeUTC, } if err := orderStore.CreateFill(fillRecord); err != nil { diff --git a/trader/bitget_order_sync.go b/trader/bitget_order_sync.go index f501114e..1f4d6384 100644 --- a/trader/bitget_order_sync.go +++ b/trader/bitget_order_sync.go @@ -110,7 +110,7 @@ func (t *BitgetTrader) GetTrades(startTime time.Time, limit int) ([]BitgetTrade, FillQty: fillQty, Fee: -fee, // Bitget returns negative fee FeeAsset: fill.FeeCcy, - ExecTime: time.UnixMilli(cTime), + ExecTime: time.UnixMilli(cTime).UTC(), ProfitLoss: profit, OrderType: "MARKET", OrderAction: orderAction, @@ -174,7 +174,8 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string, // Normalize side for storage side := strings.ToUpper(trade.Side) - // Create order record + // Create order record - use UTC time to avoid timezone issues + execTimeUTC := trade.ExecTime.UTC() orderRecord := &store.TraderOrder{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -191,9 +192,9 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string, FilledQuantity: trade.FillQty, AvgFillPrice: trade.FillPrice, Commission: trade.Fee, - FilledAt: trade.ExecTime, - CreatedAt: trade.ExecTime, - UpdatedAt: trade.ExecTime, + FilledAt: execTimeUTC, + CreatedAt: execTimeUTC, + UpdatedAt: execTimeUTC, } // Insert order record @@ -202,7 +203,7 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string, continue } - // Create fill record + // Create fill record - use UTC time fillRecord := &store.TraderFill{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -219,7 +220,7 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string, CommissionAsset: trade.FeeAsset, RealizedPnL: trade.ProfitLoss, IsMaker: false, - CreatedAt: trade.ExecTime, + CreatedAt: execTimeUTC, } if err := orderStore.CreateFill(fillRecord); err != nil { diff --git a/trader/bitget_trader.go b/trader/bitget_trader.go index 6893a045..19a7d891 100644 --- a/trader/bitget_trader.go +++ b/trader/bitget_trader.go @@ -1069,8 +1069,8 @@ func (t *BitgetTrader) GetClosedPnL(startTime time.Time, limit int) ([]ClosedPnL cTime, _ := strconv.ParseInt(pos.CTime, 10, 64) uTime, _ := strconv.ParseInt(pos.UTime, 10, 64) - record.EntryTime = time.UnixMilli(cTime) - record.ExitTime = time.UnixMilli(uTime) + record.EntryTime = time.UnixMilli(cTime).UTC() + record.ExitTime = time.UnixMilli(uTime).UTC() record.CloseType = "unknown" records = append(records, record) diff --git a/trader/bybit_order_sync.go b/trader/bybit_order_sync.go index 76afe8fb..21e67c06 100644 --- a/trader/bybit_order_sync.go +++ b/trader/bybit_order_sync.go @@ -127,7 +127,7 @@ func (t *BybitTrader) parseTradesResult(list []map[string]interface{}) ([]BybitT closedSize, _ := strconv.ParseFloat(closedSizeStr, 64) closedPnl, _ := strconv.ParseFloat(closedPnlStr, 64) execTimeMs, _ := strconv.ParseInt(execTimeStr, 10, 64) - execTime := time.UnixMilli(execTimeMs) + execTime := time.UnixMilli(execTimeMs).UTC() // Determine order action based on side and closedSize // If closedSize > 0, it's a close trade @@ -223,7 +223,8 @@ func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID string, ex // Normalize side for storage side := strings.ToUpper(trade.Side) - // Create order record + // Create order record - use UTC time to avoid timezone issues + execTimeUTC := trade.ExecTime.UTC() orderRecord := &store.TraderOrder{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -240,9 +241,9 @@ func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID string, ex FilledQuantity: trade.ExecQty, AvgFillPrice: trade.ExecPrice, Commission: trade.ExecFee, - FilledAt: trade.ExecTime, - CreatedAt: trade.ExecTime, - UpdatedAt: trade.ExecTime, + FilledAt: execTimeUTC, + CreatedAt: execTimeUTC, + UpdatedAt: execTimeUTC, } // Insert order record @@ -251,7 +252,7 @@ func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID string, ex continue } - // Create fill record + // Create fill record - use UTC time fillRecord := &store.TraderFill{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -268,7 +269,7 @@ func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID string, ex CommissionAsset: "USDT", RealizedPnL: trade.ClosedPnL, IsMaker: trade.IsMaker, - CreatedAt: trade.ExecTime, + CreatedAt: execTimeUTC, } if err := orderStore.CreateFill(fillRecord); err != nil { diff --git a/trader/bybit_trader.go b/trader/bybit_trader.go index 540a1518..46c5ff50 100644 --- a/trader/bybit_trader.go +++ b/trader/bybit_trader.go @@ -1032,8 +1032,8 @@ func (t *BybitTrader) parseClosedPnLResult(resultData interface{}) ([]ClosedPnLR RealizedPnL: closedPnL, Fee: fee, Leverage: int(leverage), - EntryTime: time.UnixMilli(createdTime), - ExitTime: time.UnixMilli(updatedTime), + EntryTime: time.UnixMilli(createdTime).UTC(), + ExitTime: time.UnixMilli(updatedTime).UTC(), OrderID: orderId, CloseType: "unknown", // Bybit doesn't provide close type directly ExchangeID: orderId, // Use orderId as exchange ID diff --git a/trader/hyperliquid_order_sync.go b/trader/hyperliquid_order_sync.go index aff5b23a..22efdd7c 100644 --- a/trader/hyperliquid_order_sync.go +++ b/trader/hyperliquid_order_sync.go @@ -61,7 +61,8 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI positionSide = "SHORT" } - // Create order record + // Create order record - use UTC time to avoid timezone issues + tradeTimeUTC := trade.Time.UTC() orderRecord := &store.TraderOrder{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -78,9 +79,9 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI FilledQuantity: trade.Quantity, AvgFillPrice: trade.Price, Commission: trade.Fee, - FilledAt: trade.Time, - CreatedAt: trade.Time, - UpdatedAt: trade.Time, + FilledAt: tradeTimeUTC, + CreatedAt: tradeTimeUTC, + UpdatedAt: tradeTimeUTC, } // Insert order record @@ -89,7 +90,7 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI continue } - // Create fill record + // Create fill record - use UTC time fillRecord := &store.TraderFill{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -106,7 +107,7 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI CommissionAsset: "USDT", RealizedPnL: trade.RealizedPnL, IsMaker: false, // Hyperliquid GetTrades doesn't provide maker/taker info - CreatedAt: trade.Time, + CreatedAt: tradeTimeUTC, } if err := orderStore.CreateFill(fillRecord); err != nil { diff --git a/trader/hyperliquid_trader.go b/trader/hyperliquid_trader.go index 0f1d3ca9..74cd7295 100644 --- a/trader/hyperliquid_trader.go +++ b/trader/hyperliquid_trader.go @@ -2070,7 +2070,7 @@ func (t *HyperliquidTrader) GetTrades(startTime time.Time, limit int) ([]TradeRe Quantity: qty, RealizedPnL: pnl, Fee: fee, - Time: time.UnixMilli(fill.Time), + Time: time.UnixMilli(fill.Time).UTC(), } trades = append(trades, trade) } diff --git a/trader/lighter_order_sync.go b/trader/lighter_order_sync.go index 03735c33..d37973ab 100644 --- a/trader/lighter_order_sync.go +++ b/trader/lighter_order_sync.go @@ -70,7 +70,8 @@ func (t *LighterTraderV2) SyncOrdersFromLighter(traderID string, exchangeID stri } } - // Create order record + // Create order record - use UTC time to avoid timezone issues + tradeTimeUTC := trade.Time.UTC() orderRecord := &store.TraderOrder{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -87,9 +88,9 @@ func (t *LighterTraderV2) SyncOrdersFromLighter(traderID string, exchangeID stri FilledQuantity: trade.Quantity, AvgFillPrice: trade.Price, Commission: trade.Fee, - FilledAt: trade.Time, - CreatedAt: trade.Time, - UpdatedAt: trade.Time, + FilledAt: tradeTimeUTC, + CreatedAt: tradeTimeUTC, + UpdatedAt: tradeTimeUTC, } // Insert order record @@ -98,7 +99,7 @@ func (t *LighterTraderV2) SyncOrdersFromLighter(traderID string, exchangeID stri continue } - // Create fill record + // Create fill record - use UTC time fillRecord := &store.TraderFill{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -115,7 +116,7 @@ func (t *LighterTraderV2) SyncOrdersFromLighter(traderID string, exchangeID stri CommissionAsset: "USDT", RealizedPnL: trade.RealizedPnL, IsMaker: false, - CreatedAt: trade.Time, + CreatedAt: tradeTimeUTC, } if err := orderStore.CreateFill(fillRecord); err != nil { diff --git a/trader/lighter_trader_v2.go b/trader/lighter_trader_v2.go index 3256cf17..6abdf405 100644 --- a/trader/lighter_trader_v2.go +++ b/trader/lighter_trader_v2.go @@ -537,7 +537,7 @@ func (t *LighterTraderV2) GetTrades(startTime time.Time, limit int) ([]TradeReco // - signChanged with position flip: split into close + open const EPSILON = 0.0001 - tradeTime := time.UnixMilli(lt.Timestamp) + tradeTime := time.UnixMilli(lt.Timestamp).UTC() // Calculate position after trade var posAfter float64 @@ -628,7 +628,7 @@ func (t *LighterTraderV2) GetTrades(startTime time.Time, limit int) ([]TradeReco Quantity: qty, RealizedPnL: 0, // Not available in API Fee: fee, - Time: time.UnixMilli(lt.Timestamp), + Time: time.UnixMilli(lt.Timestamp).UTC(), } result = append(result, trade) } diff --git a/trader/okx_order_sync.go b/trader/okx_order_sync.go index 99f537d4..d1feb81c 100644 --- a/trader/okx_order_sync.go +++ b/trader/okx_order_sync.go @@ -133,7 +133,7 @@ func (t *OKXTrader) GetTrades(startTime time.Time, limit int) ([]OKXTrade, error FillQtyBase: fillQtyBase, Fee: -fee, // OKX returns negative fee FeeAsset: fill.FeeCcy, - ExecTime: time.UnixMilli(ts), + ExecTime: time.UnixMilli(ts).UTC(), IsMaker: fill.ExecType == "M", OrderType: "MARKET", OrderAction: orderAction, @@ -197,7 +197,8 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan // Normalize side for storage side := strings.ToUpper(trade.Side) - // Create order record + // Create order record - use UTC time to avoid timezone issues + execTimeUTC := trade.ExecTime.UTC() orderRecord := &store.TraderOrder{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -214,9 +215,9 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan FilledQuantity: trade.FillQtyBase, AvgFillPrice: trade.FillPrice, Commission: trade.Fee, - FilledAt: trade.ExecTime, - CreatedAt: trade.ExecTime, - UpdatedAt: trade.ExecTime, + FilledAt: execTimeUTC, + CreatedAt: execTimeUTC, + UpdatedAt: execTimeUTC, } // Insert order record @@ -225,7 +226,7 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan continue } - // Create fill record + // Create fill record - use UTC time fillRecord := &store.TraderFill{ TraderID: traderID, ExchangeID: exchangeID, // UUID @@ -242,7 +243,7 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan CommissionAsset: trade.FeeAsset, RealizedPnL: 0, // OKX fills don't include PnL per trade IsMaker: trade.IsMaker, - CreatedAt: trade.ExecTime, + CreatedAt: execTimeUTC, } if err := orderStore.CreateFill(fillRecord); err != nil { diff --git a/trader/okx_trader.go b/trader/okx_trader.go index a57322c2..26120aa8 100644 --- a/trader/okx_trader.go +++ b/trader/okx_trader.go @@ -1366,8 +1366,8 @@ func (t *OKXTrader) GetClosedPnL(startTime time.Time, limit int) ([]ClosedPnLRec // Times cTime, _ := strconv.ParseInt(pos.CTime, 10, 64) uTime, _ := strconv.ParseInt(pos.UTime, 10, 64) - record.EntryTime = time.UnixMilli(cTime) - record.ExitTime = time.UnixMilli(uTime) + record.EntryTime = time.UnixMilli(cTime).UTC() + record.ExitTime = time.UnixMilli(uTime).UTC() // Close type switch pos.Type { diff --git a/web/src/App.tsx b/web/src/App.tsx index 920d1a7b..0c3c050a 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,7 +1,6 @@ -import { useEffect, useState, useRef } from 'react' +import { useEffect, useState } from 'react' import { motion, AnimatePresence } from 'framer-motion' -// Force HMR Update - Reliability Fix v3 (Emergency Recovery) -import useSWR, { mutate } from 'swr' +import useSWR from 'swr' import { api } from './lib/api' import { TraderDashboardPage } from './pages/TraderDashboardPage' @@ -20,13 +19,11 @@ import HeaderBar from './components/HeaderBar' import { LanguageProvider, useLanguage } from './contexts/LanguageContext' import { AuthProvider, useAuth } from './contexts/AuthContext' import { ConfirmDialogProvider } from './components/ConfirmDialog' -import { t, type Language } from './i18n/translations' -import { confirmToast, notify } from './lib/notify' +import { t } from './i18n/translations' import { useSystemConfig } from './hooks/useSystemConfig' import { OFFICIAL_LINKS } from './constants/branding' import { BacktestPage } from './components/BacktestPage' -import { LogOut, Loader2 } from 'lucide-react' import type { SystemStatus, AccountInfo, diff --git a/web/src/components/DeepVoidBackground.tsx b/web/src/components/DeepVoidBackground.tsx index de4e73d0..6ab2357a 100644 --- a/web/src/components/DeepVoidBackground.tsx +++ b/web/src/components/DeepVoidBackground.tsx @@ -1,5 +1,4 @@ import React from 'react' -import { motion } from 'framer-motion' interface DeepVoidBackgroundProps extends React.HTMLAttributes { children?: React.ReactNode diff --git a/web/src/pages/TraderDashboardPage.tsx b/web/src/pages/TraderDashboardPage.tsx index 9eba7515..1666ceee 100644 --- a/web/src/pages/TraderDashboardPage.tsx +++ b/web/src/pages/TraderDashboardPage.tsx @@ -1,5 +1,4 @@ import { useEffect, useState, useRef } from 'react' -import { motion, AnimatePresence } from 'framer-motion' import { mutate } from 'swr' import { api } from '../lib/api' import { ChartTabs } from '../components/ChartTabs'