mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-13 15:57:01 +08:00
fix: prevent DeepSeek token overflow with product-level limits (#1431)
* feat: enforce strategy limits to prevent token overflow * fix: tune token limits after real-world testing - Relax kline max 20→30, timeframes 3→4 (tested ~41K tokens, safe under 131K) - Restore ranking limits to original [5,10,15,20] options (only ~1.5K token impact) - Add static coins limit (max 3) with toast notification - Add timeframe limit toast when exceeding 4 - Log SSE token usage (prompt/completion/total) from API response - Fix nil logger crash in claw402 data client (engine.go) * feat: add token estimation functionality for strategy configurations * feat: add discard changes button in Strategy Studio for unsaved modifications * feat: retain selected strategy after saving in Strategy Studio * feat: enhance strategy display in Strategy Studio with improved layout and sorting of token limits * refactor: improve layout and styling of stats display in CompetitionPage * refactor: replace select elements with NofxSelect component for improved consistency in strategy configuration forms * style: update NofxSelect component to use smaller text size for improved readability * feat: implement token overflow handling in strategy updates and UI --------- Co-authored-by: Dean <afei.wuhao@gmail.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Clock, Activity, TrendingUp, BarChart2, Info, Lock, ExternalLink, Zap, Check, AlertCircle, Key } from 'lucide-react'
|
||||
import type { IndicatorConfig } from '../../types'
|
||||
import { indicator, ts } from '../../i18n/strategy-translations'
|
||||
import { NofxSelect } from '../ui/select'
|
||||
|
||||
// Default NofxOS API Key
|
||||
const DEFAULT_NOFXOS_API_KEY = 'cm_568c67eae410d912c54c'
|
||||
@@ -60,6 +61,16 @@ export function IndicatorEditor({
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (current.length >= 4) {
|
||||
// Show toast notification
|
||||
const toast = document.createElement('div')
|
||||
toast.textContent = language === 'zh' ? '最多选择 4 个时间维度' : 'Maximum 4 timeframes allowed'
|
||||
toast.className = 'fixed top-4 left-1/2 -translate-x-1/2 px-4 py-2 rounded-lg text-sm z-50 shadow-lg'
|
||||
toast.style.cssText = 'background:#F6465D;color:#fff;'
|
||||
document.body.appendChild(toast)
|
||||
setTimeout(() => toast.remove(), 2000)
|
||||
return
|
||||
}
|
||||
current.push(tf)
|
||||
onChange({
|
||||
...config,
|
||||
@@ -299,26 +310,22 @@ export function IndicatorEditor({
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{ts(indicator.oiRankingDesc, language)}</p>
|
||||
{config.enable_oi_ranking && (
|
||||
<div className="flex gap-2 mt-2" onClick={(e) => e.stopPropagation()}>
|
||||
<select
|
||||
<NofxSelect
|
||||
value={config.oi_ranking_duration || '1h'}
|
||||
onChange={(e) => !disabled && onChange({ ...config, oi_ranking_duration: e.target.value })}
|
||||
onChange={(val) => !disabled && onChange({ ...config, oi_ranking_duration: val })}
|
||||
disabled={disabled}
|
||||
className="flex-1 px-2 py-1 rounded text-[10px]"
|
||||
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
|
||||
>
|
||||
<option value="1h">1h</option>
|
||||
<option value="4h">4h</option>
|
||||
<option value="24h">24h</option>
|
||||
</select>
|
||||
<select
|
||||
options={[{ value: '1h', label: '1h' }, { value: '4h', label: '4h' }, { value: '24h', label: '24h' }]}
|
||||
/>
|
||||
<NofxSelect
|
||||
value={config.oi_ranking_limit || 10}
|
||||
onChange={(e) => !disabled && onChange({ ...config, oi_ranking_limit: parseInt(e.target.value) })}
|
||||
onChange={(val) => !disabled && onChange({ ...config, oi_ranking_limit: parseInt(val) })}
|
||||
disabled={disabled}
|
||||
className="w-14 px-2 py-1 rounded text-[10px]"
|
||||
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
|
||||
>
|
||||
{[5, 10, 15, 20].map(n => <option key={n} value={n}>{n}</option>)}
|
||||
</select>
|
||||
options={[5, 10, 15, 20].map(n => ({ value: n, label: String(n) }))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -359,26 +366,22 @@ export function IndicatorEditor({
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{ts(indicator.netflowRankingDesc, language)}</p>
|
||||
{config.enable_netflow_ranking && (
|
||||
<div className="flex gap-2 mt-2" onClick={(e) => e.stopPropagation()}>
|
||||
<select
|
||||
<NofxSelect
|
||||
value={config.netflow_ranking_duration || '1h'}
|
||||
onChange={(e) => !disabled && onChange({ ...config, netflow_ranking_duration: e.target.value })}
|
||||
onChange={(val) => !disabled && onChange({ ...config, netflow_ranking_duration: val })}
|
||||
disabled={disabled}
|
||||
className="flex-1 px-2 py-1 rounded text-[10px]"
|
||||
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
|
||||
>
|
||||
<option value="1h">1h</option>
|
||||
<option value="4h">4h</option>
|
||||
<option value="24h">24h</option>
|
||||
</select>
|
||||
<select
|
||||
options={[{ value: '1h', label: '1h' }, { value: '4h', label: '4h' }, { value: '24h', label: '24h' }]}
|
||||
/>
|
||||
<NofxSelect
|
||||
value={config.netflow_ranking_limit || 10}
|
||||
onChange={(e) => !disabled && onChange({ ...config, netflow_ranking_limit: parseInt(e.target.value) })}
|
||||
onChange={(val) => !disabled && onChange({ ...config, netflow_ranking_limit: parseInt(val) })}
|
||||
disabled={disabled}
|
||||
className="w-14 px-2 py-1 rounded text-[10px]"
|
||||
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
|
||||
>
|
||||
{[5, 10, 15, 20].map(n => <option key={n} value={n}>{n}</option>)}
|
||||
</select>
|
||||
options={[5, 10, 15, 20].map(n => ({ value: n, label: String(n) }))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -419,27 +422,27 @@ export function IndicatorEditor({
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{ts(indicator.priceRankingDesc, language)}</p>
|
||||
{config.enable_price_ranking && (
|
||||
<div className="flex gap-2 mt-2" onClick={(e) => e.stopPropagation()}>
|
||||
<select
|
||||
<NofxSelect
|
||||
value={config.price_ranking_duration || '1h,4h,24h'}
|
||||
onChange={(e) => !disabled && onChange({ ...config, price_ranking_duration: e.target.value })}
|
||||
onChange={(val) => !disabled && onChange({ ...config, price_ranking_duration: val })}
|
||||
disabled={disabled}
|
||||
className="flex-1 px-2 py-1 rounded text-[10px]"
|
||||
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
|
||||
>
|
||||
<option value="1h">1h</option>
|
||||
<option value="4h">4h</option>
|
||||
<option value="24h">24h</option>
|
||||
<option value="1h,4h,24h">{ts(indicator.priceRankingMulti, language)}</option>
|
||||
</select>
|
||||
<select
|
||||
options={[
|
||||
{ value: '1h', label: '1h' },
|
||||
{ value: '4h', label: '4h' },
|
||||
{ value: '24h', label: '24h' },
|
||||
{ value: '1h,4h,24h', label: ts(indicator.priceRankingMulti, language) },
|
||||
]}
|
||||
/>
|
||||
<NofxSelect
|
||||
value={config.price_ranking_limit || 10}
|
||||
onChange={(e) => !disabled && onChange({ ...config, price_ranking_limit: parseInt(e.target.value) })}
|
||||
onChange={(val) => !disabled && onChange({ ...config, price_ranking_limit: parseInt(val) })}
|
||||
disabled={disabled}
|
||||
className="w-14 px-2 py-1 rounded text-[10px]"
|
||||
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
|
||||
>
|
||||
{[5, 10, 15, 20].map(n => <option key={n} value={n}>{n}</option>)}
|
||||
</select>
|
||||
options={[5, 10, 15, 20].map(n => ({ value: n, label: String(n) }))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -515,7 +518,7 @@ export function IndicatorEditor({
|
||||
}
|
||||
disabled={disabled}
|
||||
min={10}
|
||||
max={200}
|
||||
max={30}
|
||||
className="w-16 px-2 py-1 rounded text-xs text-center"
|
||||
style={{ background: '#1E2329', border: '1px solid #2B3139', color: '#EAECEF' }}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user