feat: Add scan_interval_minutes configuration support (PR #338)

- Allow users to customize AI decision scan interval (1-60 minutes)
- Default 3 minutes, recommended range 3-10 minutes
- Support both high-frequency and low-frequency trading strategies
- Improve trading flexibility and API call efficiency
- Merge with existing actual balance query functionality
This commit is contained in:
ZhouYongyou
2025-11-03 22:34:44 +08:00
5 changed files with 55 additions and 16 deletions

View File

@@ -210,6 +210,7 @@ type CreateTraderRequest struct {
AIModelID string `json:"ai_model_id" binding:"required"` AIModelID string `json:"ai_model_id" binding:"required"`
ExchangeID string `json:"exchange_id" binding:"required"` ExchangeID string `json:"exchange_id" binding:"required"`
InitialBalance float64 `json:"initial_balance"` InitialBalance float64 `json:"initial_balance"`
ScanIntervalMinutes int `json:"scan_interval_minutes"`
BTCETHLeverage int `json:"btc_eth_leverage"` BTCETHLeverage int `json:"btc_eth_leverage"`
AltcoinLeverage int `json:"altcoin_leverage"` AltcoinLeverage int `json:"altcoin_leverage"`
TradingSymbols string `json:"trading_symbols"` TradingSymbols string `json:"trading_symbols"`
@@ -332,6 +333,12 @@ func (s *Server) handleCreateTrader(c *gin.Context) {
systemPromptTemplate = req.SystemPromptTemplate systemPromptTemplate = req.SystemPromptTemplate
} }
// 设置扫描间隔默认值
scanIntervalMinutes := req.ScanIntervalMinutes
if scanIntervalMinutes <= 0 {
scanIntervalMinutes = 3 // 默认3分钟
}
// ✨ 查询交易所实际余额,覆盖用户输入 // ✨ 查询交易所实际余额,覆盖用户输入
actualBalance := req.InitialBalance // 默认使用用户输入 actualBalance := req.InitialBalance // 默认使用用户输入
exchanges, err := s.database.GetExchanges(userID) exchanges, err := s.database.GetExchanges(userID)
@@ -416,7 +423,7 @@ func (s *Server) handleCreateTrader(c *gin.Context) {
OverrideBasePrompt: req.OverrideBasePrompt, OverrideBasePrompt: req.OverrideBasePrompt,
SystemPromptTemplate: systemPromptTemplate, SystemPromptTemplate: systemPromptTemplate,
IsCrossMargin: isCrossMargin, IsCrossMargin: isCrossMargin,
ScanIntervalMinutes: 3, // 默认3分钟 ScanIntervalMinutes: scanIntervalMinutes,
IsRunning: false, IsRunning: false,
} }
@@ -456,6 +463,7 @@ type UpdateTraderRequest struct {
CustomPrompt string `json:"custom_prompt"` CustomPrompt string `json:"custom_prompt"`
OverrideBasePrompt bool `json:"override_base_prompt"` OverrideBasePrompt bool `json:"override_base_prompt"`
IsCrossMargin *bool `json:"is_cross_margin"` IsCrossMargin *bool `json:"is_cross_margin"`
ScanIntervalMinutes int `json:"scan_interval_minutes"`
} }
// handleUpdateTrader 更新交易员配置 // handleUpdateTrader 更新交易员配置
@@ -505,6 +513,12 @@ func (s *Server) handleUpdateTrader(c *gin.Context) {
altcoinLeverage = existingTrader.AltcoinLeverage // 保持原值 altcoinLeverage = existingTrader.AltcoinLeverage // 保持原值
} }
// 设置扫描间隔,允许更新
scanIntervalMinutes := req.ScanIntervalMinutes
if scanIntervalMinutes <= 0 {
scanIntervalMinutes = existingTrader.ScanIntervalMinutes // 保持原值
}
// 更新交易员配置 // 更新交易员配置
trader := &config.TraderRecord{ trader := &config.TraderRecord{
ID: traderID, ID: traderID,
@@ -513,14 +527,14 @@ func (s *Server) handleUpdateTrader(c *gin.Context) {
AIModelID: req.AIModelID, AIModelID: req.AIModelID,
ExchangeID: req.ExchangeID, ExchangeID: req.ExchangeID,
InitialBalance: req.InitialBalance, InitialBalance: req.InitialBalance,
BTCETHLeverage: btcEthLeverage,
AltcoinLeverage: altcoinLeverage, AltcoinLeverage: altcoinLeverage,
TradingSymbols: req.TradingSymbols, TradingSymbols: req.TradingSymbols,
CustomPrompt: req.CustomPrompt, CustomPrompt: req.CustomPrompt,
OverrideBasePrompt: req.OverrideBasePrompt, OverrideBasePrompt: req.OverrideBasePrompt,
IsCrossMargin: isCrossMargin, SystemPromptTemplate: existingTrader.SystemPromptTemplate, // 保持原值
ScanIntervalMinutes: existingTrader.ScanIntervalMinutes, // 保持原值 IsCrossMargin: isCrossMargin,
IsRunning: existingTrader.IsRunning, // 保持原值 ScanIntervalMinutes: scanIntervalMinutes,
IsRunning: existingTrader.IsRunning, // 保持原值
} }
// 更新数据库 // 更新数据库
@@ -868,12 +882,12 @@ func (s *Server) handleGetTraderConfig(c *gin.Context) {
"ai_model": traderConfig.AIModelID, // 使用完整 ID "ai_model": traderConfig.AIModelID, // 使用完整 ID
"exchange_id": traderConfig.ExchangeID, "exchange_id": traderConfig.ExchangeID,
"initial_balance": traderConfig.InitialBalance, "initial_balance": traderConfig.InitialBalance,
"scan_interval_minutes": traderConfig.ScanIntervalMinutes,
"btc_eth_leverage": traderConfig.BTCETHLeverage, "btc_eth_leverage": traderConfig.BTCETHLeverage,
"altcoin_leverage": traderConfig.AltcoinLeverage, "altcoin_leverage": traderConfig.AltcoinLeverage,
"trading_symbols": traderConfig.TradingSymbols, "trading_symbols": traderConfig.TradingSymbols,
"custom_prompt": traderConfig.CustomPrompt, "custom_prompt": traderConfig.CustomPrompt,
"override_base_prompt": traderConfig.OverrideBasePrompt, "override_base_prompt": traderConfig.OverrideBasePrompt,
"system_prompt_template": traderConfig.SystemPromptTemplate, // 添加此字段
"is_cross_margin": traderConfig.IsCrossMargin, "is_cross_margin": traderConfig.IsCrossMargin,
"use_coin_pool": traderConfig.UseCoinPool, "use_coin_pool": traderConfig.UseCoinPool,
"use_oi_top": traderConfig.UseOITop, "use_oi_top": traderConfig.UseOITop,

View File

@@ -207,6 +207,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
ai_model_id: ai_model_id, // 使用修正后的值 ai_model_id: ai_model_id, // 使用修正后的值
exchange_id: exchange_id, // 使用修正后的值 exchange_id: exchange_id, // 使用修正后的值
initial_balance: data.initial_balance, initial_balance: data.initial_balance,
scan_interval_minutes: data.scan_interval_minutes,
btc_eth_leverage: data.btc_eth_leverage, btc_eth_leverage: data.btc_eth_leverage,
altcoin_leverage: data.altcoin_leverage, altcoin_leverage: data.altcoin_leverage,
trading_symbols: data.trading_symbols, trading_symbols: data.trading_symbols,

View File

@@ -26,6 +26,7 @@ interface TraderConfigData {
use_coin_pool: boolean; use_coin_pool: boolean;
use_oi_top: boolean; use_oi_top: boolean;
initial_balance: number; initial_balance: number;
scan_interval_minutes: number;
} }
interface TraderConfigModalProps { interface TraderConfigModalProps {
@@ -61,6 +62,7 @@ export function TraderConfigModal({
use_coin_pool: false, use_coin_pool: false,
use_oi_top: false, use_oi_top: false,
initial_balance: 1000, initial_balance: 1000,
scan_interval_minutes: 3,
}); });
const [isSaving, setIsSaving] = useState(false); const [isSaving, setIsSaving] = useState(false);
const [availableCoins, setAvailableCoins] = useState<string[]>([]); const [availableCoins, setAvailableCoins] = useState<string[]>([]);
@@ -91,6 +93,7 @@ export function TraderConfigModal({
use_coin_pool: false, use_coin_pool: false,
use_oi_top: false, use_oi_top: false,
initial_balance: 1000, initial_balance: 1000,
scan_interval_minutes: 3,
}); });
} }
// 确保旧数据也有默认的 system_prompt_template // 确保旧数据也有默认的 system_prompt_template
@@ -185,6 +188,7 @@ export function TraderConfigModal({
use_coin_pool: formData.use_coin_pool, use_coin_pool: formData.use_coin_pool,
use_oi_top: formData.use_oi_top, use_oi_top: formData.use_oi_top,
initial_balance: formData.initial_balance, initial_balance: formData.initial_balance,
scan_interval_minutes: formData.scan_interval_minutes,
}; };
await onSave(saveData); await onSave(saveData);
onClose(); onClose();
@@ -323,7 +327,25 @@ export function TraderConfigModal({
</div> </div>
</div> </div>
{/* 第二行:杠杆设置 */} {/* 第二行:AI 扫描决策间隔 */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm text-[#EAECEF] block mb-2">AI ()</label>
<input
type="number"
value={formData.scan_interval_minutes}
onChange={(e) => handleInputChange('scan_interval_minutes', Number(e.target.value))}
className="w-full px-3 py-2 bg-[#0B0E11] border border-[#2B3139] rounded text-[#EAECEF] focus:border-[#F0B90B] focus:outline-none"
min="1"
max="60"
step="1"
/>
<p className="text-xs text-gray-500 mt-1">建议: 3-10</p>
</div>
<div></div>
</div>
{/* 第三行:杠杆设置 */}
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div> <div>
<label className="text-sm text-[#EAECEF] block mb-2">BTC/ETH </label> <label className="text-sm text-[#EAECEF] block mb-2">BTC/ETH </label>

View File

@@ -2,12 +2,12 @@ import { motion } from 'framer-motion'
import AnimatedSection from './AnimatedSection' import AnimatedSection from './AnimatedSection'
interface CardProps { interface CardProps {
quote: string quote: string;
authorName: string authorName: string;
handle: string handle: string;
avatarUrl: string avatarUrl: string;
tweetUrl: string tweetUrl: string;
delay: number delay: number;
} }
function TestimonialCard({ quote, authorName, delay }: CardProps) { function TestimonialCard({ quote, authorName, delay }: CardProps) {

View File

@@ -125,6 +125,7 @@ export interface CreateTraderRequest {
ai_model_id: string; ai_model_id: string;
exchange_id: string; exchange_id: string;
initial_balance: number; initial_balance: number;
scan_interval_minutes?: number;
btc_eth_leverage?: number; btc_eth_leverage?: number;
altcoin_leverage?: number; altcoin_leverage?: number;
trading_symbols?: string; trading_symbols?: string;
@@ -199,5 +200,6 @@ export interface TraderConfigData {
use_coin_pool: boolean; use_coin_pool: boolean;
use_oi_top: boolean; use_oi_top: boolean;
initial_balance: number; initial_balance: number;
scan_interval_minutes: number;
is_running: boolean; is_running: boolean;
} }