feat: add OI ranking data support and fix trader config update issues

- Add OI ranking data fetching and formatting for AI prompts
- Fix trader update not saving strategy_id, ai_model_id, initial_balance
- Fix AI API key not set for non-qwen/deepseek providers
- Add strategy_id to trader config API response
- Remove old trader from memory before reloading on update
- Clean up unused useTraderActions.ts
This commit is contained in:
tinkle-community
2025-12-12 16:08:21 +08:00
parent f5ae22d85c
commit cc390706d0
16 changed files with 476 additions and 716 deletions

View File

@@ -290,6 +290,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
}
const handleSaveEditTrader = async (data: CreateTraderRequest) => {
console.log('🔥🔥🔥 handleSaveEditTrader CALLED with data:', data)
if (!editingTrader) return
try {
@@ -310,19 +311,17 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
name: data.name,
ai_model_id: data.ai_model_id,
exchange_id: data.exchange_id,
strategy_id: data.strategy_id,
initial_balance: data.initial_balance,
scan_interval_minutes: data.scan_interval_minutes,
btc_eth_leverage: data.btc_eth_leverage,
altcoin_leverage: data.altcoin_leverage,
trading_symbols: data.trading_symbols,
custom_prompt: data.custom_prompt,
override_base_prompt: data.override_base_prompt,
system_prompt_template: data.system_prompt_template,
is_cross_margin: data.is_cross_margin,
use_coin_pool: data.use_coin_pool,
use_oi_top: data.use_oi_top,
show_in_competition: data.show_in_competition,
}
console.log('🔥 handleSaveEditTrader - data:', data)
console.log('🔥 handleSaveEditTrader - data.strategy_id:', data.strategy_id)
console.log('🔥 handleSaveEditTrader - request:', request)
await toast.promise(api.updateTrader(editingTrader.trader_id, request), {
loading: '正在保存…',
success: '保存成功',

View File

@@ -163,7 +163,7 @@ export function TraderConfigModal({
name: formData.trader_name,
ai_model_id: formData.ai_model,
exchange_id: formData.exchange_id,
strategy_id: formData.strategy_id || undefined,
strategy_id: formData.strategy_id,
is_cross_margin: formData.is_cross_margin,
show_in_competition: formData.show_in_competition,
scan_interval_minutes: formData.scan_interval_minutes,

View File

@@ -1,8 +1,10 @@
import { Clock, Activity, Database, TrendingUp, BarChart2, Info, Lock } from 'lucide-react'
import { Clock, Activity, Database, TrendingUp, BarChart2, Info, Lock, LineChart } from 'lucide-react'
import type { IndicatorConfig } from '../../types'
// Default API URL for quant data (must contain {symbol} placeholder)
const DEFAULT_QUANT_DATA_API_URL = 'http://nofxaios.com:30006/api/coin/{symbol}?include=netflow,oi,price&auth=cm_568c67eae410d912c54c'
// Default API base URL for OI ranking data
const DEFAULT_OI_RANKING_API_URL = 'http://nofxaios.com:30006'
interface IndicatorEditorProps {
config: IndicatorConfig
@@ -82,6 +84,13 @@ export function IndicatorEditor({
fillDefault: { zh: '填入默认', en: 'Fill Default' },
symbolPlaceholder: { zh: '{symbol} 会被替换为币种', en: '{symbol} will be replaced with coin' },
// OI Ranking
oiRanking: { zh: 'OI 排行数据', en: 'OI Ranking Data' },
oiRankingDesc: { zh: '市场持仓量增减排行,反映资金流向', en: 'Market-wide OI changes, reflects capital flow' },
oiRankingDuration: { zh: '时间周期', en: 'Duration' },
oiRankingLimit: { zh: '排行数量', en: 'Top N' },
oiRankingNote: { zh: '显示持仓量增加/减少的币种排行,帮助发现资金流向', en: 'Shows coins with OI increase/decrease, helps identify capital flow' },
// Tips
aiCanCalculate: { zh: '💡 提示AI 可自行计算这些指标,开启可减少 AI 计算量', en: '💡 Tip: AI can calculate these, enabling reduces AI workload' },
}
@@ -456,6 +465,82 @@ export function IndicatorEditor({
)}
</div>
</div>
{/* Section 5: OI Ranking Data (Market-wide) */}
<div className="rounded-lg overflow-hidden" style={{ background: '#0B0E11', border: '1px solid #2B3139' }}>
<div className="px-3 py-2 flex items-center gap-2" style={{ background: '#1E2329', borderBottom: '1px solid #2B3139' }}>
<LineChart className="w-4 h-4" style={{ color: '#22c55e' }} />
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{t('oiRanking')}</span>
<span className="text-xs" style={{ color: '#848E9C' }}>- {t('oiRankingDesc')}</span>
</div>
<div className="p-3 space-y-3">
{/* Enable Toggle */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full" style={{ background: '#22c55e' }} />
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{t('oiRanking')}</span>
</div>
<input
type="checkbox"
checked={config.enable_oi_ranking || false}
onChange={(e) => !disabled && onChange({
...config,
enable_oi_ranking: e.target.checked,
// Set defaults when enabling
...(e.target.checked && !config.oi_ranking_api_url ? { oi_ranking_api_url: DEFAULT_OI_RANKING_API_URL } : {}),
...(e.target.checked && !config.oi_ranking_duration ? { oi_ranking_duration: '1h' } : {}),
...(e.target.checked && !config.oi_ranking_limit ? { oi_ranking_limit: 10 } : {}),
})}
disabled={disabled}
className="w-4 h-4 rounded accent-green-500"
/>
</div>
{/* Settings */}
{config.enable_oi_ranking && (
<div className="space-y-3">
<div className="flex gap-3">
{/* Duration */}
<div className="flex-1">
<label className="text-[10px] mb-1 block" style={{ color: '#848E9C' }}>
{t('oiRankingDuration')}
</label>
<select
value={config.oi_ranking_duration || '1h'}
onChange={(e) => !disabled && onChange({ ...config, oi_ranking_duration: e.target.value })}
disabled={disabled}
className="w-full px-2 py-1.5 rounded text-xs"
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
>
<option value="1h">{language === 'zh' ? '1小时' : '1 Hour'}</option>
<option value="4h">{language === 'zh' ? '4小时' : '4 Hours'}</option>
<option value="24h">{language === 'zh' ? '24小时' : '24 Hours'}</option>
</select>
</div>
{/* Limit */}
<div className="flex-1">
<label className="text-[10px] mb-1 block" style={{ color: '#848E9C' }}>
{t('oiRankingLimit')}
</label>
<select
value={config.oi_ranking_limit || 10}
onChange={(e) => !disabled && onChange({ ...config, oi_ranking_limit: parseInt(e.target.value) })}
disabled={disabled}
className="w-full px-2 py-1.5 rounded text-xs"
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
>
{[5, 10, 15, 20].map(n => (
<option key={n} value={n}>{n}</option>
))}
</select>
</div>
</div>
<p className="text-[10px]" style={{ color: '#5E6673' }}>{t('oiRankingNote')}</p>
</div>
)}
</div>
</div>
</div>
)
}