Skip to content
Merged

merge #1192

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a121115
fix: Bybit order quantity precision and position fields
tinkle-community Dec 7, 2025
bee4e5d
fix: add OKX support to order/position sync and fix WebSocket concurr…
tinkle-community Dec 7, 2025
07ac8e4
fix: use http.DefaultClient for OKX trader
tinkle-community Dec 7, 2025
2334d78
refactor: simplify config and remove unused database tables
tinkle-community Dec 7, 2025
0636ced
feat: improve trading UI with interactive position table and chart tabs
tinkle-community Dec 7, 2025
a12c0ae
refactor: standardize code comments
tinkle-community Dec 7, 2025
d780c2a
refactor: simplify log format
tinkle-community Dec 7, 2025
4a0f56f
refactor: remove database pre-population and add i18n strategy templates
tinkle-community Dec 7, 2025
1004757
fix: improve UI state updates after form submissions
tinkle-community Dec 8, 2025
f39fc8a
fix: save raw AI response for debugging and require calculated numbers
tinkle-community Dec 8, 2025
8a5744e
fix: use actual fill price from exchange API for position records
tinkle-community Dec 8, 2025
24717d8
feat: use OHLCV table format for kline data in AI prompts
tinkle-community Dec 8, 2025
ce3f62c
docs: add quant data plugin API documentation
tinkle-community Dec 8, 2025
9c1a322
fix: OI Top API response parsing and quant data URL validation
tinkle-community Dec 8, 2025
7a6e6f2
fix: preserve AI model API key when updating and add default URLs
tinkle-community Dec 8, 2025
9c53a26
feat: redesign indicator editor with required raw klines and improved UX
tinkle-community Dec 8, 2025
c6f6d3b
feat: auto-restart traders that were running before shutdown
tinkle-community Dec 8, 2025
e55a6a6
feat: fix competition chart with accurate PnL calculation and improve…
tinkle-community Dec 8, 2025
9d6b631
feat: add Web3 punk avatars and official social links
tinkle-community Dec 8, 2025
30f7113
fix: calculate pnl_pct in frontend when backend doesn't return it
tinkle-community Dec 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions api/backtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (s *Server) handleBacktestStart(c *gin.Context) {
cfg.PromptTemplate = "default"
}
if _, err := decision.GetPromptTemplate(cfg.PromptTemplate); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("提示词模板不存在: %s", cfg.PromptTemplate)})
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Prompt template does not exist: %s", cfg.PromptTemplate)})
return
}
cfg.CustomPrompt = strings.TrimSpace(cfg.CustomPrompt)
Expand Down Expand Up @@ -498,9 +498,9 @@ func writeBacktestAccessError(c *gin.Context, err error) bool {
}
switch {
case errors.Is(err, errBacktestForbidden):
c.JSON(http.StatusForbidden, gin.H{"error": "无权访问该回测任务"})
c.JSON(http.StatusForbidden, gin.H{"error": "No permission to access this backtest task"})
case errors.Is(err, os.ErrNotExist), errors.Is(err, sql.ErrNoRows):
c.JSON(http.StatusNotFound, gin.H{"error": "回测任务不存在"})
c.JSON(http.StatusNotFound, gin.H{"error": "Backtest task does not exist"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
Expand All @@ -512,7 +512,7 @@ func (s *Server) resolveBacktestAIConfig(cfg *backtest.BacktestConfig, userID st
return fmt.Errorf("config is nil")
}
if s.store == nil {
return fmt.Errorf("系统数据库未就绪,无法加载AI模型配置")
return fmt.Errorf("System database not ready, cannot load AI model configuration")
}

cfg.UserID = normalizeUserID(userID)
Expand All @@ -525,7 +525,7 @@ func (s *Server) hydrateBacktestAIConfig(cfg *backtest.BacktestConfig) error {
return fmt.Errorf("config is nil")
}
if s.store == nil {
return fmt.Errorf("系统数据库未就绪,无法加载AI模型配置")
return fmt.Errorf("System database not ready, cannot load AI model configuration")
}

cfg.UserID = normalizeUserID(cfg.UserID)
Expand All @@ -539,23 +539,23 @@ func (s *Server) hydrateBacktestAIConfig(cfg *backtest.BacktestConfig) error {
if modelID != "" {
model, err = s.store.AIModel().Get(cfg.UserID, modelID)
if err != nil {
return fmt.Errorf("加载AI模型失败: %w", err)
return fmt.Errorf("Failed to load AI model: %w", err)
}
} else {
model, err = s.store.AIModel().GetDefault(cfg.UserID)
if err != nil {
return fmt.Errorf("未找到可用的AI模型: %w", err)
return fmt.Errorf("No available AI model found: %w", err)
}
cfg.AIModelID = model.ID
}

if !model.Enabled {
return fmt.Errorf("AI模型 %s 尚未启用", model.Name)
return fmt.Errorf("AI model %s is not enabled yet", model.Name)
}

apiKey := strings.TrimSpace(model.APIKey)
if apiKey == "" {
return fmt.Errorf("AI模型 %s 缺少API Key,请先在系统中配置", model.Name)
return fmt.Errorf("AI model %s is missing API Key, please configure it in the system first", model.Name)
}

cfg.AICfg.Provider = strings.ToLower(model.Provider)
Expand All @@ -569,10 +569,10 @@ func (s *Server) hydrateBacktestAIConfig(cfg *backtest.BacktestConfig) error {

if cfg.AICfg.Provider == "custom" {
if cfg.AICfg.BaseURL == "" {
return fmt.Errorf("自定义AI模型需要配置 API 地址")
return fmt.Errorf("Custom AI model requires API URL configuration")
}
if cfg.AICfg.Model == "" {
return fmt.Errorf("自定义AI模型需要配置模型名称")
return fmt.Errorf("Custom AI model requires model name configuration")
}
}

Expand Down
28 changes: 14 additions & 14 deletions api/crypto_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ import (
"github.com/gin-gonic/gin"
)

// CryptoHandler 加密 API 處理器
// CryptoHandler Encryption API handler
type CryptoHandler struct {
cryptoService *crypto.CryptoService
}

// NewCryptoHandler 創建加密處理器
// NewCryptoHandler Creates encryption handler
func NewCryptoHandler(cryptoService *crypto.CryptoService) *CryptoHandler {
return &CryptoHandler{
cryptoService: cryptoService,
}
}

// ==================== 公鑰端點 ====================
// ==================== Public Key Endpoint ====================

// HandleGetPublicKey 獲取伺服器公鑰
// HandleGetPublicKey Get server public key
func (h *CryptoHandler) HandleGetPublicKey(c *gin.Context) {
publicKey := h.cryptoService.GetPublicKeyPEM()

Expand All @@ -32,20 +32,20 @@ func (h *CryptoHandler) HandleGetPublicKey(c *gin.Context) {
})
}

// ==================== 加密數據解密端點 ====================
// ==================== Encrypted Data Decryption Endpoint ====================

// HandleDecryptSensitiveData 解密客戶端傳送的加密数据
// HandleDecryptSensitiveData Decrypt encrypted data sent from client
func (h *CryptoHandler) HandleDecryptSensitiveData(c *gin.Context) {
var payload crypto.EncryptedPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}

// 解密
// Decrypt
decrypted, err := h.cryptoService.DecryptSensitiveData(&payload)
if err != nil {
log.Printf("❌ 解密失敗: %v", err)
log.Printf("❌ Decryption failed: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Decryption failed"})
return
}
Expand All @@ -55,18 +55,18 @@ func (h *CryptoHandler) HandleDecryptSensitiveData(c *gin.Context) {
})
}

// ==================== 審計日誌查詢端點 ====================
// ==================== Audit Log Query Endpoint ====================

// 删除审计日志相关功能,在当前简化的实现中不需要
// Audit log functionality removed, not needed in current simplified implementation

// ==================== 工具函數 ====================
// ==================== Utility Functions ====================

// isValidPrivateKey 驗證私鑰格式
// isValidPrivateKey Validate private key format
func isValidPrivateKey(key string) bool {
// EVM 私鑰: 64 位十六進制 (可選 0x 前綴)
// EVM private key: 64 hex characters (optional 0x prefix)
if len(key) == 64 || (len(key) == 66 && key[:2] == "0x") {
return true
}
// TODO: 添加其他鏈的驗證
// TODO: Add validation for other chains
return false
}
Loading
Loading