a
This commit is contained in:
parent
d9830f395b
commit
42a6f2c1f3
2374
frontend/package-lock.json
generated
Normal file
2374
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'
|
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'
|
||||||
import ConfigPanel from './components/ConfigPanel'
|
import ConfigPanel from './components/ConfigPanel'
|
||||||
|
import ConfigGuide from './components/ConfigGuide'
|
||||||
import TradeList from './components/TradeList'
|
import TradeList from './components/TradeList'
|
||||||
import StatsDashboard from './components/StatsDashboard'
|
import StatsDashboard from './components/StatsDashboard'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
@ -24,6 +25,7 @@ function App() {
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<StatsDashboard />} />
|
<Route path="/" element={<StatsDashboard />} />
|
||||||
<Route path="/config" element={<ConfigPanel />} />
|
<Route path="/config" element={<ConfigPanel />} />
|
||||||
|
<Route path="/config/guide" element={<ConfigGuide />} />
|
||||||
<Route path="/trades" element={<TradeList />} />
|
<Route path="/trades" element={<TradeList />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
164
frontend/src/components/ConfigGuide.css
Normal file
164
frontend/src/components/ConfigGuide.css
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
.config-guide {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
border-bottom: 2px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #2196F3;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-link:hover {
|
||||||
|
color: #1976D2;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-content {
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-section h2 {
|
||||||
|
color: #34495e;
|
||||||
|
border-left: 4px solid #2196F3;
|
||||||
|
padding-left: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-card {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-card h3 {
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-desc {
|
||||||
|
color: #666;
|
||||||
|
font-style: italic;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-params {
|
||||||
|
background: white;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-params ul {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-params li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-effect {
|
||||||
|
background: #e7f3ff;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: 3px solid #2196F3;
|
||||||
|
color: #1976D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-warning {
|
||||||
|
background: #fff3cd;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: 3px solid #ffc107;
|
||||||
|
color: #856404;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-detail {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-detail h3 {
|
||||||
|
color: #34495e;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-item {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-left: 4px solid #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-item h4 {
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-item p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-item ul,
|
||||||
|
.param-item ol {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-item li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-left: 4px solid #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips h3 {
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips ul,
|
||||||
|
.tips ol {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
294
frontend/src/components/ConfigGuide.jsx
Normal file
294
frontend/src/components/ConfigGuide.jsx
Normal file
|
|
@ -0,0 +1,294 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import './ConfigGuide.css'
|
||||||
|
|
||||||
|
const ConfigGuide = () => {
|
||||||
|
return (
|
||||||
|
<div className="config-guide">
|
||||||
|
<div className="guide-header">
|
||||||
|
<Link to="/config" className="back-link">← 返回配置页面</Link>
|
||||||
|
<h1>交易配置说明文档</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="guide-content">
|
||||||
|
<section className="guide-section">
|
||||||
|
<h2>一、预设方案说明</h2>
|
||||||
|
|
||||||
|
<div className="preset-card">
|
||||||
|
<h3>方案1:保守配置(默认)</h3>
|
||||||
|
<p className="preset-desc">适合新手或稳健型交易者,风险较低,交易频率适中</p>
|
||||||
|
<div className="preset-params">
|
||||||
|
<ul>
|
||||||
|
<li><strong>扫描间隔</strong>: 3600秒(1小时)</li>
|
||||||
|
<li><strong>最小涨跌幅</strong>: 2.0%</li>
|
||||||
|
<li><strong>信号强度</strong>: 5/10</li>
|
||||||
|
<li><strong>处理交易对</strong>: 10个</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="preset-effect">
|
||||||
|
<strong>效果:</strong>每小时扫描一次,只捕捉2%以上的波动,信号质量高,胜率较高但交易机会较少
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="preset-card">
|
||||||
|
<h3>方案2:平衡配置(推荐)</h3>
|
||||||
|
<p className="preset-desc">平衡交易频率和信号质量,适合大多数交易者</p>
|
||||||
|
<div className="preset-params">
|
||||||
|
<ul>
|
||||||
|
<li><strong>扫描间隔</strong>: 600秒(10分钟)</li>
|
||||||
|
<li><strong>最小涨跌幅</strong>: 1.5%</li>
|
||||||
|
<li><strong>信号强度</strong>: 4/10</li>
|
||||||
|
<li><strong>处理交易对</strong>: 12个</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="preset-effect">
|
||||||
|
<strong>效果:</strong>10分钟扫描一次,捕捉1.5%以上的波动,交易机会增加,信号质量仍然较高
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="preset-card">
|
||||||
|
<h3>方案3:激进高频配置</h3>
|
||||||
|
<p className="preset-desc">适合晚间波动大时使用,交易频率高,需要密切监控</p>
|
||||||
|
<div className="preset-params">
|
||||||
|
<ul>
|
||||||
|
<li><strong>扫描间隔</strong>: 300秒(5分钟)</li>
|
||||||
|
<li><strong>最小涨跌幅</strong>: 1.0%</li>
|
||||||
|
<li><strong>信号强度</strong>: 3/10</li>
|
||||||
|
<li><strong>处理交易对</strong>: 20个</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="preset-effect">
|
||||||
|
<strong>效果:</strong>5分钟扫描一次,捕捉1%以上的波动,交易机会大幅增加,但需要监控胜率和手续费
|
||||||
|
</div>
|
||||||
|
<div className="preset-warning">
|
||||||
|
⚠️ <strong>风险提示:</strong>高频交易会增加手续费成本,建议在波动大的时段使用,并密切监控胜率
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="guide-section">
|
||||||
|
<h2>二、配置参数详细说明</h2>
|
||||||
|
|
||||||
|
<div className="config-detail">
|
||||||
|
<h3>市场扫描参数</h3>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>SCAN_INTERVAL(扫描间隔)</h4>
|
||||||
|
<p><strong>单位:</strong>秒</p>
|
||||||
|
<p><strong>默认值:</strong>3600(1小时)</p>
|
||||||
|
<p><strong>说明:</strong>系统每隔多长时间扫描一次市场,寻找交易机会</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越小,扫描越频繁,能更快捕捉市场波动,但系统资源消耗增加</li>
|
||||||
|
<li>建议范围:300(5分钟)- 3600(1小时)</li>
|
||||||
|
<li>晚间波动大时建议:300-600秒</li>
|
||||||
|
<li>白天平稳时段建议:1800-3600秒</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>MIN_CHANGE_PERCENT(最小涨跌幅阈值)</h4>
|
||||||
|
<p><strong>单位:</strong>百分比(前端显示为%,存储为小数)</p>
|
||||||
|
<p><strong>默认值:</strong>2.0(2%)</p>
|
||||||
|
<p><strong>说明:</strong>只有涨跌幅达到此阈值的交易对才会被考虑交易</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越小,捕捉的交易机会越多,但可能包含更多噪音</li>
|
||||||
|
<li>值越大,只捕捉大幅波动,信号质量更高但机会更少</li>
|
||||||
|
<li>建议范围:1.0% - 3.0%</li>
|
||||||
|
<li>高频交易建议:1.0-1.5%</li>
|
||||||
|
<li>稳健交易建议:2.0-3.0%</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>MIN_SIGNAL_STRENGTH(最小信号强度)</h4>
|
||||||
|
<p><strong>单位:</strong>0-10的整数</p>
|
||||||
|
<p><strong>默认值:</strong>5</p>
|
||||||
|
<p><strong>说明:</strong>技术指标综合评分,只有达到此强度的信号才会执行交易</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越小,交易机会越多,但信号质量可能下降</li>
|
||||||
|
<li>值越大,只执行高质量信号,胜率更高但机会更少</li>
|
||||||
|
<li>建议范围:3-7</li>
|
||||||
|
<li>激进策略建议:3-4</li>
|
||||||
|
<li>稳健策略建议:5-7</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>TOP_N_SYMBOLS(处理交易对数量)</h4>
|
||||||
|
<p><strong>单位:</strong>个</p>
|
||||||
|
<p><strong>默认值:</strong>10</p>
|
||||||
|
<p><strong>说明:</strong>每次扫描后,处理涨跌幅最大的前N个交易对</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越大,处理的交易对越多,交易机会增加</li>
|
||||||
|
<li>但系统计算量增加,可能影响响应速度</li>
|
||||||
|
<li>建议范围:10-20</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>MIN_VOLATILITY(最小波动率)</h4>
|
||||||
|
<p><strong>单位:</strong>百分比(小数形式)</p>
|
||||||
|
<p><strong>默认值:</strong>0.02(2%)</p>
|
||||||
|
<p><strong>说明:</strong>交易对的最小波动率要求,过滤掉波动过小的交易对</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越小,允许更多交易对参与,但可能包含波动不足的交易对</li>
|
||||||
|
<li>建议范围:0.015(1.5%)- 0.025(2.5%)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>MIN_VOLUME_24H(最小24小时成交量)</h4>
|
||||||
|
<p><strong>单位:</strong>USDT</p>
|
||||||
|
<p><strong>默认值:</strong>10000000(1000万)</p>
|
||||||
|
<p><strong>说明:</strong>过滤掉成交量过小的交易对,确保流动性</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越小,允许更多交易对,但可能包含流动性差的交易对</li>
|
||||||
|
<li>建议不要低于500万USDT</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="config-detail">
|
||||||
|
<h3>时间周期参数</h3>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>PRIMARY_INTERVAL(主周期)</h4>
|
||||||
|
<p><strong>默认值:</strong>1h</p>
|
||||||
|
<p><strong>说明:</strong>用于计算技术指标的主要时间周期</p>
|
||||||
|
<p><strong>选项:</strong>1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 1d</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>周期越短,反应越快,但信号可能更频繁变化</li>
|
||||||
|
<li>周期越长,信号更稳定,但反应较慢</li>
|
||||||
|
<li>高频交易建议:15m-30m</li>
|
||||||
|
<li>稳健交易建议:1h-4h</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>ENTRY_INTERVAL(入场周期)</h4>
|
||||||
|
<p><strong>默认值:</strong>15m</p>
|
||||||
|
<p><strong>说明:</strong>用于确定入场时机的K线周期</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>周期越短,入场时机更精确,但可能产生更多假信号</li>
|
||||||
|
<li>建议:5m-15m</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="config-detail">
|
||||||
|
<h3>仓位控制参数</h3>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>MAX_POSITION_PERCENT(单笔最大仓位)</h4>
|
||||||
|
<p><strong>单位:</strong>账户余额的百分比</p>
|
||||||
|
<p><strong>默认值:</strong>0.05(5%)</p>
|
||||||
|
<p><strong>说明:</strong>单笔交易最多使用账户余额的百分比</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越大,单笔交易金额越大,潜在收益和风险都增加</li>
|
||||||
|
<li>建议范围:3%-10%</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>MAX_TOTAL_POSITION_PERCENT(总仓位上限)</h4>
|
||||||
|
<p><strong>单位:</strong>账户余额的百分比</p>
|
||||||
|
<p><strong>默认值:</strong>0.30(30%)</p>
|
||||||
|
<p><strong>说明:</strong>所有持仓的总价值不能超过账户余额的百分比</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越大,可以同时持有更多仓位,但风险集中度增加</li>
|
||||||
|
<li>建议范围:20%-50%</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="config-detail">
|
||||||
|
<h3>风险控制参数</h3>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>STOP_LOSS_PERCENT(止损百分比)</h4>
|
||||||
|
<p><strong>单位:</strong>百分比</p>
|
||||||
|
<p><strong>默认值:</strong>0.03(3%)</p>
|
||||||
|
<p><strong>说明:</strong>当亏损达到此百分比时自动平仓止损</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越小,止损更严格,单笔损失更小但可能被正常波动触发</li>
|
||||||
|
<li>建议范围:2%-5%</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="param-item">
|
||||||
|
<h4>TAKE_PROFIT_PERCENT(止盈百分比)</h4>
|
||||||
|
<p><strong>单位:</strong>百分比</p>
|
||||||
|
<p><strong>默认值:</strong>0.05(5%)</p>
|
||||||
|
<p><strong>说明:</strong>当盈利达到此百分比时自动平仓止盈</p>
|
||||||
|
<p><strong>影响:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>值越大,目标利润更高,但可能错过及时止盈的机会</li>
|
||||||
|
<li>建议范围:3%-8%</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="guide-section">
|
||||||
|
<h2>三、使用建议</h2>
|
||||||
|
|
||||||
|
<div className="tips">
|
||||||
|
<h3>时段差异化配置</h3>
|
||||||
|
<p>建议在不同时段使用不同配置:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>晚间波动时段(20:00-02:00 UTC+8)</strong>:使用激进高频配置,捕捉更多机会</li>
|
||||||
|
<li><strong>白天平稳时段</strong>:使用保守或平衡配置,确保信号质量</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tips">
|
||||||
|
<h3>渐进式调整</h3>
|
||||||
|
<p>不要一次性大幅调整所有参数,建议:</p>
|
||||||
|
<ol>
|
||||||
|
<li>先调整SCAN_INTERVAL(从3600→1800→600→300)</li>
|
||||||
|
<li>观察1-2天效果</li>
|
||||||
|
<li>再调整MIN_CHANGE_PERCENT和MIN_SIGNAL_STRENGTH</li>
|
||||||
|
<li>根据实际表现微调</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tips">
|
||||||
|
<h3>监控指标</h3>
|
||||||
|
<p>修改配置后,建议监控:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>胜率</strong>:如果下降明显,需要提高MIN_SIGNAL_STRENGTH</li>
|
||||||
|
<li><strong>总盈亏</strong>:确保增加的交易次数带来正收益</li>
|
||||||
|
<li><strong>手续费</strong>:高频交易会增加手续费成本</li>
|
||||||
|
<li><strong>系统负载</strong>:确保服务器能承受更频繁的扫描</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="guide-section">
|
||||||
|
<h2>四、配置生效时间</h2>
|
||||||
|
<p>配置修改后:</p>
|
||||||
|
<ul>
|
||||||
|
<li>立即保存到数据库</li>
|
||||||
|
<li>交易系统会在<strong>下次扫描时</strong>自动重新加载配置</li>
|
||||||
|
<li>如果SCAN_INTERVAL是3600秒,最多等待1小时</li>
|
||||||
|
<li>如果改为300秒,最多等待5分钟</li>
|
||||||
|
<li>无需重启交易系统</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConfigGuide
|
||||||
|
|
@ -9,6 +9,82 @@
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-top {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-link {
|
||||||
|
color: #2196F3;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: 1px solid #2196F3;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-link:hover {
|
||||||
|
background: #2196F3;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-section {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-section h3 {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
color: #34495e;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-btn {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 2px solid #dee2e6;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-btn:hover:not(:disabled) {
|
||||||
|
border-color: #2196F3;
|
||||||
|
background: #e7f3ff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-name {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-desc {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
.config-panel h2 {
|
.config-panel h2 {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
|
|
@ -128,6 +204,15 @@
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
color: #888;
|
color: #888;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-icon {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
import { api } from '../services/api'
|
import { api } from '../services/api'
|
||||||
import './ConfigPanel.css'
|
import './ConfigPanel.css'
|
||||||
|
|
||||||
|
|
@ -8,6 +9,43 @@ const ConfigPanel = () => {
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
const [message, setMessage] = useState('')
|
const [message, setMessage] = useState('')
|
||||||
|
|
||||||
|
// 预设方案配置
|
||||||
|
const presets = {
|
||||||
|
conservative: {
|
||||||
|
name: '保守配置',
|
||||||
|
desc: '适合新手,风险较低,交易频率适中',
|
||||||
|
configs: {
|
||||||
|
SCAN_INTERVAL: 3600,
|
||||||
|
MIN_CHANGE_PERCENT: 2.0,
|
||||||
|
MIN_SIGNAL_STRENGTH: 5,
|
||||||
|
TOP_N_SYMBOLS: 10,
|
||||||
|
MIN_VOLATILITY: 0.02
|
||||||
|
}
|
||||||
|
},
|
||||||
|
balanced: {
|
||||||
|
name: '平衡配置',
|
||||||
|
desc: '推荐使用,平衡频率和质量',
|
||||||
|
configs: {
|
||||||
|
SCAN_INTERVAL: 600,
|
||||||
|
MIN_CHANGE_PERCENT: 1.5,
|
||||||
|
MIN_SIGNAL_STRENGTH: 4,
|
||||||
|
TOP_N_SYMBOLS: 12,
|
||||||
|
MIN_VOLATILITY: 0.018
|
||||||
|
}
|
||||||
|
},
|
||||||
|
aggressive: {
|
||||||
|
name: '激进高频',
|
||||||
|
desc: '晚间波动大时使用,交易频率高',
|
||||||
|
configs: {
|
||||||
|
SCAN_INTERVAL: 300,
|
||||||
|
MIN_CHANGE_PERCENT: 1.0,
|
||||||
|
MIN_SIGNAL_STRENGTH: 3,
|
||||||
|
TOP_N_SYMBOLS: 20,
|
||||||
|
MIN_VOLATILITY: 0.015
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadConfigs()
|
loadConfigs()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -50,6 +88,40 @@ const ConfigPanel = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyPreset = async (presetKey) => {
|
||||||
|
const preset = presets[presetKey]
|
||||||
|
if (!preset) return
|
||||||
|
|
||||||
|
setSaving(true)
|
||||||
|
setMessage('')
|
||||||
|
try {
|
||||||
|
const configItems = Object.entries(preset.configs).map(([key, value]) => {
|
||||||
|
const config = configs[key]
|
||||||
|
if (!config) return null
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
value: key.includes('PERCENT') ? value / 100 : value,
|
||||||
|
type: config.type,
|
||||||
|
category: config.category,
|
||||||
|
description: config.description
|
||||||
|
}
|
||||||
|
}).filter(Boolean)
|
||||||
|
|
||||||
|
const response = await api.updateConfigsBatch(configItems)
|
||||||
|
setMessage(response.message || `已应用${preset.name}`)
|
||||||
|
if (response.note) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setMessage(response.note)
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
await loadConfigs()
|
||||||
|
} catch (error) {
|
||||||
|
setMessage('应用预设失败: ' + error.message)
|
||||||
|
} finally {
|
||||||
|
setSaving(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loading) return <div className="loading">加载中...</div>
|
if (loading) return <div className="loading">加载中...</div>
|
||||||
|
|
||||||
const configCategories = {
|
const configCategories = {
|
||||||
|
|
@ -63,10 +135,32 @@ const ConfigPanel = () => {
|
||||||
return (
|
return (
|
||||||
<div className="config-panel">
|
<div className="config-panel">
|
||||||
<div className="config-header">
|
<div className="config-header">
|
||||||
|
<div className="header-top">
|
||||||
<h2>交易配置</h2>
|
<h2>交易配置</h2>
|
||||||
|
<Link to="/config/guide" className="guide-link">📖 配置说明</Link>
|
||||||
|
</div>
|
||||||
<div className="config-info">
|
<div className="config-info">
|
||||||
<p>修改配置后,交易系统将在下次扫描时自动使用新配置</p>
|
<p>修改配置后,交易系统将在下次扫描时自动使用新配置</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 预设方案快速切换 */}
|
||||||
|
<div className="preset-section">
|
||||||
|
<h3>快速切换方案</h3>
|
||||||
|
<div className="preset-buttons">
|
||||||
|
{Object.entries(presets).map(([key, preset]) => (
|
||||||
|
<button
|
||||||
|
key={key}
|
||||||
|
className="preset-btn"
|
||||||
|
onClick={() => applyPreset(key)}
|
||||||
|
disabled={saving}
|
||||||
|
title={preset.desc}
|
||||||
|
>
|
||||||
|
<div className="preset-name">{preset.name}</div>
|
||||||
|
<div className="preset-desc">{preset.desc}</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{message && (
|
{message && (
|
||||||
<div className={`message ${message.includes('失败') || message.includes('错误') ? 'error' : 'success'}`}>
|
<div className={`message ${message.includes('失败') || message.includes('错误') ? 'error' : 'success'}`}>
|
||||||
|
|
@ -155,7 +249,12 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
<option value="true">是</option>
|
<option value="true">是</option>
|
||||||
<option value="false">否</option>
|
<option value="false">否</option>
|
||||||
</select>
|
</select>
|
||||||
{config.description && <span className="description">{config.description}</span>}
|
{config.description && (
|
||||||
|
<span className="description" title={getConfigDetail(label)}>
|
||||||
|
{config.description}
|
||||||
|
<span className="help-icon" title="悬停查看详细说明">ℹ️</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +277,12 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
<option key={opt} value={opt}>{opt}</option>
|
<option key={opt} value={opt}>{opt}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{config.description && <span className="description">{config.description}</span>}
|
{config.description && (
|
||||||
|
<span className="description" title={getConfigDetail(label)}>
|
||||||
|
{config.description}
|
||||||
|
<span className="help-icon" title="悬停查看详细说明">ℹ️</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -202,9 +306,33 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
className={isEditing ? 'editing' : ''}
|
className={isEditing ? 'editing' : ''}
|
||||||
/>
|
/>
|
||||||
{isEditing && <span className="edit-hint">按Enter保存</span>}
|
{isEditing && <span className="edit-hint">按Enter保存</span>}
|
||||||
{config.description && <span className="description">{config.description}</span>}
|
{config.description && (
|
||||||
|
<span className="description" title={getConfigDetail(label)}>
|
||||||
|
{config.description}
|
||||||
|
<span className="help-icon" title="悬停查看详细说明">ℹ️</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 配置项详细说明
|
||||||
|
const getConfigDetail = (key) => {
|
||||||
|
const details = {
|
||||||
|
'SCAN_INTERVAL': '扫描间隔(秒)。值越小扫描越频繁,能更快捕捉波动,但资源消耗增加。建议:300-3600秒',
|
||||||
|
'MIN_CHANGE_PERCENT': '最小涨跌幅阈值(%)。值越小捕捉机会越多,但可能包含噪音。建议:1.0%-3.0%',
|
||||||
|
'MIN_SIGNAL_STRENGTH': '最小信号强度(0-10)。值越小交易机会越多,但信号质量可能下降。建议:3-7',
|
||||||
|
'TOP_N_SYMBOLS': '每次处理的交易对数量。值越大机会越多,但计算量增加。建议:10-20',
|
||||||
|
'MIN_VOLATILITY': '最小波动率(小数)。值越小允许更多交易对。建议:0.015-0.025',
|
||||||
|
'MIN_VOLUME_24H': '最小24小时成交量(USDT)。过滤流动性差的交易对。建议:≥500万',
|
||||||
|
'PRIMARY_INTERVAL': '主周期。用于计算技术指标。周期越短反应越快。建议:15m-1h',
|
||||||
|
'ENTRY_INTERVAL': '入场周期。用于确定入场时机。建议:5m-15m',
|
||||||
|
'MAX_POSITION_PERCENT': '单笔最大仓位(账户余额%)。值越大单笔金额越大。建议:3%-10%',
|
||||||
|
'MAX_TOTAL_POSITION_PERCENT': '总仓位上限(账户余额%)。值越大可同时持有更多仓位。建议:20%-50%',
|
||||||
|
'STOP_LOSS_PERCENT': '止损百分比。值越小止损更严格。建议:2%-5%',
|
||||||
|
'TAKE_PROFIT_PERCENT': '止盈百分比。值越大目标利润更高。建议:3%-8%'
|
||||||
|
}
|
||||||
|
return details[key] || '暂无详细说明'
|
||||||
|
}
|
||||||
|
|
||||||
export default ConfigPanel
|
export default ConfigPanel
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user