mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-18 09:54:35 +08:00
fix: ensure all timestamps use UTC timezone
- Add NowFunc to GORM config for UTC auto-generated timestamps - Add .UTC() to all time.UnixMilli() calls in trader files - Add .UTC() to all time.Now() calls in store and api files - Fix TypeScript unused imports in frontend
This commit is contained in:
@@ -1452,9 +1452,9 @@ func (s *Server) recordClosePositionOrder(traderID, exchangeID, exchangeType, sy
|
|||||||
FilledQuantity: quantity,
|
FilledQuantity: quantity,
|
||||||
AvgFillPrice: exitPrice,
|
AvgFillPrice: exitPrice,
|
||||||
Commission: fee,
|
Commission: fee,
|
||||||
FilledAt: time.Now(),
|
FilledAt: time.Now().UTC(),
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now().UTC(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.store.Order().CreateOrder(orderRecord); err != nil {
|
if err := s.store.Order().CreateOrder(orderRecord); err != nil {
|
||||||
@@ -1482,7 +1482,7 @@ func (s *Server) recordClosePositionOrder(traderID, exchangeID, exchangeType, sy
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: 0,
|
RealizedPnL: 0,
|
||||||
IsMaker: false,
|
IsMaker: false,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.store.Order().CreateFill(fillRecord); err != nil {
|
if err := s.store.Order().CreateFill(fillRecord); err != nil {
|
||||||
@@ -1557,7 +1557,7 @@ func (s *Server) pollAndUpdateOrderStatus(orderRecordID int64, traderID, exchang
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: 0,
|
RealizedPnL: 0,
|
||||||
IsMaker: false,
|
IsMaker: false,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.store.Order().CreateFill(fillRecord); err != nil {
|
if err := s.store.Order().CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI
|
|||||||
"enabled": enabled,
|
"enabled": enabled,
|
||||||
"custom_api_url": customAPIURL,
|
"custom_api_url": customAPIURL,
|
||||||
"custom_model_name": customModelName,
|
"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 is not empty, update it (encryption handled by crypto.EncryptedString)
|
||||||
if apiKey != "" {
|
if apiKey != "" {
|
||||||
@@ -167,7 +167,7 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI
|
|||||||
"enabled": enabled,
|
"enabled": enabled,
|
||||||
"custom_api_url": customAPIURL,
|
"custom_api_url": customAPIURL,
|
||||||
"custom_model_name": customModelName,
|
"custom_model_name": customModelName,
|
||||||
"updated_at": time.Now(),
|
"updated_at": time.Now().UTC(),
|
||||||
}
|
}
|
||||||
if apiKey != "" {
|
if apiKey != "" {
|
||||||
updates["api_key"] = crypto.EncryptedString(apiKey)
|
updates["api_key"] = crypto.EncryptedString(apiKey)
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ func (s *ExchangeStore) Update(userID, id string, enabled bool, apiKey, secretKe
|
|||||||
"aster_signer": asterSigner,
|
"aster_signer": asterSigner,
|
||||||
"lighter_wallet_addr": lighterWalletAddr,
|
"lighter_wallet_addr": lighterWalletAddr,
|
||||||
"lighter_api_key_index": lighterApiKeyIndex,
|
"lighter_api_key_index": lighterApiKeyIndex,
|
||||||
"updated_at": time.Now(),
|
"updated_at": time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update encrypted fields if not empty
|
// 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).
|
Where("id = ? AND user_id = ?", id, userID).
|
||||||
Updates(map[string]interface{}{
|
Updates(map[string]interface{}{
|
||||||
"account_name": accountName,
|
"account_name": accountName,
|
||||||
"updated_at": time.Now(),
|
"updated_at": time.Now().UTC(),
|
||||||
})
|
})
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return result.Error
|
return result.Error
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
@@ -21,6 +22,10 @@ func DB() *gorm.DB {
|
|||||||
func InitGorm(dbPath string) (*gorm.DB, error) {
|
func InitGorm(dbPath string) (*gorm.DB, error) {
|
||||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||||
Logger: logger.Default.LogMode(logger.Silent),
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open SQLite database: %w", err)
|
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{
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
Logger: logger.Default.LogMode(logger.Silent),
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open PostgreSQL database: %w", err)
|
return nil, fmt.Errorf("failed to open PostgreSQL database: %w", err)
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ func (pb *PositionBuilder) handleOpen(
|
|||||||
Status: "OPEN",
|
Status: "OPEN",
|
||||||
Source: "sync",
|
Source: "sync",
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now().UTC(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
return pb.positionStore.CreateOpenPosition(position)
|
return pb.positionStore.CreateOpenPosition(position)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ func (s *StrategyStore) Update(strategy *Strategy) error {
|
|||||||
"config": strategy.Config,
|
"config": strategy.Config,
|
||||||
"is_public": strategy.IsPublic,
|
"is_public": strategy.IsPublic,
|
||||||
"config_visible": strategy.ConfigVisible,
|
"config_visible": strategy.ConfigVisible,
|
||||||
"updated_at": time.Now(),
|
"updated_at": time.Now().UTC(),
|
||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ func (s *UserStore) UpdateOTPVerified(userID string, verified bool) error {
|
|||||||
func (s *UserStore) UpdatePassword(userID, passwordHash string) error {
|
func (s *UserStore) UpdatePassword(userID, passwordHash string) error {
|
||||||
return s.db.Model(&User{}).Where("id = ?", userID).Updates(map[string]interface{}{
|
return s.db.Model(&User{}).Where("id = ?", userID).Updates(map[string]interface{}{
|
||||||
"password_hash": passwordHash,
|
"password_hash": passwordHash,
|
||||||
"updated_at": time.Now(),
|
"updated_at": time.Now().UTC(),
|
||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex
|
|||||||
// Normalize side for storage
|
// Normalize side for storage
|
||||||
side := strings.ToUpper(trade.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{
|
orderRecord := &store.TraderOrder{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -85,9 +86,9 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex
|
|||||||
FilledQuantity: trade.Quantity,
|
FilledQuantity: trade.Quantity,
|
||||||
AvgFillPrice: trade.Price,
|
AvgFillPrice: trade.Price,
|
||||||
Commission: trade.Fee,
|
Commission: trade.Fee,
|
||||||
FilledAt: trade.Time,
|
FilledAt: tradeTimeUTC,
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
UpdatedAt: trade.Time,
|
UpdatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert order record
|
// Insert order record
|
||||||
@@ -96,7 +97,7 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fill record
|
// Create fill record - use UTC time
|
||||||
fillRecord := &store.TraderFill{
|
fillRecord := &store.TraderFill{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -113,7 +114,7 @@ func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID string, ex
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: trade.RealizedPnL,
|
RealizedPnL: trade.RealizedPnL,
|
||||||
IsMaker: false,
|
IsMaker: false,
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := orderStore.CreateFill(fillRecord); err != nil {
|
if err := orderStore.CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -1407,7 +1407,7 @@ func (t *AsterTrader) GetTrades(startTime time.Time, limit int) ([]TradeRecord,
|
|||||||
Quantity: qty,
|
Quantity: qty,
|
||||||
RealizedPnL: pnl,
|
RealizedPnL: pnl,
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
Time: time.UnixMilli(at.Time),
|
Time: time.UnixMilli(at.Time).UTC(),
|
||||||
}
|
}
|
||||||
result = append(result, trade)
|
result = append(result, trade)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -637,7 +637,7 @@ func (at *AutoTrader) runCycle() error {
|
|||||||
TakeProfit: d.TakeProfit,
|
TakeProfit: d.TakeProfit,
|
||||||
Confidence: d.Confidence,
|
Confidence: d.Confidence,
|
||||||
Reasoning: d.Reasoning,
|
Reasoning: d.Reasoning,
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now().UTC(),
|
||||||
Success: false,
|
Success: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1976,7 +1976,7 @@ func (at *AutoTrader) recordPositionChange(orderID, symbol, side, action string,
|
|||||||
Quantity: quantity,
|
Quantity: quantity,
|
||||||
EntryPrice: price,
|
EntryPrice: price,
|
||||||
EntryOrderID: orderID,
|
EntryOrderID: orderID,
|
||||||
EntryTime: time.Now(),
|
EntryTime: time.Now().UTC(),
|
||||||
Leverage: leverage,
|
Leverage: leverage,
|
||||||
Status: "OPEN",
|
Status: "OPEN",
|
||||||
}
|
}
|
||||||
@@ -1996,7 +1996,7 @@ func (at *AutoTrader) recordPositionChange(orderID, symbol, side, action string,
|
|||||||
at.id, at.exchangeID, at.exchange,
|
at.id, at.exchangeID, at.exchange,
|
||||||
symbol, side, action,
|
symbol, side, action,
|
||||||
quantity, price, fee, 0, // realizedPnL will be calculated
|
quantity, price, fee, 0, // realizedPnL will be calculated
|
||||||
time.Now(), orderID,
|
time.Now().UTC(), orderID,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
logger.Infof(" ⚠️ Failed to process close position: %v", err)
|
logger.Infof(" ⚠️ Failed to process close position: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@@ -2049,8 +2049,8 @@ func (at *AutoTrader) createOrderRecord(orderID, symbol, action, positionSide st
|
|||||||
ReduceOnly: reduceOnly,
|
ReduceOnly: reduceOnly,
|
||||||
ClosePosition: reduceOnly,
|
ClosePosition: reduceOnly,
|
||||||
OrderAction: orderAction,
|
OrderAction: orderAction,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now().UTC(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2091,7 +2091,7 @@ func (at *AutoTrader) recordOrderFill(orderRecordID int64, exchangeOrderID, symb
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: 0, // Will be calculated for close orders
|
RealizedPnL: 0, // Will be calculated for close orders
|
||||||
IsMaker: false, // Market orders are usually taker
|
IsMaker: false, // Market orders are usually taker
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate realized PnL for close orders
|
// Calculate realized PnL for close orders
|
||||||
|
|||||||
@@ -1122,7 +1122,7 @@ func (t *FuturesTrader) GetTrades(startTime time.Time, limit int) ([]TradeRecord
|
|||||||
TradeID: strconv.FormatInt(income.TranID, 10),
|
TradeID: strconv.FormatInt(income.TranID, 10),
|
||||||
Symbol: income.Symbol,
|
Symbol: income.Symbol,
|
||||||
RealizedPnL: pnl,
|
RealizedPnL: pnl,
|
||||||
Time: time.UnixMilli(income.Time),
|
Time: time.UnixMilli(income.Time).UTC(),
|
||||||
// Note: Income API doesn't provide price, quantity, side, fee
|
// Note: Income API doesn't provide price, quantity, side, fee
|
||||||
// For accurate data, use GetTradesForSymbol with specific symbol
|
// For accurate data, use GetTradesForSymbol with specific symbol
|
||||||
}
|
}
|
||||||
@@ -1167,7 +1167,7 @@ func (t *FuturesTrader) GetTradesForSymbol(symbol string, startTime time.Time, l
|
|||||||
Quantity: qty,
|
Quantity: qty,
|
||||||
RealizedPnL: pnl,
|
RealizedPnL: pnl,
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
Time: time.UnixMilli(at.Time),
|
Time: time.UnixMilli(at.Time).UTC(),
|
||||||
}
|
}
|
||||||
trades = append(trades, trade)
|
trades = append(trades, trade)
|
||||||
}
|
}
|
||||||
@@ -1210,7 +1210,7 @@ func (t *FuturesTrader) GetTradesForSymbolFromID(symbol string, fromID int64, li
|
|||||||
Quantity: qty,
|
Quantity: qty,
|
||||||
RealizedPnL: pnl,
|
RealizedPnL: pnl,
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
Time: time.UnixMilli(at.Time),
|
Time: time.UnixMilli(at.Time).UTC(),
|
||||||
}
|
}
|
||||||
trades = append(trades, trade)
|
trades = append(trades, trade)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string
|
|||||||
// Normalize side
|
// Normalize side
|
||||||
side := strings.ToUpper(trade.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{
|
orderRecord := &store.TraderOrder{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID,
|
ExchangeID: exchangeID,
|
||||||
@@ -162,9 +163,9 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string
|
|||||||
FilledQuantity: trade.Quantity,
|
FilledQuantity: trade.Quantity,
|
||||||
AvgFillPrice: trade.Price,
|
AvgFillPrice: trade.Price,
|
||||||
Commission: trade.Fee,
|
Commission: trade.Fee,
|
||||||
FilledAt: trade.Time,
|
FilledAt: tradeTimeUTC,
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
UpdatedAt: trade.Time,
|
UpdatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert order record
|
// Insert order record
|
||||||
@@ -173,7 +174,7 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fill record
|
// Create fill record - use UTC time
|
||||||
fillRecord := &store.TraderFill{
|
fillRecord := &store.TraderFill{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID,
|
ExchangeID: exchangeID,
|
||||||
@@ -190,7 +191,7 @@ func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeID string
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: trade.RealizedPnL,
|
RealizedPnL: trade.RealizedPnL,
|
||||||
IsMaker: false,
|
IsMaker: false,
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := orderStore.CreateFill(fillRecord); err != nil {
|
if err := orderStore.CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ func (t *BitgetTrader) GetTrades(startTime time.Time, limit int) ([]BitgetTrade,
|
|||||||
FillQty: fillQty,
|
FillQty: fillQty,
|
||||||
Fee: -fee, // Bitget returns negative fee
|
Fee: -fee, // Bitget returns negative fee
|
||||||
FeeAsset: fill.FeeCcy,
|
FeeAsset: fill.FeeCcy,
|
||||||
ExecTime: time.UnixMilli(cTime),
|
ExecTime: time.UnixMilli(cTime).UTC(),
|
||||||
ProfitLoss: profit,
|
ProfitLoss: profit,
|
||||||
OrderType: "MARKET",
|
OrderType: "MARKET",
|
||||||
OrderAction: orderAction,
|
OrderAction: orderAction,
|
||||||
@@ -174,7 +174,8 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string,
|
|||||||
// Normalize side for storage
|
// Normalize side for storage
|
||||||
side := strings.ToUpper(trade.Side)
|
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{
|
orderRecord := &store.TraderOrder{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -191,9 +192,9 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string,
|
|||||||
FilledQuantity: trade.FillQty,
|
FilledQuantity: trade.FillQty,
|
||||||
AvgFillPrice: trade.FillPrice,
|
AvgFillPrice: trade.FillPrice,
|
||||||
Commission: trade.Fee,
|
Commission: trade.Fee,
|
||||||
FilledAt: trade.ExecTime,
|
FilledAt: execTimeUTC,
|
||||||
CreatedAt: trade.ExecTime,
|
CreatedAt: execTimeUTC,
|
||||||
UpdatedAt: trade.ExecTime,
|
UpdatedAt: execTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert order record
|
// Insert order record
|
||||||
@@ -202,7 +203,7 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fill record
|
// Create fill record - use UTC time
|
||||||
fillRecord := &store.TraderFill{
|
fillRecord := &store.TraderFill{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -219,7 +220,7 @@ func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID string,
|
|||||||
CommissionAsset: trade.FeeAsset,
|
CommissionAsset: trade.FeeAsset,
|
||||||
RealizedPnL: trade.ProfitLoss,
|
RealizedPnL: trade.ProfitLoss,
|
||||||
IsMaker: false,
|
IsMaker: false,
|
||||||
CreatedAt: trade.ExecTime,
|
CreatedAt: execTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := orderStore.CreateFill(fillRecord); err != nil {
|
if err := orderStore.CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -1069,8 +1069,8 @@ func (t *BitgetTrader) GetClosedPnL(startTime time.Time, limit int) ([]ClosedPnL
|
|||||||
|
|
||||||
cTime, _ := strconv.ParseInt(pos.CTime, 10, 64)
|
cTime, _ := strconv.ParseInt(pos.CTime, 10, 64)
|
||||||
uTime, _ := strconv.ParseInt(pos.UTime, 10, 64)
|
uTime, _ := strconv.ParseInt(pos.UTime, 10, 64)
|
||||||
record.EntryTime = time.UnixMilli(cTime)
|
record.EntryTime = time.UnixMilli(cTime).UTC()
|
||||||
record.ExitTime = time.UnixMilli(uTime)
|
record.ExitTime = time.UnixMilli(uTime).UTC()
|
||||||
|
|
||||||
record.CloseType = "unknown"
|
record.CloseType = "unknown"
|
||||||
records = append(records, record)
|
records = append(records, record)
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ func (t *BybitTrader) parseTradesResult(list []map[string]interface{}) ([]BybitT
|
|||||||
closedSize, _ := strconv.ParseFloat(closedSizeStr, 64)
|
closedSize, _ := strconv.ParseFloat(closedSizeStr, 64)
|
||||||
closedPnl, _ := strconv.ParseFloat(closedPnlStr, 64)
|
closedPnl, _ := strconv.ParseFloat(closedPnlStr, 64)
|
||||||
execTimeMs, _ := strconv.ParseInt(execTimeStr, 10, 64)
|
execTimeMs, _ := strconv.ParseInt(execTimeStr, 10, 64)
|
||||||
execTime := time.UnixMilli(execTimeMs)
|
execTime := time.UnixMilli(execTimeMs).UTC()
|
||||||
|
|
||||||
// Determine order action based on side and closedSize
|
// Determine order action based on side and closedSize
|
||||||
// If closedSize > 0, it's a close trade
|
// 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
|
// Normalize side for storage
|
||||||
side := strings.ToUpper(trade.Side)
|
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{
|
orderRecord := &store.TraderOrder{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -240,9 +241,9 @@ func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID string, ex
|
|||||||
FilledQuantity: trade.ExecQty,
|
FilledQuantity: trade.ExecQty,
|
||||||
AvgFillPrice: trade.ExecPrice,
|
AvgFillPrice: trade.ExecPrice,
|
||||||
Commission: trade.ExecFee,
|
Commission: trade.ExecFee,
|
||||||
FilledAt: trade.ExecTime,
|
FilledAt: execTimeUTC,
|
||||||
CreatedAt: trade.ExecTime,
|
CreatedAt: execTimeUTC,
|
||||||
UpdatedAt: trade.ExecTime,
|
UpdatedAt: execTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert order record
|
// Insert order record
|
||||||
@@ -251,7 +252,7 @@ func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID string, ex
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fill record
|
// Create fill record - use UTC time
|
||||||
fillRecord := &store.TraderFill{
|
fillRecord := &store.TraderFill{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -268,7 +269,7 @@ func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID string, ex
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: trade.ClosedPnL,
|
RealizedPnL: trade.ClosedPnL,
|
||||||
IsMaker: trade.IsMaker,
|
IsMaker: trade.IsMaker,
|
||||||
CreatedAt: trade.ExecTime,
|
CreatedAt: execTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := orderStore.CreateFill(fillRecord); err != nil {
|
if err := orderStore.CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -1032,8 +1032,8 @@ func (t *BybitTrader) parseClosedPnLResult(resultData interface{}) ([]ClosedPnLR
|
|||||||
RealizedPnL: closedPnL,
|
RealizedPnL: closedPnL,
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
Leverage: int(leverage),
|
Leverage: int(leverage),
|
||||||
EntryTime: time.UnixMilli(createdTime),
|
EntryTime: time.UnixMilli(createdTime).UTC(),
|
||||||
ExitTime: time.UnixMilli(updatedTime),
|
ExitTime: time.UnixMilli(updatedTime).UTC(),
|
||||||
OrderID: orderId,
|
OrderID: orderId,
|
||||||
CloseType: "unknown", // Bybit doesn't provide close type directly
|
CloseType: "unknown", // Bybit doesn't provide close type directly
|
||||||
ExchangeID: orderId, // Use orderId as exchange ID
|
ExchangeID: orderId, // Use orderId as exchange ID
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI
|
|||||||
positionSide = "SHORT"
|
positionSide = "SHORT"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create order record
|
// Create order record - use UTC time to avoid timezone issues
|
||||||
|
tradeTimeUTC := trade.Time.UTC()
|
||||||
orderRecord := &store.TraderOrder{
|
orderRecord := &store.TraderOrder{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -78,9 +79,9 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI
|
|||||||
FilledQuantity: trade.Quantity,
|
FilledQuantity: trade.Quantity,
|
||||||
AvgFillPrice: trade.Price,
|
AvgFillPrice: trade.Price,
|
||||||
Commission: trade.Fee,
|
Commission: trade.Fee,
|
||||||
FilledAt: trade.Time,
|
FilledAt: tradeTimeUTC,
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
UpdatedAt: trade.Time,
|
UpdatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert order record
|
// Insert order record
|
||||||
@@ -89,7 +90,7 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fill record
|
// Create fill record - use UTC time
|
||||||
fillRecord := &store.TraderFill{
|
fillRecord := &store.TraderFill{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -106,7 +107,7 @@ func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, exchangeI
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: trade.RealizedPnL,
|
RealizedPnL: trade.RealizedPnL,
|
||||||
IsMaker: false, // Hyperliquid GetTrades doesn't provide maker/taker info
|
IsMaker: false, // Hyperliquid GetTrades doesn't provide maker/taker info
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := orderStore.CreateFill(fillRecord); err != nil {
|
if err := orderStore.CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -2070,7 +2070,7 @@ func (t *HyperliquidTrader) GetTrades(startTime time.Time, limit int) ([]TradeRe
|
|||||||
Quantity: qty,
|
Quantity: qty,
|
||||||
RealizedPnL: pnl,
|
RealizedPnL: pnl,
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
Time: time.UnixMilli(fill.Time),
|
Time: time.UnixMilli(fill.Time).UTC(),
|
||||||
}
|
}
|
||||||
trades = append(trades, trade)
|
trades = append(trades, trade)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{
|
orderRecord := &store.TraderOrder{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -87,9 +88,9 @@ func (t *LighterTraderV2) SyncOrdersFromLighter(traderID string, exchangeID stri
|
|||||||
FilledQuantity: trade.Quantity,
|
FilledQuantity: trade.Quantity,
|
||||||
AvgFillPrice: trade.Price,
|
AvgFillPrice: trade.Price,
|
||||||
Commission: trade.Fee,
|
Commission: trade.Fee,
|
||||||
FilledAt: trade.Time,
|
FilledAt: tradeTimeUTC,
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
UpdatedAt: trade.Time,
|
UpdatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert order record
|
// Insert order record
|
||||||
@@ -98,7 +99,7 @@ func (t *LighterTraderV2) SyncOrdersFromLighter(traderID string, exchangeID stri
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fill record
|
// Create fill record - use UTC time
|
||||||
fillRecord := &store.TraderFill{
|
fillRecord := &store.TraderFill{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -115,7 +116,7 @@ func (t *LighterTraderV2) SyncOrdersFromLighter(traderID string, exchangeID stri
|
|||||||
CommissionAsset: "USDT",
|
CommissionAsset: "USDT",
|
||||||
RealizedPnL: trade.RealizedPnL,
|
RealizedPnL: trade.RealizedPnL,
|
||||||
IsMaker: false,
|
IsMaker: false,
|
||||||
CreatedAt: trade.Time,
|
CreatedAt: tradeTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := orderStore.CreateFill(fillRecord); err != nil {
|
if err := orderStore.CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ func (t *LighterTraderV2) GetTrades(startTime time.Time, limit int) ([]TradeReco
|
|||||||
// - signChanged with position flip: split into close + open
|
// - signChanged with position flip: split into close + open
|
||||||
|
|
||||||
const EPSILON = 0.0001
|
const EPSILON = 0.0001
|
||||||
tradeTime := time.UnixMilli(lt.Timestamp)
|
tradeTime := time.UnixMilli(lt.Timestamp).UTC()
|
||||||
|
|
||||||
// Calculate position after trade
|
// Calculate position after trade
|
||||||
var posAfter float64
|
var posAfter float64
|
||||||
@@ -628,7 +628,7 @@ func (t *LighterTraderV2) GetTrades(startTime time.Time, limit int) ([]TradeReco
|
|||||||
Quantity: qty,
|
Quantity: qty,
|
||||||
RealizedPnL: 0, // Not available in API
|
RealizedPnL: 0, // Not available in API
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
Time: time.UnixMilli(lt.Timestamp),
|
Time: time.UnixMilli(lt.Timestamp).UTC(),
|
||||||
}
|
}
|
||||||
result = append(result, trade)
|
result = append(result, trade)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ func (t *OKXTrader) GetTrades(startTime time.Time, limit int) ([]OKXTrade, error
|
|||||||
FillQtyBase: fillQtyBase,
|
FillQtyBase: fillQtyBase,
|
||||||
Fee: -fee, // OKX returns negative fee
|
Fee: -fee, // OKX returns negative fee
|
||||||
FeeAsset: fill.FeeCcy,
|
FeeAsset: fill.FeeCcy,
|
||||||
ExecTime: time.UnixMilli(ts),
|
ExecTime: time.UnixMilli(ts).UTC(),
|
||||||
IsMaker: fill.ExecType == "M",
|
IsMaker: fill.ExecType == "M",
|
||||||
OrderType: "MARKET",
|
OrderType: "MARKET",
|
||||||
OrderAction: orderAction,
|
OrderAction: orderAction,
|
||||||
@@ -197,7 +197,8 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan
|
|||||||
// Normalize side for storage
|
// Normalize side for storage
|
||||||
side := strings.ToUpper(trade.Side)
|
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{
|
orderRecord := &store.TraderOrder{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -214,9 +215,9 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan
|
|||||||
FilledQuantity: trade.FillQtyBase,
|
FilledQuantity: trade.FillQtyBase,
|
||||||
AvgFillPrice: trade.FillPrice,
|
AvgFillPrice: trade.FillPrice,
|
||||||
Commission: trade.Fee,
|
Commission: trade.Fee,
|
||||||
FilledAt: trade.ExecTime,
|
FilledAt: execTimeUTC,
|
||||||
CreatedAt: trade.ExecTime,
|
CreatedAt: execTimeUTC,
|
||||||
UpdatedAt: trade.ExecTime,
|
UpdatedAt: execTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert order record
|
// Insert order record
|
||||||
@@ -225,7 +226,7 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fill record
|
// Create fill record - use UTC time
|
||||||
fillRecord := &store.TraderFill{
|
fillRecord := &store.TraderFill{
|
||||||
TraderID: traderID,
|
TraderID: traderID,
|
||||||
ExchangeID: exchangeID, // UUID
|
ExchangeID: exchangeID, // UUID
|
||||||
@@ -242,7 +243,7 @@ func (t *OKXTrader) SyncOrdersFromOKX(traderID string, exchangeID string, exchan
|
|||||||
CommissionAsset: trade.FeeAsset,
|
CommissionAsset: trade.FeeAsset,
|
||||||
RealizedPnL: 0, // OKX fills don't include PnL per trade
|
RealizedPnL: 0, // OKX fills don't include PnL per trade
|
||||||
IsMaker: trade.IsMaker,
|
IsMaker: trade.IsMaker,
|
||||||
CreatedAt: trade.ExecTime,
|
CreatedAt: execTimeUTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := orderStore.CreateFill(fillRecord); err != nil {
|
if err := orderStore.CreateFill(fillRecord); err != nil {
|
||||||
|
|||||||
@@ -1366,8 +1366,8 @@ func (t *OKXTrader) GetClosedPnL(startTime time.Time, limit int) ([]ClosedPnLRec
|
|||||||
// Times
|
// Times
|
||||||
cTime, _ := strconv.ParseInt(pos.CTime, 10, 64)
|
cTime, _ := strconv.ParseInt(pos.CTime, 10, 64)
|
||||||
uTime, _ := strconv.ParseInt(pos.UTime, 10, 64)
|
uTime, _ := strconv.ParseInt(pos.UTime, 10, 64)
|
||||||
record.EntryTime = time.UnixMilli(cTime)
|
record.EntryTime = time.UnixMilli(cTime).UTC()
|
||||||
record.ExitTime = time.UnixMilli(uTime)
|
record.ExitTime = time.UnixMilli(uTime).UTC()
|
||||||
|
|
||||||
// Close type
|
// Close type
|
||||||
switch pos.Type {
|
switch pos.Type {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
// Force HMR Update - Reliability Fix v3 (Emergency Recovery)
|
import useSWR from 'swr'
|
||||||
import useSWR, { mutate } from 'swr'
|
|
||||||
import { api } from './lib/api'
|
import { api } from './lib/api'
|
||||||
import { TraderDashboardPage } from './pages/TraderDashboardPage'
|
import { TraderDashboardPage } from './pages/TraderDashboardPage'
|
||||||
|
|
||||||
@@ -20,13 +19,11 @@ import HeaderBar from './components/HeaderBar'
|
|||||||
import { LanguageProvider, useLanguage } from './contexts/LanguageContext'
|
import { LanguageProvider, useLanguage } from './contexts/LanguageContext'
|
||||||
import { AuthProvider, useAuth } from './contexts/AuthContext'
|
import { AuthProvider, useAuth } from './contexts/AuthContext'
|
||||||
import { ConfirmDialogProvider } from './components/ConfirmDialog'
|
import { ConfirmDialogProvider } from './components/ConfirmDialog'
|
||||||
import { t, type Language } from './i18n/translations'
|
import { t } from './i18n/translations'
|
||||||
import { confirmToast, notify } from './lib/notify'
|
|
||||||
import { useSystemConfig } from './hooks/useSystemConfig'
|
import { useSystemConfig } from './hooks/useSystemConfig'
|
||||||
|
|
||||||
import { OFFICIAL_LINKS } from './constants/branding'
|
import { OFFICIAL_LINKS } from './constants/branding'
|
||||||
import { BacktestPage } from './components/BacktestPage'
|
import { BacktestPage } from './components/BacktestPage'
|
||||||
import { LogOut, Loader2 } from 'lucide-react'
|
|
||||||
import type {
|
import type {
|
||||||
SystemStatus,
|
SystemStatus,
|
||||||
AccountInfo,
|
AccountInfo,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { motion } from 'framer-motion'
|
|
||||||
|
|
||||||
interface DeepVoidBackgroundProps extends React.HTMLAttributes<HTMLDivElement> {
|
interface DeepVoidBackgroundProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
|
||||||
import { mutate } from 'swr'
|
import { mutate } from 'swr'
|
||||||
import { api } from '../lib/api'
|
import { api } from '../lib/api'
|
||||||
import { ChartTabs } from '../components/ChartTabs'
|
import { ChartTabs } from '../components/ChartTabs'
|
||||||
|
|||||||
Reference in New Issue
Block a user