mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-10 06:20:58 +08:00
refactor: optimize codebase encoding
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { Plus, X, Database, TrendingUp, TrendingDown, List, Ban, Zap, Shuffle } from 'lucide-react'
|
||||
import type { CoinSourceConfig } from '../../types'
|
||||
import { coinSource, ts } from '../../i18n/strategy-translations'
|
||||
|
||||
interface CoinSourceEditorProps {
|
||||
config: CoinSourceConfig
|
||||
@@ -18,52 +19,6 @@ export function CoinSourceEditor({
|
||||
const [newCoin, setNewCoin] = useState('')
|
||||
const [newExcludedCoin, setNewExcludedCoin] = useState('')
|
||||
|
||||
const t = (key: string) => {
|
||||
const translations: Record<string, Record<string, string>> = {
|
||||
sourceType: { zh: '数据来源类型', en: 'Source Type' },
|
||||
static: { zh: '静态列表', en: 'Static List' },
|
||||
ai500: { zh: 'AI500 数据源', en: 'AI500 Data Provider' },
|
||||
oi_top: { zh: 'OI 持仓增加', en: 'OI Increase' },
|
||||
oi_low: { zh: 'OI 持仓减少', en: 'OI Decrease' },
|
||||
mixed: { zh: '混合模式', en: 'Mixed Mode' },
|
||||
staticCoins: { zh: '自定义币种', en: 'Custom Coins' },
|
||||
addCoin: { zh: '添加币种', en: 'Add Coin' },
|
||||
useAI500: { zh: '启用 AI500 数据源', en: 'Enable AI500 Data Provider' },
|
||||
ai500Limit: { zh: '数量上限', en: 'Limit' },
|
||||
useOITop: { zh: '启用 OI 持仓增加榜', en: 'Enable OI Increase' },
|
||||
oiTopLimit: { zh: '数量上限', en: 'Limit' },
|
||||
useOILow: { zh: '启用 OI 持仓减少榜', en: 'Enable OI Decrease' },
|
||||
oiLowLimit: { zh: '数量上限', en: 'Limit' },
|
||||
staticDesc: { zh: '手动指定交易币种列表', en: 'Manually specify trading coins' },
|
||||
ai500Desc: {
|
||||
zh: '使用 AI500 智能筛选的热门币种',
|
||||
en: 'Use AI500 smart-filtered popular coins',
|
||||
},
|
||||
oiTopDesc: {
|
||||
zh: '持仓增加榜,适合做多',
|
||||
en: 'OI increase ranking, for long',
|
||||
},
|
||||
oi_lowDesc: {
|
||||
zh: '持仓减少榜,适合做空',
|
||||
en: 'OI decrease ranking, for short',
|
||||
},
|
||||
mixedDesc: {
|
||||
zh: '组合多种数据源',
|
||||
en: 'Combine multiple sources',
|
||||
},
|
||||
mixedConfig: { zh: '组合数据源配置', en: 'Combined Sources Configuration' },
|
||||
mixedSummary: { zh: '已选组合', en: 'Selected Sources' },
|
||||
maxCoins: { zh: '最多', en: 'Up to' },
|
||||
coins: { zh: '个币种', en: 'coins' },
|
||||
dataSourceConfig: { zh: '数据源配置', en: 'Data Source Configuration' },
|
||||
excludedCoins: { zh: '排除币种', en: 'Excluded Coins' },
|
||||
excludedCoinsDesc: { zh: '这些币种将从所有数据源中排除,不会被交易', en: 'These coins will be excluded from all sources and will not be traded' },
|
||||
addExcludedCoin: { zh: '添加排除', en: 'Add Excluded' },
|
||||
nofxosNote: { zh: '使用 NofxOS API Key(在指标配置中设置)', en: 'Uses NofxOS API Key (set in Indicators config)' },
|
||||
}
|
||||
return translations[key]?.[language] || key
|
||||
}
|
||||
|
||||
const sourceTypes = [
|
||||
{ value: 'static', icon: List, color: '#848E9C' },
|
||||
{ value: 'ai500', icon: Database, color: '#F0B90B' },
|
||||
@@ -82,15 +37,15 @@ export function CoinSourceEditor({
|
||||
totalLimit += config.ai500_limit || 10
|
||||
}
|
||||
if (config.use_oi_top) {
|
||||
sources.push(`${language === 'zh' ? 'OI增' : 'OI↑'}(${config.oi_top_limit || 10})`)
|
||||
sources.push(`${ts(coinSource.oiIncreaseShort, language)}(${config.oi_top_limit || 10})`)
|
||||
totalLimit += config.oi_top_limit || 10
|
||||
}
|
||||
if (config.use_oi_low) {
|
||||
sources.push(`${language === 'zh' ? 'OI减' : 'OI↓'}(${config.oi_low_limit || 10})`)
|
||||
sources.push(`${ts(coinSource.oiDecreaseShort, language)}(${config.oi_low_limit || 10})`)
|
||||
totalLimit += config.oi_low_limit || 10
|
||||
}
|
||||
if ((config.static_coins || []).length > 0) {
|
||||
sources.push(`${language === 'zh' ? '自定义' : 'Custom'}(${config.static_coins?.length || 0})`)
|
||||
sources.push(`${ts(coinSource.custom, language)}(${config.static_coins?.length || 0})`)
|
||||
totalLimit += config.static_coins?.length || 0
|
||||
}
|
||||
|
||||
@@ -191,7 +146,7 @@ export function CoinSourceEditor({
|
||||
{/* Source Type Selector */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-3 text-nofx-text">
|
||||
{t('sourceType')}
|
||||
{ts(coinSource.sourceType, language)}
|
||||
</label>
|
||||
<div className="grid grid-cols-5 gap-2">
|
||||
{sourceTypes.map(({ value, icon: Icon, color }) => (
|
||||
@@ -209,10 +164,10 @@ export function CoinSourceEditor({
|
||||
>
|
||||
<Icon className="w-6 h-6 mx-auto mb-2" style={{ color }} />
|
||||
<div className="text-sm font-medium text-nofx-text">
|
||||
{t(value)}
|
||||
{ts(coinSource[value as keyof typeof coinSource], language)}
|
||||
</div>
|
||||
<div className="text-xs mt-1 text-nofx-text-muted">
|
||||
{t(`${value}Desc`)}
|
||||
{ts(coinSource[`${value}Desc` as keyof typeof coinSource], language)}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
@@ -223,7 +178,7 @@ export function CoinSourceEditor({
|
||||
{config.source_type === 'static' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-3 text-nofx-text">
|
||||
{t('staticCoins')}
|
||||
{ts(coinSource.staticCoins, language)}
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-2 mb-3">
|
||||
{(config.static_coins || []).map((coin) => (
|
||||
@@ -258,7 +213,7 @@ export function CoinSourceEditor({
|
||||
className="px-4 py-2 rounded-lg flex items-center gap-2 transition-colors bg-nofx-gold text-black hover:bg-yellow-500"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
{t('addCoin')}
|
||||
{ts(coinSource.addCoin, language)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -270,11 +225,11 @@ export function CoinSourceEditor({
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Ban className="w-4 h-4 text-nofx-danger" />
|
||||
<label className="text-sm font-medium text-nofx-text">
|
||||
{t('excludedCoins')}
|
||||
{ts(coinSource.excludedCoins, language)}
|
||||
</label>
|
||||
</div>
|
||||
<p className="text-xs mb-3 text-nofx-text-muted">
|
||||
{t('excludedCoinsDesc')}
|
||||
{ts(coinSource.excludedCoinsDesc, language)}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2 mb-3">
|
||||
{(config.excluded_coins || []).map((coin) => (
|
||||
@@ -295,7 +250,7 @@ export function CoinSourceEditor({
|
||||
))}
|
||||
{(config.excluded_coins || []).length === 0 && (
|
||||
<span className="text-xs italic text-nofx-text-muted">
|
||||
{language === 'zh' ? '无' : 'None'}
|
||||
{ts(coinSource.excludedNone, language)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -314,7 +269,7 @@ export function CoinSourceEditor({
|
||||
className="px-4 py-2 rounded-lg flex items-center gap-2 transition-colors text-sm bg-nofx-danger text-white hover:bg-red-600"
|
||||
>
|
||||
<Ban className="w-4 h-4" />
|
||||
{t('addExcludedCoin')}
|
||||
{ts(coinSource.addExcludedCoin, language)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -329,7 +284,7 @@ export function CoinSourceEditor({
|
||||
<div className="flex items-center gap-2">
|
||||
<Zap className="w-4 h-4 text-nofx-gold" />
|
||||
<span className="text-sm font-medium text-nofx-text">
|
||||
AI500 {t('dataSourceConfig')}
|
||||
AI500 {ts(coinSource.dataSourceConfig, language)}
|
||||
</span>
|
||||
<NofxOSBadge />
|
||||
</div>
|
||||
@@ -346,13 +301,13 @@ export function CoinSourceEditor({
|
||||
disabled={disabled}
|
||||
className="w-5 h-5 rounded accent-nofx-gold"
|
||||
/>
|
||||
<span className="text-nofx-text">{t('useAI500')}</span>
|
||||
<span className="text-nofx-text">{ts(coinSource.useAI500, language)}</span>
|
||||
</label>
|
||||
|
||||
{config.use_ai500 && (
|
||||
<div className="flex items-center gap-3 pl-8">
|
||||
<span className="text-sm text-nofx-text-muted">
|
||||
{t('ai500Limit')}:
|
||||
{ts(coinSource.ai500Limit, language)}:
|
||||
</span>
|
||||
<select
|
||||
value={config.ai500_limit || 10}
|
||||
@@ -371,7 +326,7 @@ export function CoinSourceEditor({
|
||||
)}
|
||||
|
||||
<p className="text-xs pl-8 text-nofx-text-muted">
|
||||
{t('nofxosNote')}
|
||||
{ts(coinSource.nofxosNote, language)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -386,7 +341,7 @@ export function CoinSourceEditor({
|
||||
<div className="flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4 text-nofx-success" />
|
||||
<span className="text-sm font-medium text-nofx-text">
|
||||
OI {language === 'zh' ? '持仓增加榜' : 'Increase'} {t('dataSourceConfig')}
|
||||
{ts(coinSource.oiIncreaseTitle, language)} {ts(coinSource.dataSourceConfig, language)}
|
||||
</span>
|
||||
<NofxOSBadge />
|
||||
</div>
|
||||
@@ -403,13 +358,13 @@ export function CoinSourceEditor({
|
||||
disabled={disabled}
|
||||
className="w-5 h-5 rounded accent-nofx-success"
|
||||
/>
|
||||
<span className="text-nofx-text">{t('useOITop')}</span>
|
||||
<span className="text-nofx-text">{ts(coinSource.useOITop, language)}</span>
|
||||
</label>
|
||||
|
||||
{config.use_oi_top && (
|
||||
<div className="flex items-center gap-3 pl-8">
|
||||
<span className="text-sm text-nofx-text-muted">
|
||||
{t('oiTopLimit')}:
|
||||
{ts(coinSource.oiTopLimit, language)}:
|
||||
</span>
|
||||
<select
|
||||
value={config.oi_top_limit || 10}
|
||||
@@ -428,7 +383,7 @@ export function CoinSourceEditor({
|
||||
)}
|
||||
|
||||
<p className="text-xs pl-8 text-nofx-text-muted">
|
||||
{t('nofxosNote')}
|
||||
{ts(coinSource.nofxosNote, language)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -443,7 +398,7 @@ export function CoinSourceEditor({
|
||||
<div className="flex items-center gap-2">
|
||||
<TrendingDown className="w-4 h-4 text-nofx-danger" />
|
||||
<span className="text-sm font-medium text-nofx-text">
|
||||
OI {language === 'zh' ? '持仓减少榜' : 'Decrease'} {t('dataSourceConfig')}
|
||||
{ts(coinSource.oiDecreaseTitle, language)} {ts(coinSource.dataSourceConfig, language)}
|
||||
</span>
|
||||
<NofxOSBadge />
|
||||
</div>
|
||||
@@ -460,13 +415,13 @@ export function CoinSourceEditor({
|
||||
disabled={disabled}
|
||||
className="w-5 h-5 rounded accent-red-500"
|
||||
/>
|
||||
<span className="text-nofx-text">{t('useOILow')}</span>
|
||||
<span className="text-nofx-text">{ts(coinSource.useOILow, language)}</span>
|
||||
</label>
|
||||
|
||||
{config.use_oi_low && (
|
||||
<div className="flex items-center gap-3 pl-8">
|
||||
<span className="text-sm text-nofx-text-muted">
|
||||
{t('oiLowLimit')}:
|
||||
{ts(coinSource.oiLowLimit, language)}:
|
||||
</span>
|
||||
<select
|
||||
value={config.oi_low_limit || 10}
|
||||
@@ -485,7 +440,7 @@ export function CoinSourceEditor({
|
||||
)}
|
||||
|
||||
<p className="text-xs pl-8 text-nofx-text-muted">
|
||||
{t('nofxosNote')}
|
||||
{ts(coinSource.nofxosNote, language)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -497,7 +452,7 @@ export function CoinSourceEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Shuffle className="w-4 h-4 text-blue-400" />
|
||||
<span className="text-sm font-medium text-nofx-text">
|
||||
{t('mixedConfig')}
|
||||
{ts(coinSource.mixedConfig, language)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -566,11 +521,11 @@ export function CoinSourceEditor({
|
||||
/>
|
||||
<TrendingUp className="w-4 h-4 text-nofx-success" />
|
||||
<span className="text-sm font-medium text-nofx-text">
|
||||
{language === 'zh' ? 'OI 增加' : 'OI Increase'}
|
||||
{ts(coinSource.oiIncreaseLabel, language)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-nofx-text-muted pl-6 mb-1">
|
||||
{language === 'zh' ? '适合做多' : 'For long'}
|
||||
{ts(coinSource.forLong, language)}
|
||||
</p>
|
||||
{config.use_oi_top && (
|
||||
<div className="flex items-center gap-2 mt-2 pl-6">
|
||||
@@ -613,11 +568,11 @@ export function CoinSourceEditor({
|
||||
/>
|
||||
<TrendingDown className="w-4 h-4 text-nofx-danger" />
|
||||
<span className="text-sm font-medium text-nofx-text">
|
||||
{language === 'zh' ? 'OI 减少' : 'OI Decrease'}
|
||||
{ts(coinSource.oiDecreaseLabel, language)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-nofx-text-muted pl-6 mb-1">
|
||||
{language === 'zh' ? '适合做空' : 'For short'}
|
||||
{ts(coinSource.forShort, language)}
|
||||
</p>
|
||||
{config.use_oi_low && (
|
||||
<div className="flex items-center gap-2 mt-2 pl-6">
|
||||
@@ -651,7 +606,7 @@ export function CoinSourceEditor({
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<List className="w-4 h-4 text-gray-400" />
|
||||
<span className="text-sm font-medium text-nofx-text">
|
||||
{language === 'zh' ? '自定义' : 'Custom'}
|
||||
{ts(coinSource.custom, language)}
|
||||
</span>
|
||||
{(config.static_coins || []).length > 0 && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-gray-500/20 text-gray-400">
|
||||
@@ -720,13 +675,13 @@ export function CoinSourceEditor({
|
||||
return (
|
||||
<div className="p-2 rounded bg-nofx-bg border border-nofx-border">
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="text-nofx-text-muted">{t('mixedSummary')}:</span>
|
||||
<span className="text-nofx-text-muted">{ts(coinSource.mixedSummary, language)}:</span>
|
||||
<span className="text-nofx-text font-medium">
|
||||
{sources.join(' + ')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-nofx-text-muted mt-1">
|
||||
{t('maxCoins')} {totalLimit} {t('coins')}
|
||||
{ts(coinSource.maxCoins, language)} {totalLimit} {ts(coinSource.coins, language)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Grid, DollarSign, TrendingUp, Shield, Compass } from 'lucide-react'
|
||||
import type { GridStrategyConfig } from '../../types'
|
||||
import { gridConfig, ts } from '../../i18n/strategy-translations'
|
||||
|
||||
interface GridConfigEditorProps {
|
||||
config: GridStrategyConfig
|
||||
@@ -8,7 +9,7 @@ interface GridConfigEditorProps {
|
||||
language: string
|
||||
}
|
||||
|
||||
// Default grid config
|
||||
// Default grid configuration
|
||||
export const defaultGridConfig: GridStrategyConfig = {
|
||||
symbol: 'BTCUSDT',
|
||||
grid_count: 10,
|
||||
@@ -33,71 +34,6 @@ export function GridConfigEditor({
|
||||
disabled,
|
||||
language,
|
||||
}: GridConfigEditorProps) {
|
||||
const t = (key: string) => {
|
||||
const translations: Record<string, Record<string, string>> = {
|
||||
// Section titles
|
||||
tradingPair: { zh: '交易设置', en: 'Trading Setup' },
|
||||
gridParameters: { zh: '网格参数', en: 'Grid Parameters' },
|
||||
priceBounds: { zh: '价格边界', en: 'Price Bounds' },
|
||||
riskControl: { zh: '风险控制', en: 'Risk Control' },
|
||||
|
||||
// Trading pair
|
||||
symbol: { zh: '交易对', en: 'Trading Pair' },
|
||||
symbolDesc: { zh: '选择要进行网格交易的交易对', en: 'Select trading pair for grid trading' },
|
||||
|
||||
// Investment
|
||||
totalInvestment: { zh: '投资金额 (USDT)', en: 'Investment (USDT)' },
|
||||
totalInvestmentDesc: { zh: '网格策略的总投资金额', en: 'Total investment for grid strategy' },
|
||||
leverage: { zh: '杠杆倍数', en: 'Leverage' },
|
||||
leverageDesc: { zh: '交易使用的杠杆倍数 (1-5)', en: 'Leverage for trading (1-5)' },
|
||||
|
||||
// Grid parameters
|
||||
gridCount: { zh: '网格数量', en: 'Grid Count' },
|
||||
gridCountDesc: { zh: '网格层级数量 (5-50)', en: 'Number of grid levels (5-50)' },
|
||||
distribution: { zh: '资金分配方式', en: 'Distribution' },
|
||||
distributionDesc: { zh: '网格层级的资金分配方式', en: 'Fund allocation across grid levels' },
|
||||
uniform: { zh: '均匀分配', en: 'Uniform' },
|
||||
gaussian: { zh: '高斯分配 (推荐)', en: 'Gaussian (Recommended)' },
|
||||
pyramid: { zh: '金字塔分配', en: 'Pyramid' },
|
||||
|
||||
// Price bounds
|
||||
useAtrBounds: { zh: '自动计算边界 (ATR)', en: 'Auto-calculate Bounds (ATR)' },
|
||||
useAtrBoundsDesc: { zh: '基于 ATR 自动计算网格上下边界', en: 'Auto-calculate bounds based on ATR' },
|
||||
atrMultiplier: { zh: 'ATR 倍数', en: 'ATR Multiplier' },
|
||||
atrMultiplierDesc: { zh: '边界距离当前价格的 ATR 倍数', en: 'ATR multiplier for bounds distance' },
|
||||
upperPrice: { zh: '上边界价格', en: 'Upper Price' },
|
||||
upperPriceDesc: { zh: '网格上边界价格 (0=自动计算)', en: 'Grid upper bound (0=auto)' },
|
||||
lowerPrice: { zh: '下边界价格', en: 'Lower Price' },
|
||||
lowerPriceDesc: { zh: '网格下边界价格 (0=自动计算)', en: 'Grid lower bound (0=auto)' },
|
||||
|
||||
// Risk control
|
||||
maxDrawdown: { zh: '最大回撤 (%)', en: 'Max Drawdown (%)' },
|
||||
maxDrawdownDesc: { zh: '触发紧急退出的最大回撤百分比', en: 'Max drawdown before emergency exit' },
|
||||
stopLoss: { zh: '止损 (%)', en: 'Stop Loss (%)' },
|
||||
stopLossDesc: { zh: '单仓位止损百分比', en: 'Stop loss per position' },
|
||||
dailyLossLimit: { zh: '日损失限制 (%)', en: 'Daily Loss Limit (%)' },
|
||||
dailyLossLimitDesc: { zh: '每日最大亏损百分比', en: 'Maximum daily loss percentage' },
|
||||
useMakerOnly: { zh: '仅使用 Maker 订单', en: 'Maker Only Orders' },
|
||||
useMakerOnlyDesc: { zh: '使用限价单以降低手续费', en: 'Use limit orders for lower fees' },
|
||||
|
||||
// Direction adjustment
|
||||
directionAdjust: { zh: '方向自动调整', en: 'Direction Auto-Adjust' },
|
||||
enableDirectionAdjust: { zh: '启用方向调整', en: 'Enable Direction Adjust' },
|
||||
enableDirectionAdjustDesc: { zh: '根据箱体突破自动调整网格方向', en: 'Auto-adjust grid direction based on box breakouts' },
|
||||
directionBiasRatio: { zh: '偏向强度', en: 'Bias Strength' },
|
||||
directionBiasRatioDesc: { zh: '偏多/偏空模式的强度', en: 'Strength for long_bias/short_bias modes' },
|
||||
directionBiasExplain: { zh: '偏多模式:X%买 + (100-X)%卖 | 偏空模式:(100-X)%买 + X%卖', en: 'Long bias: X% buy + (100-X)% sell | Short bias: (100-X)% buy + X% sell' },
|
||||
directionExplain: { zh: '短期箱体突破 → 偏向,中期箱体突破 → 全仓,价格回归 → 逐步恢复中性', en: 'Short box breakout → bias, Mid box breakout → full, Price return → gradually recover to neutral' },
|
||||
directionModes: { zh: '方向模式说明', en: 'Direction Modes' },
|
||||
modeNeutral: { zh: '中性:50%买 + 50%卖(默认)', en: 'Neutral: 50% buy + 50% sell (default)' },
|
||||
modeLongBias: { zh: '偏多:X%买 + (100-X)%卖', en: 'Long Bias: X% buy + (100-X)% sell' },
|
||||
modeLong: { zh: '全多:100%买 + 0%卖', en: 'Long: 100% buy + 0% sell' },
|
||||
modeShortBias: { zh: '偏空:(100-X)%买 + X%卖', en: 'Short Bias: (100-X)% buy + X% sell' },
|
||||
modeShort: { zh: '全空:0%买 + 100%卖', en: 'Short: 0% buy + 100% sell' },
|
||||
}
|
||||
return translations[key]?.[language] || key
|
||||
}
|
||||
|
||||
const updateField = <K extends keyof GridStrategyConfig>(
|
||||
key: K,
|
||||
value: GridStrategyConfig[K]
|
||||
@@ -125,7 +61,7 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<DollarSign className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('tradingPair')}
|
||||
{ts(gridConfig.tradingPair, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -133,10 +69,10 @@ export function GridConfigEditor({
|
||||
{/* Symbol */}
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('symbol')}
|
||||
{ts(gridConfig.symbol, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('symbolDesc')}
|
||||
{ts(gridConfig.symbolDesc, language)}
|
||||
</p>
|
||||
<select
|
||||
value={config.symbol}
|
||||
@@ -157,10 +93,10 @@ export function GridConfigEditor({
|
||||
{/* Investment */}
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('totalInvestment')}
|
||||
{ts(gridConfig.totalInvestment, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('totalInvestmentDesc')}
|
||||
{ts(gridConfig.totalInvestmentDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -177,10 +113,10 @@ export function GridConfigEditor({
|
||||
{/* Leverage */}
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('leverage')}
|
||||
{ts(gridConfig.leverage, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('leverageDesc')}
|
||||
{ts(gridConfig.leverageDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -201,7 +137,7 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Grid className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('gridParameters')}
|
||||
{ts(gridConfig.gridParameters, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -209,10 +145,10 @@ export function GridConfigEditor({
|
||||
{/* Grid Count */}
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('gridCount')}
|
||||
{ts(gridConfig.gridCount, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('gridCountDesc')}
|
||||
{ts(gridConfig.gridCountDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -229,10 +165,10 @@ export function GridConfigEditor({
|
||||
{/* Distribution */}
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('distribution')}
|
||||
{ts(gridConfig.distribution, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('distributionDesc')}
|
||||
{ts(gridConfig.distributionDesc, language)}
|
||||
</p>
|
||||
<select
|
||||
value={config.distribution}
|
||||
@@ -241,9 +177,9 @@ export function GridConfigEditor({
|
||||
className="w-full px-3 py-2 rounded"
|
||||
style={inputStyle}
|
||||
>
|
||||
<option value="uniform">{t('uniform')}</option>
|
||||
<option value="gaussian">{t('gaussian')}</option>
|
||||
<option value="pyramid">{t('pyramid')}</option>
|
||||
<option value="uniform">{ts(gridConfig.uniform, language)}</option>
|
||||
<option value="gaussian">{ts(gridConfig.gaussian, language)}</option>
|
||||
<option value="pyramid">{ts(gridConfig.pyramid, language)}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -254,7 +190,7 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<TrendingUp className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('priceBounds')}
|
||||
{ts(gridConfig.priceBounds, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -263,10 +199,10 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="block text-sm" style={{ color: '#EAECEF' }}>
|
||||
{t('useAtrBounds')}
|
||||
{ts(gridConfig.useAtrBounds, language)}
|
||||
</label>
|
||||
<p className="text-xs" style={{ color: '#848E9C' }}>
|
||||
{t('useAtrBoundsDesc')}
|
||||
{ts(gridConfig.useAtrBoundsDesc, language)}
|
||||
</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
@@ -285,10 +221,10 @@ export function GridConfigEditor({
|
||||
{config.use_atr_bounds ? (
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('atrMultiplier')}
|
||||
{ts(gridConfig.atrMultiplier, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('atrMultiplierDesc')}
|
||||
{ts(gridConfig.atrMultiplierDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -306,10 +242,10 @@ export function GridConfigEditor({
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('upperPrice')}
|
||||
{ts(gridConfig.upperPrice, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('upperPriceDesc')}
|
||||
{ts(gridConfig.upperPriceDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -324,10 +260,10 @@ export function GridConfigEditor({
|
||||
</div>
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('lowerPrice')}
|
||||
{ts(gridConfig.lowerPrice, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('lowerPriceDesc')}
|
||||
{ts(gridConfig.lowerPriceDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -349,17 +285,17 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Shield className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('riskControl')}
|
||||
{ts(gridConfig.riskControl, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('maxDrawdown')}
|
||||
{ts(gridConfig.maxDrawdown, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('maxDrawdownDesc')}
|
||||
{ts(gridConfig.maxDrawdownDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -375,10 +311,10 @@ export function GridConfigEditor({
|
||||
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('stopLoss')}
|
||||
{ts(gridConfig.stopLoss, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('stopLossDesc')}
|
||||
{ts(gridConfig.stopLossDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -394,10 +330,10 @@ export function GridConfigEditor({
|
||||
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('dailyLossLimit')}
|
||||
{ts(gridConfig.dailyLossLimit, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('dailyLossLimitDesc')}
|
||||
{ts(gridConfig.dailyLossLimitDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -417,10 +353,10 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="block text-sm" style={{ color: '#EAECEF' }}>
|
||||
{t('useMakerOnly')}
|
||||
{ts(gridConfig.useMakerOnly, language)}
|
||||
</label>
|
||||
<p className="text-xs" style={{ color: '#848E9C' }}>
|
||||
{t('useMakerOnlyDesc')}
|
||||
{ts(gridConfig.useMakerOnlyDesc, language)}
|
||||
</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
@@ -442,7 +378,7 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Compass className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('directionAdjust')}
|
||||
{ts(gridConfig.directionAdjust, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -451,10 +387,10 @@ export function GridConfigEditor({
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="block text-sm" style={{ color: '#EAECEF' }}>
|
||||
{t('enableDirectionAdjust')}
|
||||
{ts(gridConfig.enableDirectionAdjust, language)}
|
||||
</label>
|
||||
<p className="text-xs" style={{ color: '#848E9C' }}>
|
||||
{t('enableDirectionAdjustDesc')}
|
||||
{ts(gridConfig.enableDirectionAdjustDesc, language)}
|
||||
</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
@@ -475,30 +411,30 @@ export function GridConfigEditor({
|
||||
{/* Direction Modes Explanation */}
|
||||
<div className="p-4 rounded-lg mb-4" style={{ background: '#1E2329', border: '1px solid #F0B90B33' }}>
|
||||
<p className="text-xs font-medium mb-2" style={{ color: '#F0B90B' }}>
|
||||
📊 {t('directionModes')}
|
||||
📊 {ts(gridConfig.directionModes, language)}
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-xs" style={{ color: '#848E9C' }}>
|
||||
<div>• {t('modeNeutral')}</div>
|
||||
<div>• <span style={{ color: '#0ECB81' }}>{t('modeLongBias')}</span></div>
|
||||
<div>• <span style={{ color: '#0ECB81' }}>{t('modeLong')}</span></div>
|
||||
<div>• <span style={{ color: '#F6465D' }}>{t('modeShortBias')}</span></div>
|
||||
<div>• <span style={{ color: '#F6465D' }}>{t('modeShort')}</span></div>
|
||||
<div>• {ts(gridConfig.modeNeutral, language)}</div>
|
||||
<div>• <span style={{ color: '#0ECB81' }}>{ts(gridConfig.modeLongBias, language)}</span></div>
|
||||
<div>• <span style={{ color: '#0ECB81' }}>{ts(gridConfig.modeLong, language)}</span></div>
|
||||
<div>• <span style={{ color: '#F6465D' }}>{ts(gridConfig.modeShortBias, language)}</span></div>
|
||||
<div>• <span style={{ color: '#F6465D' }}>{ts(gridConfig.modeShort, language)}</span></div>
|
||||
</div>
|
||||
<p className="text-xs mt-3 pt-2 border-t border-zinc-700" style={{ color: '#848E9C' }}>
|
||||
💡 {t('directionExplain')}
|
||||
💡 {ts(gridConfig.directionExplain, language)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Bias Strength */}
|
||||
<div className="p-4 rounded-lg" style={sectionStyle}>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('directionBiasRatio')} (X)
|
||||
{ts(gridConfig.directionBiasRatio, language)} (X)
|
||||
</label>
|
||||
<p className="text-xs mb-1" style={{ color: '#848E9C' }}>
|
||||
{t('directionBiasRatioDesc')}
|
||||
{ts(gridConfig.directionBiasRatioDesc, language)}
|
||||
</p>
|
||||
<p className="text-xs mb-3" style={{ color: '#F0B90B' }}>
|
||||
{t('directionBiasExplain')}
|
||||
{ts(gridConfig.directionBiasExplain, language)}
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
@@ -518,12 +454,12 @@ export function GridConfigEditor({
|
||||
</div>
|
||||
<div className="mt-2 grid grid-cols-2 gap-2 text-xs">
|
||||
<div className="p-2 rounded" style={{ background: '#0ECB8115', border: '1px solid #0ECB8130' }}>
|
||||
<span style={{ color: '#0ECB81' }}>偏多/Long Bias: </span>
|
||||
<span style={{ color: '#EAECEF' }}>{Math.round((config.direction_bias_ratio ?? 0.7) * 100)}% 买 + {Math.round((1 - (config.direction_bias_ratio ?? 0.7)) * 100)}% 卖</span>
|
||||
<span style={{ color: '#0ECB81' }}>Long Bias: </span>
|
||||
<span style={{ color: '#EAECEF' }}>{Math.round((config.direction_bias_ratio ?? 0.7) * 100)}% {ts(gridConfig.buy, language)} + {Math.round((1 - (config.direction_bias_ratio ?? 0.7)) * 100)}% {ts(gridConfig.sell, language)}</span>
|
||||
</div>
|
||||
<div className="p-2 rounded" style={{ background: '#F6465D15', border: '1px solid #F6465D30' }}>
|
||||
<span style={{ color: '#F6465D' }}>偏空/Short Bias: </span>
|
||||
<span style={{ color: '#EAECEF' }}>{Math.round((1 - (config.direction_bias_ratio ?? 0.7)) * 100)}% 买 + {Math.round((config.direction_bias_ratio ?? 0.7) * 100)}% 卖</span>
|
||||
<span style={{ color: '#F6465D' }}>Short Bias: </span>
|
||||
<span style={{ color: '#EAECEF' }}>{Math.round((1 - (config.direction_bias_ratio ?? 0.7)) * 100)}% {ts(gridConfig.buy, language)} + {Math.round((config.direction_bias_ratio ?? 0.7) * 100)}% {ts(gridConfig.sell, language)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { Shield, TrendingUp, AlertTriangle, Activity, Box, ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import type { GridRiskInfo } from '../../types'
|
||||
import { gridRisk, ts } from '../../i18n/strategy-translations'
|
||||
|
||||
interface GridRiskPanelProps {
|
||||
traderId: string
|
||||
@@ -18,66 +19,6 @@ export function GridRiskPanel({
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
|
||||
const t = (key: string) => {
|
||||
const translations: Record<string, Record<string, string>> = {
|
||||
// Section titles
|
||||
gridRisk: { zh: '网格风控', en: 'Grid Risk' },
|
||||
leverageInfo: { zh: '杠杆', en: 'Leverage' },
|
||||
positionInfo: { zh: '仓位', en: 'Position' },
|
||||
liquidationInfo: { zh: '清算', en: 'Liquidation' },
|
||||
marketState: { zh: '市场', en: 'Market' },
|
||||
boxState: { zh: '箱体', en: 'Box' },
|
||||
|
||||
// Leverage
|
||||
currentLeverage: { zh: '当前', en: 'Current' },
|
||||
effectiveLeverage: { zh: '有效', en: 'Effective' },
|
||||
recommendedLeverage: { zh: '建议', en: 'Recommend' },
|
||||
|
||||
// Position
|
||||
currentPosition: { zh: '当前', en: 'Current' },
|
||||
maxPosition: { zh: '最大', en: 'Max' },
|
||||
positionPercent: { zh: '占比', en: 'Usage' },
|
||||
|
||||
// Liquidation
|
||||
liquidationPrice: { zh: '清算价', en: 'Liq Price' },
|
||||
liquidationDistance: { zh: '距离', en: 'Distance' },
|
||||
|
||||
// Market
|
||||
regimeLevel: { zh: '波动', en: 'Regime' },
|
||||
currentPrice: { zh: '价格', en: 'Price' },
|
||||
breakoutLevel: { zh: '突破', en: 'Breakout' },
|
||||
breakoutDirection: { zh: '方向', en: 'Direction' },
|
||||
|
||||
// Box
|
||||
shortBox: { zh: '短期', en: 'Short' },
|
||||
midBox: { zh: '中期', en: 'Mid' },
|
||||
longBox: { zh: '长期', en: 'Long' },
|
||||
|
||||
// Regime levels
|
||||
narrow: { zh: '窄幅', en: 'Narrow' },
|
||||
standard: { zh: '标准', en: 'Standard' },
|
||||
wide: { zh: '宽幅', en: 'Wide' },
|
||||
volatile: { zh: '剧烈', en: 'Volatile' },
|
||||
trending: { zh: '趋势', en: 'Trending' },
|
||||
|
||||
// Breakout levels
|
||||
none: { zh: '无', en: 'None' },
|
||||
short: { zh: '短期', en: 'Short' },
|
||||
mid: { zh: '中期', en: 'Mid' },
|
||||
long: { zh: '长期', en: 'Long' },
|
||||
|
||||
// Directions
|
||||
up: { zh: '↑', en: '↑' },
|
||||
down: { zh: '↓', en: '↓' },
|
||||
|
||||
// Status
|
||||
loading: { zh: '加载中...', en: 'Loading...' },
|
||||
error: { zh: '加载失败', en: 'Load Failed' },
|
||||
noData: { zh: '暂无数据', en: 'No Data' },
|
||||
}
|
||||
return translations[key]?.[language] || key
|
||||
}
|
||||
|
||||
const fetchRiskInfo = useCallback(async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('auth_token')
|
||||
@@ -153,7 +94,7 @@ export function GridRiskPanel({
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="p-3 text-center text-xs" style={{ color: '#848E9C' }}>
|
||||
{t('loading')}
|
||||
{ts(gridRisk.loading, language)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -161,7 +102,7 @@ export function GridRiskPanel({
|
||||
if (error) {
|
||||
return (
|
||||
<div className="p-3 text-center text-xs" style={{ color: '#F6465D' }}>
|
||||
{t('error')}: {error}
|
||||
{ts(gridRisk.error, language)}: {error}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -169,7 +110,7 @@ export function GridRiskPanel({
|
||||
if (!riskInfo) {
|
||||
return (
|
||||
<div className="p-3 text-center text-xs" style={{ color: '#848E9C' }}>
|
||||
{t('noData')}
|
||||
{ts(gridRisk.noData, language)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -184,7 +125,7 @@ export function GridRiskPanel({
|
||||
<div className="flex items-center gap-2">
|
||||
<Shield className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||
<span className="font-medium text-sm" style={{ color: '#EAECEF' }}>
|
||||
{t('gridRisk')}
|
||||
{ts(gridRisk.gridRisk, language)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -194,7 +135,7 @@ export function GridRiskPanel({
|
||||
className="px-2 py-0.5 rounded"
|
||||
style={{ background: getRegimeColor(riskInfo.regime_level) + '20', color: getRegimeColor(riskInfo.regime_level) }}
|
||||
>
|
||||
{t(riskInfo.regime_level || 'standard')}
|
||||
{ts(gridRisk[(riskInfo.regime_level || 'standard') as keyof typeof gridRisk], language)}
|
||||
</span>
|
||||
<span className="font-mono" style={{ color: '#EAECEF' }}>
|
||||
{riskInfo.effective_leverage.toFixed(1)}x
|
||||
@@ -223,19 +164,19 @@ export function GridRiskPanel({
|
||||
<div className="p-2 rounded" style={{ background: '#1E2329' }}>
|
||||
<div className="flex items-center gap-1 mb-2">
|
||||
<TrendingUp className="w-3 h-3" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{t('leverageInfo')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{ts(gridRisk.leverageInfo, language)}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-1 text-xs">
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('currentLeverage')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.currentLeverage, language)}</div>
|
||||
<div className="font-mono" style={{ color: '#EAECEF' }}>{riskInfo.current_leverage}x</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('effectiveLeverage')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.effectiveLeverage, language)}</div>
|
||||
<div className="font-mono" style={{ color: '#F0B90B' }}>{riskInfo.effective_leverage.toFixed(2)}x</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('recommendedLeverage')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.recommendedLeverage, language)}</div>
|
||||
<div
|
||||
className="font-mono"
|
||||
style={{ color: riskInfo.current_leverage > riskInfo.recommended_leverage ? '#F6465D' : '#0ECB81' }}
|
||||
@@ -250,19 +191,19 @@ export function GridRiskPanel({
|
||||
<div className="p-2 rounded" style={{ background: '#1E2329' }}>
|
||||
<div className="flex items-center gap-1 mb-2">
|
||||
<Activity className="w-3 h-3" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{t('positionInfo')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{ts(gridRisk.positionInfo, language)}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-1 text-xs">
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('currentPosition')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.currentPosition, language)}</div>
|
||||
<div className="font-mono" style={{ color: '#EAECEF' }}>{formatUSD(riskInfo.current_position)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('maxPosition')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.maxPosition, language)}</div>
|
||||
<div className="font-mono" style={{ color: '#EAECEF' }}>{formatUSD(riskInfo.max_position)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('positionPercent')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.positionPercent, language)}</div>
|
||||
<div className="font-mono" style={{ color: getPositionColor(riskInfo.position_percent) }}>
|
||||
{riskInfo.position_percent.toFixed(1)}%
|
||||
</div>
|
||||
@@ -284,32 +225,32 @@ export function GridRiskPanel({
|
||||
<div className="p-2 rounded" style={{ background: '#1E2329' }}>
|
||||
<div className="flex items-center gap-1 mb-2">
|
||||
<Shield className="w-3 h-3" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{t('marketState')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{ts(gridRisk.marketState, language)}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('regimeLevel')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.regimeLevel, language)}</div>
|
||||
<div className="font-medium" style={{ color: getRegimeColor(riskInfo.regime_level) }}>
|
||||
{t(riskInfo.regime_level || 'standard')}
|
||||
{ts(gridRisk[(riskInfo.regime_level || 'standard') as keyof typeof gridRisk], language)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('currentPrice')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.currentPrice, language)}</div>
|
||||
<div className="font-mono" style={{ color: '#EAECEF' }}>{formatPrice(riskInfo.current_price)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('breakoutLevel')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.breakoutLevel, language)}</div>
|
||||
<div className="font-medium" style={{ color: getBreakoutColor(riskInfo.breakout_level) }}>
|
||||
{t(riskInfo.breakout_level || 'none')}
|
||||
{ts(gridRisk[(riskInfo.breakout_level || 'none') as keyof typeof gridRisk], language)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('breakoutDirection')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.breakoutDirection, language)}</div>
|
||||
<div
|
||||
className="font-medium"
|
||||
style={{ color: riskInfo.breakout_direction === 'up' ? '#0ECB81' : riskInfo.breakout_direction === 'down' ? '#F6465D' : '#848E9C' }}
|
||||
>
|
||||
{riskInfo.breakout_direction ? t(riskInfo.breakout_direction) : '-'}
|
||||
{riskInfo.breakout_direction ? ts(gridRisk[riskInfo.breakout_direction as keyof typeof gridRisk], language) : '-'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -319,17 +260,17 @@ export function GridRiskPanel({
|
||||
<div className="p-2 rounded" style={{ background: '#1E2329' }}>
|
||||
<div className="flex items-center gap-1 mb-2">
|
||||
<AlertTriangle className="w-3 h-3" style={{ color: '#F6465D' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{t('liquidationInfo')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{ts(gridRisk.liquidationInfo, language)}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('liquidationPrice')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.liquidationPrice, language)}</div>
|
||||
<div className="font-mono" style={{ color: '#F6465D' }}>
|
||||
{riskInfo.liquidation_price > 0 ? formatPrice(riskInfo.liquidation_price) : '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: '#5E6673' }}>{t('liquidationDistance')}</div>
|
||||
<div style={{ color: '#5E6673' }}>{ts(gridRisk.liquidationDistance, language)}</div>
|
||||
<div className="font-mono" style={{ color: '#F6465D' }}>
|
||||
{riskInfo.liquidation_distance > 0 ? `${riskInfo.liquidation_distance.toFixed(1)}%` : '-'}
|
||||
</div>
|
||||
@@ -342,23 +283,23 @@ export function GridRiskPanel({
|
||||
<div className="p-2 rounded" style={{ background: '#1E2329' }}>
|
||||
<div className="flex items-center gap-1 mb-2">
|
||||
<Box className="w-3 h-3" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{t('boxState')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#848E9C' }}>{ts(gridRisk.boxState, language)}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2 text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span style={{ color: '#5E6673' }}>{t('shortBox')}</span>
|
||||
<span style={{ color: '#5E6673' }}>{ts(gridRisk.shortBox, language)}</span>
|
||||
<span className="font-mono" style={{ color: '#EAECEF' }}>
|
||||
{formatPrice(riskInfo.short_box_lower)} - {formatPrice(riskInfo.short_box_upper)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span style={{ color: '#5E6673' }}>{t('midBox')}</span>
|
||||
<span style={{ color: '#5E6673' }}>{ts(gridRisk.midBox, language)}</span>
|
||||
<span className="font-mono" style={{ color: '#EAECEF' }}>
|
||||
{formatPrice(riskInfo.mid_box_lower)} - {formatPrice(riskInfo.mid_box_upper)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span style={{ color: '#5E6673' }}>{t('longBox')}</span>
|
||||
<span style={{ color: '#5E6673' }}>{ts(gridRisk.longBox, language)}</span>
|
||||
<span className="font-mono" style={{ color: '#EAECEF' }}>
|
||||
{formatPrice(riskInfo.long_box_lower)} - {formatPrice(riskInfo.long_box_upper)}
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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'
|
||||
|
||||
// Default NofxOS API Key
|
||||
const DEFAULT_NOFXOS_API_KEY = 'cm_568c67eae410d912c54c'
|
||||
@@ -11,7 +12,7 @@ interface IndicatorEditorProps {
|
||||
language: string
|
||||
}
|
||||
|
||||
// 所有可用时间周期
|
||||
// All available timeframes
|
||||
const allTimeframes = [
|
||||
{ value: '1m', label: '1m', category: 'scalp' },
|
||||
{ value: '3m', label: '3m', category: 'scalp' },
|
||||
@@ -35,92 +36,10 @@ export function IndicatorEditor({
|
||||
disabled,
|
||||
language,
|
||||
}: IndicatorEditorProps) {
|
||||
const t = (key: string) => {
|
||||
const translations: Record<string, Record<string, string>> = {
|
||||
// Section titles
|
||||
marketData: { zh: '市场数据', en: 'Market Data' },
|
||||
marketDataDesc: { zh: 'AI 分析所需的核心价格数据', en: 'Core price data for AI analysis' },
|
||||
technicalIndicators: { zh: '技术指标', en: 'Technical Indicators' },
|
||||
technicalIndicatorsDesc: { zh: '可选的技术分析指标,AI 可自行计算', en: 'Optional indicators, AI can calculate them' },
|
||||
marketSentiment: { zh: '市场情绪', en: 'Market Sentiment' },
|
||||
marketSentimentDesc: { zh: '持仓量、资金费率等市场情绪数据', en: 'OI, funding rate and market sentiment data' },
|
||||
quantData: { zh: '量化数据', en: 'Quant Data' },
|
||||
quantDataDesc: { zh: '资金流向、大户动向', en: 'Netflow, whale movements' },
|
||||
|
||||
// Timeframes
|
||||
timeframes: { zh: '时间周期', en: 'Timeframes' },
|
||||
timeframesDesc: { zh: '选择 K 线分析周期,★ 为主周期(双击设置)', en: 'Select K-line timeframes, ★ = primary (double-click)' },
|
||||
klineCount: { zh: 'K 线数量', en: 'K-line Count' },
|
||||
scalp: { zh: '超短', en: 'Scalp' },
|
||||
intraday: { zh: '日内', en: 'Intraday' },
|
||||
swing: { zh: '波段', en: 'Swing' },
|
||||
position: { zh: '趋势', en: 'Position' },
|
||||
|
||||
// Data types
|
||||
rawKlines: { zh: 'OHLCV 原始 K 线', en: 'Raw OHLCV K-lines' },
|
||||
rawKlinesDesc: { zh: '必须 - 开高低收量原始数据,AI 核心分析依据', en: 'Required - Open/High/Low/Close/Volume data for AI' },
|
||||
required: { zh: '必须', en: 'Required' },
|
||||
|
||||
// Indicators
|
||||
ema: { zh: 'EMA 均线', en: 'EMA' },
|
||||
emaDesc: { zh: '指数移动平均线', en: 'Exponential Moving Average' },
|
||||
macd: { zh: 'MACD', en: 'MACD' },
|
||||
macdDesc: { zh: '异同移动平均线', en: 'Moving Average Convergence Divergence' },
|
||||
rsi: { zh: 'RSI', en: 'RSI' },
|
||||
rsiDesc: { zh: '相对强弱指标', en: 'Relative Strength Index' },
|
||||
atr: { zh: 'ATR', en: 'ATR' },
|
||||
atrDesc: { zh: '真实波幅均值', en: 'Average True Range' },
|
||||
boll: { zh: 'BOLL 布林带', en: 'Bollinger Bands' },
|
||||
bollDesc: { zh: '布林带指标(上中下轨)', en: 'Upper/Middle/Lower Bands' },
|
||||
volume: { zh: '成交量', en: 'Volume' },
|
||||
volumeDesc: { zh: '交易量分析', en: 'Trading volume analysis' },
|
||||
oi: { zh: '持仓量', en: 'Open Interest' },
|
||||
oiDesc: { zh: '合约未平仓量', en: 'Futures open interest' },
|
||||
fundingRate: { zh: '资金费率', en: 'Funding Rate' },
|
||||
fundingRateDesc: { zh: '永续合约资金费率', en: 'Perpetual funding rate' },
|
||||
|
||||
// OI Ranking
|
||||
oiRanking: { zh: 'OI 排行', en: 'OI Ranking' },
|
||||
oiRankingDesc: { zh: '持仓量增减排行', en: 'OI change ranking' },
|
||||
oiRankingNote: { zh: '显示持仓量增加/减少的币种排行,帮助发现资金流向', en: 'Shows coins with OI increase/decrease, helps identify capital flow' },
|
||||
|
||||
// NetFlow Ranking
|
||||
netflowRanking: { zh: '资金流向', en: 'NetFlow' },
|
||||
netflowRankingDesc: { zh: '机构/散户资金流向', en: 'Institution/retail fund flow' },
|
||||
netflowRankingNote: { zh: '显示机构资金流入/流出排行,散户动向对比,发现聪明钱信号', en: 'Shows institution inflow/outflow ranking, retail flow comparison, Smart Money signals' },
|
||||
|
||||
// Price Ranking
|
||||
priceRanking: { zh: '涨跌幅排行', en: 'Price Ranking' },
|
||||
priceRankingDesc: { zh: '涨跌幅排行榜', en: 'Gainers/losers ranking' },
|
||||
priceRankingNote: { zh: '显示涨幅/跌幅排行,结合资金流和持仓变化分析趋势强度', en: 'Shows top gainers/losers, combined with fund flow and OI for trend analysis' },
|
||||
priceRankingMulti: { zh: '多周期', en: 'Multi-period' },
|
||||
|
||||
// Common settings
|
||||
duration: { zh: '周期', en: 'Duration' },
|
||||
limit: { zh: '数量', en: 'Limit' },
|
||||
|
||||
// Tips
|
||||
aiCanCalculate: { zh: '💡 提示:AI 可自行计算这些指标,开启可减少 AI 计算量', en: '💡 Tip: AI can calculate these, enabling reduces AI workload' },
|
||||
|
||||
// NofxOS Data Provider
|
||||
nofxosTitle: { zh: 'NofxOS 量化数据源', en: 'NofxOS Data Provider' },
|
||||
nofxosDesc: { zh: '专业加密货币量化数据服务', en: 'Professional crypto quant data service' },
|
||||
nofxosFeatures: { zh: 'AI500 · OI排行 · 资金流向 · 涨跌榜', en: 'AI500 · OI Ranking · Fund Flow · Price Ranking' },
|
||||
viewApiDocs: { zh: 'API 文档', en: 'API Docs' },
|
||||
apiKey: { zh: 'API Key', en: 'API Key' },
|
||||
apiKeyPlaceholder: { zh: '输入 NofxOS API Key', en: 'Enter NofxOS API Key' },
|
||||
fillDefault: { zh: '填入默认', en: 'Fill Default' },
|
||||
connected: { zh: '已配置', en: 'Configured' },
|
||||
notConfigured: { zh: '未配置', en: 'Not Configured' },
|
||||
nofxosDataSources: { zh: 'NofxOS 数据源', en: 'NofxOS Data Sources' },
|
||||
}
|
||||
return translations[key]?.[language] || key
|
||||
}
|
||||
|
||||
// 获取当前选中的时间周期
|
||||
// Get currently selected timeframes
|
||||
const selectedTimeframes = config.klines.selected_timeframes || [config.klines.primary_timeframe]
|
||||
|
||||
// 切换时间周期选择
|
||||
// Toggle timeframe selection
|
||||
const toggleTimeframe = (tf: string) => {
|
||||
if (disabled) return
|
||||
const current = [...selectedTimeframes]
|
||||
@@ -153,7 +72,7 @@ export function IndicatorEditor({
|
||||
}
|
||||
}
|
||||
|
||||
// 设置主时间周期
|
||||
// Set primary timeframe
|
||||
const setPrimaryTimeframe = (tf: string) => {
|
||||
if (disabled) return
|
||||
onChange({
|
||||
@@ -218,10 +137,10 @@ export function IndicatorEditor({
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold" style={{ color: '#EAECEF' }}>
|
||||
{t('nofxosTitle')}
|
||||
{ts(indicator.nofxosTitle, language)}
|
||||
</h3>
|
||||
<span className="text-[10px]" style={{ color: '#848E9C' }}>
|
||||
{t('nofxosFeatures')}
|
||||
{ts(indicator.nofxosFeatures, language)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -231,12 +150,12 @@ export function IndicatorEditor({
|
||||
{hasApiKey ? (
|
||||
<span className="flex items-center gap-1 text-[10px] px-2 py-1 rounded-full" style={{ background: 'rgba(14, 203, 129, 0.15)', color: '#0ECB81' }}>
|
||||
<Check className="w-3 h-3" />
|
||||
{t('connected')}
|
||||
{ts(indicator.connected, language)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-1 text-[10px] px-2 py-1 rounded-full" style={{ background: 'rgba(246, 70, 93, 0.15)', color: '#F6465D' }}>
|
||||
<AlertCircle className="w-3 h-3" />
|
||||
{t('notConfigured')}
|
||||
{ts(indicator.notConfigured, language)}
|
||||
</span>
|
||||
)}
|
||||
<a
|
||||
@@ -250,7 +169,7 @@ export function IndicatorEditor({
|
||||
}}
|
||||
>
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
{t('viewApiDocs')}
|
||||
{ts(indicator.viewApiDocs, language)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -264,7 +183,7 @@ export function IndicatorEditor({
|
||||
value={config.nofxos_api_key || ''}
|
||||
onChange={(e) => !disabled && onChange({ ...config, nofxos_api_key: e.target.value })}
|
||||
disabled={disabled}
|
||||
placeholder={t('apiKeyPlaceholder')}
|
||||
placeholder={ts(indicator.apiKeyPlaceholder, language)}
|
||||
className="w-full pl-9 pr-3 py-2 rounded-lg text-sm font-mono"
|
||||
style={{
|
||||
background: 'rgba(30, 35, 41, 0.8)',
|
||||
@@ -283,7 +202,7 @@ export function IndicatorEditor({
|
||||
color: '#fff',
|
||||
}}
|
||||
>
|
||||
{t('fillDefault')}
|
||||
{ts(indicator.fillDefault, language)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -291,7 +210,7 @@ export function IndicatorEditor({
|
||||
{/* NofxOS Data Sources Grid */}
|
||||
<div className="mt-4">
|
||||
<div className="text-[10px] font-medium mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('nofxosDataSources')}
|
||||
{ts(indicator.nofxosDataSources, language)}
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{/* Quant Data */}
|
||||
@@ -307,7 +226,7 @@ export function IndicatorEditor({
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full" style={{ background: '#60a5fa' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{t('quantData')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.quantData, language)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -317,7 +236,7 @@ export function IndicatorEditor({
|
||||
className="w-3.5 h-3.5 rounded accent-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{t('quantDataDesc')}</p>
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{ts(indicator.quantDataDesc, language)}</p>
|
||||
{config.enable_quant_data && (
|
||||
<div className="flex gap-3 mt-2">
|
||||
<label className="flex items-center gap-1.5 cursor-pointer">
|
||||
@@ -362,7 +281,7 @@ export function IndicatorEditor({
|
||||
<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>
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.oiRanking, language)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -377,7 +296,7 @@ export function IndicatorEditor({
|
||||
className="w-3.5 h-3.5 rounded accent-green-500"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{t('oiRankingDesc')}</p>
|
||||
<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
|
||||
@@ -422,7 +341,7 @@ export function IndicatorEditor({
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full" style={{ background: '#f59e0b' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{t('netflowRanking')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.netflowRanking, language)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -437,7 +356,7 @@ export function IndicatorEditor({
|
||||
className="w-3.5 h-3.5 rounded accent-amber-500"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{t('netflowRankingDesc')}</p>
|
||||
<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
|
||||
@@ -482,7 +401,7 @@ export function IndicatorEditor({
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full" style={{ background: '#ec4899' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{t('priceRanking')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.priceRanking, language)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -497,7 +416,7 @@ export function IndicatorEditor({
|
||||
className="w-3.5 h-3.5 rounded accent-pink-500"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[10px] mt-1" style={{ color: '#5E6673' }}>{t('priceRankingDesc')}</p>
|
||||
<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
|
||||
@@ -510,7 +429,7 @@ export function IndicatorEditor({
|
||||
<option value="1h">1h</option>
|
||||
<option value="4h">4h</option>
|
||||
<option value="24h">24h</option>
|
||||
<option value="1h,4h,24h">{t('priceRankingMulti')}</option>
|
||||
<option value="1h,4h,24h">{ts(indicator.priceRankingMulti, language)}</option>
|
||||
</select>
|
||||
<select
|
||||
value={config.price_ranking_limit || 10}
|
||||
@@ -531,7 +450,7 @@ export function IndicatorEditor({
|
||||
<div className="flex items-center gap-2 mt-3 p-2 rounded-lg" style={{ background: 'rgba(246, 70, 93, 0.1)', border: '1px solid rgba(246, 70, 93, 0.2)' }}>
|
||||
<AlertCircle className="w-4 h-4 flex-shrink-0" style={{ color: '#F6465D' }} />
|
||||
<span className="text-[10px]" style={{ color: '#F6465D' }}>
|
||||
{language === 'zh' ? '请配置 API Key 以启用 NofxOS 数据源' : 'Please configure API Key to enable NofxOS data sources'}
|
||||
{ts(indicator.configureApiKey, language)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -545,8 +464,8 @@ export function IndicatorEditor({
|
||||
<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' }}>
|
||||
<BarChart2 className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{t('marketData')}</span>
|
||||
<span className="text-xs" style={{ color: '#848E9C' }}>- {t('marketDataDesc')}</span>
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.marketData, language)}</span>
|
||||
<span className="text-xs" style={{ color: '#848E9C' }}>- {ts(indicator.marketDataDesc, language)}</span>
|
||||
</div>
|
||||
|
||||
<div className="p-3 space-y-4">
|
||||
@@ -558,13 +477,13 @@ export function IndicatorEditor({
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{t('rawKlines')}</span>
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.rawKlines, language)}</span>
|
||||
<span className="px-1.5 py-0.5 rounded text-[10px] font-medium flex items-center gap-1" style={{ background: 'rgba(240, 185, 11, 0.2)', color: '#F0B90B' }}>
|
||||
<Lock className="w-2.5 h-2.5" />
|
||||
{t('required')}
|
||||
{ts(indicator.required, language)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs mt-0.5" style={{ color: '#848E9C' }}>{t('rawKlinesDesc')}</p>
|
||||
<p className="text-xs mt-0.5" style={{ color: '#848E9C' }}>{ts(indicator.rawKlinesDesc, language)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
@@ -580,10 +499,10 @@ export function IndicatorEditor({
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-3.5 h-3.5" style={{ color: '#848E9C' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{t('timeframes')}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.timeframes, language)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-[10px]" style={{ color: '#848E9C' }}>{t('klineCount')}:</span>
|
||||
<span className="text-[10px]" style={{ color: '#848E9C' }}>{ts(indicator.klineCount, language)}:</span>
|
||||
<input
|
||||
type="number"
|
||||
value={config.klines.primary_count}
|
||||
@@ -602,7 +521,7 @@ export function IndicatorEditor({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[10px] mb-2" style={{ color: '#5E6673' }}>{t('timeframesDesc')}</p>
|
||||
<p className="text-[10px] mb-2" style={{ color: '#5E6673' }}>{ts(indicator.timeframesDesc, language)}</p>
|
||||
|
||||
{/* Timeframe Grid */}
|
||||
<div className="space-y-1.5">
|
||||
@@ -611,7 +530,7 @@ export function IndicatorEditor({
|
||||
return (
|
||||
<div key={category} className="flex items-center gap-2">
|
||||
<span className="text-[10px] w-10 flex-shrink-0" style={{ color: categoryColors[category] }}>
|
||||
{t(category)}
|
||||
{ts(indicator[category], language)}
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{categoryTfs.map((tf) => {
|
||||
@@ -654,15 +573,15 @@ export function IndicatorEditor({
|
||||
<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' }}>
|
||||
<Activity className="w-4 h-4" style={{ color: '#0ECB81' }} />
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{t('technicalIndicators')}</span>
|
||||
<span className="text-xs" style={{ color: '#848E9C' }}>- {t('technicalIndicatorsDesc')}</span>
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.technicalIndicators, language)}</span>
|
||||
<span className="text-xs" style={{ color: '#848E9C' }}>- {ts(indicator.technicalIndicatorsDesc, language)}</span>
|
||||
</div>
|
||||
|
||||
<div className="p-3">
|
||||
{/* Tip */}
|
||||
<div className="flex items-start gap-2 mb-3 p-2 rounded" style={{ background: 'rgba(14, 203, 129, 0.05)' }}>
|
||||
<Info className="w-3.5 h-3.5 mt-0.5 flex-shrink-0" style={{ color: '#0ECB81' }} />
|
||||
<p className="text-[10px]" style={{ color: '#848E9C' }}>{t('aiCanCalculate')}</p>
|
||||
<p className="text-[10px]" style={{ color: '#848E9C' }}>{ts(indicator.aiCanCalculate, language)}</p>
|
||||
</div>
|
||||
|
||||
{/* Indicator Grid */}
|
||||
@@ -685,7 +604,7 @@ export function IndicatorEditor({
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full" style={{ background: color }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{t(label)}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{ts(indicator[label as keyof typeof indicator], language)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -695,7 +614,7 @@ export function IndicatorEditor({
|
||||
className="w-4 h-4 rounded accent-yellow-500"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[10px] mb-1.5" style={{ color: '#5E6673' }}>{t(desc)}</p>
|
||||
<p className="text-[10px] mb-1.5" style={{ color: '#5E6673' }}>{ts(indicator[desc as keyof typeof indicator], language)}</p>
|
||||
{periodKey && config[key as keyof IndicatorConfig] && (
|
||||
<input
|
||||
type="text"
|
||||
@@ -726,8 +645,8 @@ export function IndicatorEditor({
|
||||
<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' }}>
|
||||
<TrendingUp className="w-4 h-4" style={{ color: '#22c55e' }} />
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{t('marketSentiment')}</span>
|
||||
<span className="text-xs" style={{ color: '#848E9C' }}>- {t('marketSentimentDesc')}</span>
|
||||
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}>{ts(indicator.marketSentiment, language)}</span>
|
||||
<span className="text-xs" style={{ color: '#848E9C' }}>- {ts(indicator.marketSentimentDesc, language)}</span>
|
||||
</div>
|
||||
|
||||
<div className="p-3">
|
||||
@@ -748,7 +667,7 @@ export function IndicatorEditor({
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full" style={{ background: color }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{t(label)}</span>
|
||||
<span className="text-xs font-medium" style={{ color: '#EAECEF' }}>{ts(indicator[label as keyof typeof indicator], language)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -758,7 +677,7 @@ export function IndicatorEditor({
|
||||
className="w-4 h-4 rounded accent-yellow-500"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[10px]" style={{ color: '#5E6673' }}>{t(desc)}</p>
|
||||
<p className="text-[10px]" style={{ color: '#5E6673' }}>{ts(indicator[desc as keyof typeof indicator], language)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { ChevronDown, ChevronRight, RotateCcw, FileText } from 'lucide-react'
|
||||
import type { PromptSectionsConfig } from '../../types'
|
||||
import { promptSections as promptSectionsI18n, ts } from '../../i18n/strategy-translations'
|
||||
|
||||
interface PromptSectionsEditorProps {
|
||||
config: PromptSectionsConfig | undefined
|
||||
@@ -54,29 +55,11 @@ export function PromptSectionsEditor({
|
||||
decision_process: false,
|
||||
})
|
||||
|
||||
const t = (key: string) => {
|
||||
const translations: Record<string, Record<string, string>> = {
|
||||
promptSections: { zh: 'System Prompt 自定义', en: 'System Prompt Customization' },
|
||||
promptSectionsDesc: { zh: '自定义 AI 行为和决策逻辑(输出格式和风控规则不可修改)', en: 'Customize AI behavior and decision logic (output format and risk rules are fixed)' },
|
||||
roleDefinition: { zh: '角色定义', en: 'Role Definition' },
|
||||
roleDefinitionDesc: { zh: '定义 AI 的身份和核心目标', en: 'Define AI identity and core objectives' },
|
||||
tradingFrequency: { zh: '交易频率', en: 'Trading Frequency' },
|
||||
tradingFrequencyDesc: { zh: '设定交易频率预期和过度交易警告', en: 'Set trading frequency expectations and overtrading warnings' },
|
||||
entryStandards: { zh: '开仓标准', en: 'Entry Standards' },
|
||||
entryStandardsDesc: { zh: '定义开仓信号条件和避免事项', en: 'Define entry signal conditions and avoidances' },
|
||||
decisionProcess: { zh: '决策流程', en: 'Decision Process' },
|
||||
decisionProcessDesc: { zh: '设定决策步骤和思考流程', en: 'Set decision steps and thinking process' },
|
||||
resetToDefault: { zh: '重置为默认', en: 'Reset to Default' },
|
||||
chars: { zh: '字符', en: 'chars' },
|
||||
}
|
||||
return translations[key]?.[language] || key
|
||||
}
|
||||
|
||||
const sections = [
|
||||
{ key: 'role_definition', label: t('roleDefinition'), desc: t('roleDefinitionDesc') },
|
||||
{ key: 'trading_frequency', label: t('tradingFrequency'), desc: t('tradingFrequencyDesc') },
|
||||
{ key: 'entry_standards', label: t('entryStandards'), desc: t('entryStandardsDesc') },
|
||||
{ key: 'decision_process', label: t('decisionProcess'), desc: t('decisionProcessDesc') },
|
||||
{ key: 'role_definition', label: ts(promptSectionsI18n.roleDefinition, language), desc: ts(promptSectionsI18n.roleDefinitionDesc, language) },
|
||||
{ key: 'trading_frequency', label: ts(promptSectionsI18n.tradingFrequency, language), desc: ts(promptSectionsI18n.tradingFrequencyDesc, language) },
|
||||
{ key: 'entry_standards', label: ts(promptSectionsI18n.entryStandards, language), desc: ts(promptSectionsI18n.entryStandardsDesc, language) },
|
||||
{ key: 'decision_process', label: ts(promptSectionsI18n.decisionProcess, language), desc: ts(promptSectionsI18n.decisionProcessDesc, language) },
|
||||
]
|
||||
|
||||
const currentConfig = config || {}
|
||||
@@ -107,10 +90,10 @@ export function PromptSectionsEditor({
|
||||
<FileText className="w-5 h-5 mt-0.5" style={{ color: '#a855f7' }} />
|
||||
<div>
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('promptSections')}
|
||||
{ts(promptSectionsI18n.promptSections, language)}
|
||||
</h3>
|
||||
<p className="text-xs mt-1" style={{ color: '#848E9C' }}>
|
||||
{t('promptSectionsDesc')}
|
||||
{ts(promptSectionsI18n.promptSectionsDesc, language)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -146,12 +129,12 @@ export function PromptSectionsEditor({
|
||||
className="px-1.5 py-0.5 text-[10px] rounded"
|
||||
style={{ background: 'rgba(168, 85, 247, 0.15)', color: '#a855f7' }}
|
||||
>
|
||||
{language === 'zh' ? '已修改' : 'Modified'}
|
||||
{ts(promptSectionsI18n.modified, language)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[10px]" style={{ color: '#848E9C' }}>
|
||||
{value.length} {t('chars')}
|
||||
{value.length} {ts(promptSectionsI18n.chars, language)}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -181,7 +164,7 @@ export function PromptSectionsEditor({
|
||||
style={{ color: '#848E9C' }}
|
||||
>
|
||||
<RotateCcw className="w-3 h-3" />
|
||||
{t('resetToDefault')}
|
||||
{ts(promptSectionsI18n.resetToDefault, language)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Globe, Lock, Eye, EyeOff } from 'lucide-react'
|
||||
import { publishSettings, ts } from '../../i18n/strategy-translations'
|
||||
|
||||
interface PublishSettingsEditorProps {
|
||||
isPublic: boolean
|
||||
@@ -17,23 +18,9 @@ export function PublishSettingsEditor({
|
||||
disabled = false,
|
||||
language,
|
||||
}: PublishSettingsEditorProps) {
|
||||
const t = (key: string) => {
|
||||
const translations: Record<string, Record<string, string>> = {
|
||||
publishToMarket: { zh: '发布到策略市场', en: 'Publish to Market' },
|
||||
publishDesc: { zh: '策略将在市场公开展示,其他用户可发现并使用', en: 'Strategy will be publicly visible in the marketplace' },
|
||||
showConfig: { zh: '公开配置参数', en: 'Show Config' },
|
||||
showConfigDesc: { zh: '允许他人查看和复制详细配置', en: 'Allow others to view and clone config details' },
|
||||
private: { zh: '私有', en: 'PRIVATE' },
|
||||
public: { zh: '公开', en: 'PUBLIC' },
|
||||
hidden: { zh: '隐藏', en: 'HIDDEN' },
|
||||
visible: { zh: '可见', en: 'VISIBLE' },
|
||||
}
|
||||
return translations[key]?.[language] || key
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{/* 发布开关 */}
|
||||
{/* Publish toggle */}
|
||||
<div
|
||||
className={`relative overflow-hidden rounded-lg transition-all duration-300 ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
|
||||
style={{
|
||||
@@ -73,10 +60,10 @@ export function PublishSettingsEditor({
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('publishToMarket')}
|
||||
{ts(publishSettings.publishToMarket, language)}
|
||||
</div>
|
||||
<div className="text-xs mt-0.5" style={{ color: '#848E9C' }}>
|
||||
{t('publishDesc')}
|
||||
{ts(publishSettings.publishDesc, language)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -87,7 +74,7 @@ export function PublishSettingsEditor({
|
||||
className="text-[10px] font-mono font-bold tracking-wider"
|
||||
style={{ color: isPublic ? '#0ECB81' : '#848E9C' }}
|
||||
>
|
||||
{isPublic ? t('public') : t('private')}
|
||||
{isPublic ? ts(publishSettings.public, language) : ts(publishSettings.private, language)}
|
||||
</span>
|
||||
<div
|
||||
className="relative w-12 h-6 rounded-full transition-all duration-300"
|
||||
@@ -111,7 +98,7 @@ export function PublishSettingsEditor({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 配置可见性开关 - 仅在公开时显示 */}
|
||||
{/* Config visibility toggle - only shown when public */}
|
||||
{isPublic && (
|
||||
<div
|
||||
className={`relative overflow-hidden rounded-lg transition-all duration-300 ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
|
||||
@@ -152,10 +139,10 @@ export function PublishSettingsEditor({
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('showConfig')}
|
||||
{ts(publishSettings.showConfig, language)}
|
||||
</div>
|
||||
<div className="text-xs mt-0.5" style={{ color: '#848E9C' }}>
|
||||
{t('showConfigDesc')}
|
||||
{ts(publishSettings.showConfigDesc, language)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -166,7 +153,7 @@ export function PublishSettingsEditor({
|
||||
className="text-[10px] font-mono font-bold tracking-wider"
|
||||
style={{ color: configVisible ? '#a855f7' : '#848E9C' }}
|
||||
>
|
||||
{configVisible ? t('visible') : t('hidden')}
|
||||
{configVisible ? ts(publishSettings.visible, language) : ts(publishSettings.hidden, language)}
|
||||
</span>
|
||||
<div
|
||||
className="relative w-12 h-6 rounded-full transition-all duration-300"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Shield, AlertTriangle } from 'lucide-react'
|
||||
import type { RiskControlConfig } from '../../types'
|
||||
import { riskControl, ts } from '../../i18n/strategy-translations'
|
||||
|
||||
interface RiskControlEditorProps {
|
||||
config: RiskControlConfig
|
||||
@@ -14,38 +15,6 @@ export function RiskControlEditor({
|
||||
disabled,
|
||||
language,
|
||||
}: RiskControlEditorProps) {
|
||||
const t = (key: string) => {
|
||||
const translations: Record<string, Record<string, string>> = {
|
||||
positionLimits: { zh: '仓位限制', en: 'Position Limits' },
|
||||
maxPositions: { zh: '最大持仓数量', en: 'Max Positions' },
|
||||
maxPositionsDesc: { zh: '同时持有的最大币种数量', en: 'Maximum coins held simultaneously' },
|
||||
// Trading leverage (exchange leverage)
|
||||
tradingLeverage: { zh: '交易杠杆(交易所杠杆)', en: 'Trading Leverage (Exchange)' },
|
||||
btcEthLeverage: { zh: 'BTC/ETH 交易杠杆', en: 'BTC/ETH Trading Leverage' },
|
||||
btcEthLeverageDesc: { zh: '交易所开仓使用的杠杆倍数', en: 'Exchange leverage for opening positions' },
|
||||
altcoinLeverage: { zh: '山寨币交易杠杆', en: 'Altcoin Trading Leverage' },
|
||||
altcoinLeverageDesc: { zh: '交易所开仓使用的杠杆倍数', en: 'Exchange leverage for opening positions' },
|
||||
// Position value ratio (risk control) - CODE ENFORCED
|
||||
positionValueRatio: { zh: '仓位价值比例(代码强制)', en: 'Position Value Ratio (CODE ENFORCED)' },
|
||||
positionValueRatioDesc: { zh: '单仓位名义价值 / 账户净值,由代码强制执行', en: 'Position notional value / equity, enforced by code' },
|
||||
btcEthPositionValueRatio: { zh: 'BTC/ETH 仓位价值比例', en: 'BTC/ETH Position Value Ratio' },
|
||||
btcEthPositionValueRatioDesc: { zh: '单仓最大名义价值 = 净值 × 此值(代码强制)', en: 'Max position value = equity × this ratio (CODE ENFORCED)' },
|
||||
altcoinPositionValueRatio: { zh: '山寨币仓位价值比例', en: 'Altcoin Position Value Ratio' },
|
||||
altcoinPositionValueRatioDesc: { zh: '单仓最大名义价值 = 净值 × 此值(代码强制)', en: 'Max position value = equity × this ratio (CODE ENFORCED)' },
|
||||
riskParameters: { zh: '风险参数', en: 'Risk Parameters' },
|
||||
minRiskReward: { zh: '最小风险回报比', en: 'Min Risk/Reward Ratio' },
|
||||
minRiskRewardDesc: { zh: '开仓要求的最低盈亏比', en: 'Minimum profit ratio for opening' },
|
||||
maxMarginUsage: { zh: '最大保证金使用率(代码强制)', en: 'Max Margin Usage (CODE ENFORCED)' },
|
||||
maxMarginUsageDesc: { zh: '保证金使用率上限,由代码强制执行', en: 'Maximum margin utilization, enforced by code' },
|
||||
entryRequirements: { zh: '开仓要求', en: 'Entry Requirements' },
|
||||
minPositionSize: { zh: '最小开仓金额', en: 'Min Position Size' },
|
||||
minPositionSizeDesc: { zh: 'USDT 最小名义价值', en: 'Minimum notional value in USDT' },
|
||||
minConfidence: { zh: '最小信心度', en: 'Min Confidence' },
|
||||
minConfidenceDesc: { zh: 'AI 开仓信心度阈值', en: 'AI confidence threshold for entry' },
|
||||
}
|
||||
return translations[key]?.[language] || key
|
||||
}
|
||||
|
||||
const updateField = <K extends keyof RiskControlConfig>(
|
||||
key: K,
|
||||
value: RiskControlConfig[K]
|
||||
@@ -62,7 +31,7 @@ export function RiskControlEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Shield className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('positionLimits')}
|
||||
{ts(riskControl.positionLimits, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -72,10 +41,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('maxPositions')}
|
||||
{ts(riskControl.maxPositions, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('maxPositionsDesc')}
|
||||
{ts(riskControl.maxPositionsDesc, language)}
|
||||
</p>
|
||||
<input
|
||||
type="number"
|
||||
@@ -99,7 +68,7 @@ export function RiskControlEditor({
|
||||
{/* Trading Leverage (Exchange) */}
|
||||
<div className="mb-2">
|
||||
<p className="text-xs font-medium mb-2" style={{ color: '#F0B90B' }}>
|
||||
{t('tradingLeverage')}
|
||||
{ts(riskControl.tradingLeverage, language)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
@@ -108,10 +77,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('btcEthLeverage')}
|
||||
{ts(riskControl.btcEthLeverage, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('btcEthLeverageDesc')}
|
||||
{ts(riskControl.btcEthLeverageDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -139,10 +108,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('altcoinLeverage')}
|
||||
{ts(riskControl.altcoinLeverage, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('altcoinLeverageDesc')}
|
||||
{ts(riskControl.altcoinLeverageDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -169,10 +138,10 @@ export function RiskControlEditor({
|
||||
{/* Position Value Ratio (Risk Control - CODE ENFORCED) */}
|
||||
<div className="mb-2">
|
||||
<p className="text-xs font-medium" style={{ color: '#0ECB81' }}>
|
||||
{t('positionValueRatio')}
|
||||
{ts(riskControl.positionValueRatio, language)}
|
||||
</p>
|
||||
<p className="text-xs mt-1" style={{ color: '#848E9C' }}>
|
||||
{t('positionValueRatioDesc')}
|
||||
{ts(riskControl.positionValueRatioDesc, language)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
@@ -181,10 +150,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #0ECB81' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('btcEthPositionValueRatio')}
|
||||
{ts(riskControl.btcEthPositionValueRatio, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('btcEthPositionValueRatioDesc')}
|
||||
{ts(riskControl.btcEthPositionValueRatioDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -213,10 +182,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #0ECB81' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('altcoinPositionValueRatio')}
|
||||
{ts(riskControl.altcoinPositionValueRatio, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('altcoinPositionValueRatioDesc')}
|
||||
{ts(riskControl.altcoinPositionValueRatioDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -247,7 +216,7 @@ export function RiskControlEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<AlertTriangle className="w-5 h-5" style={{ color: '#F6465D' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('riskParameters')}
|
||||
{ts(riskControl.riskParameters, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -257,10 +226,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('minRiskReward')}
|
||||
{ts(riskControl.minRiskReward, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('minRiskRewardDesc')}
|
||||
{ts(riskControl.minRiskRewardDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center">
|
||||
<span style={{ color: '#848E9C' }}>1:</span>
|
||||
@@ -289,10 +258,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #0ECB81' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('maxMarginUsage')}
|
||||
{ts(riskControl.maxMarginUsage, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('maxMarginUsageDesc')}
|
||||
{ts(riskControl.maxMarginUsageDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -319,7 +288,7 @@ export function RiskControlEditor({
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Shield className="w-5 h-5" style={{ color: '#0ECB81' }} />
|
||||
<h3 className="font-medium" style={{ color: '#EAECEF' }}>
|
||||
{t('entryRequirements')}
|
||||
{ts(riskControl.entryRequirements, language)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -329,10 +298,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('minPositionSize')}
|
||||
{ts(riskControl.minPositionSize, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('minPositionSizeDesc')}
|
||||
{ts(riskControl.minPositionSizeDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
@@ -362,10 +331,10 @@ export function RiskControlEditor({
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
>
|
||||
<label className="block text-sm mb-1" style={{ color: '#EAECEF' }}>
|
||||
{t('minConfidence')}
|
||||
{ts(riskControl.minConfidence, language)}
|
||||
</label>
|
||||
<p className="text-xs mb-2" style={{ color: '#848E9C' }}>
|
||||
{t('minConfidenceDesc')}
|
||||
{ts(riskControl.minConfidenceDesc, language)}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user