feat: 所有参数移至config.json, 重构退出逻辑, 清理遗留接口
- 将所有硬编码参数迁移到 config.json (手续费率、止盈/止损阈值、 超时、腿间隔、加仓步进等) - 退出条件: 净利 >= take_profit_pct 止盈, 价差 <= 0 平仓 - 删除 Binance/dYdX 遗留代码 - 更新 README 文档 - Dashboard: 双交易所价格表、黑名单UI、按币名排序持仓 - Bitget WS: 文本ping保活 - 数据库: 重置, 无历史仓位
This commit is contained in:
+32
-10
@@ -38,8 +38,8 @@ function updateClock() {
|
||||
setInterval(updateClock, 1000);
|
||||
updateClock();
|
||||
|
||||
const EXCHANGES = ['Binance', 'HyperLiquid', 'Bitget', 'dYdX'];
|
||||
const COINS = ['DOGE', 'LINK', 'ONDO', 'OP', 'WIF', 'ARB'];
|
||||
const EXCHANGES = ['HyperLiquid', 'Bitget'];
|
||||
const COINS = []; // populated dynamically from SSE data
|
||||
|
||||
function formatPrice(p) {
|
||||
if (p == null || p <= 0) return '-';
|
||||
@@ -97,6 +97,13 @@ const eventHandlers = {};
|
||||
eventHandlers.prices = (prices) => {
|
||||
if (!prices || prices.length === 0) return;
|
||||
|
||||
// Dynamically populate COINS list on first data
|
||||
if (COINS.length === 0) {
|
||||
for (const row of prices) {
|
||||
COINS.push(row.coin);
|
||||
}
|
||||
}
|
||||
|
||||
let html = '';
|
||||
let coinsOnline = 0;
|
||||
|
||||
@@ -110,7 +117,6 @@ eventHandlers.prices = (prices) => {
|
||||
|
||||
const cells = EXCHANGES.map(ex => {
|
||||
const p = row[ex];
|
||||
const sp = row[ex + '_spread'];
|
||||
const key = coin + '.' + ex;
|
||||
const prev = priceCache[key];
|
||||
const curP = p || 0;
|
||||
@@ -131,9 +137,6 @@ eventHandlers.prices = (prices) => {
|
||||
}
|
||||
|
||||
let display = formatPrice(p);
|
||||
if (sp && sp > 0.01) {
|
||||
display += `<span class="text-dim" style="font-size:10px"> (${sp.toFixed(3)}%)</span>`;
|
||||
}
|
||||
return `<td class="${cls}">${display}</td>`;
|
||||
});
|
||||
|
||||
@@ -171,14 +174,17 @@ eventHandlers.arb = (opps) => {
|
||||
els.arbBody.innerHTML = html;
|
||||
};
|
||||
|
||||
// P3-3: Positions with live PnL
|
||||
// P3-3: Positions with live PnL — sorted by time (oldest first)
|
||||
eventHandlers.positions = (positions) => {
|
||||
if (!positions || positions.length === 0) {
|
||||
els.posBody.innerHTML = '<tr><td colspan="8" class="text-dim">无持仓</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
const html = positions.map(p => {
|
||||
// Sort by coin name (stable, deterministic)
|
||||
const sorted = [...positions].sort((a, b) => a.coin.localeCompare(b.coin));
|
||||
|
||||
const html = sorted.map(p => {
|
||||
const pnl = p.pnl_est;
|
||||
const pnlStr = pnl != null ? pnl.toFixed(4) + '%' : '-';
|
||||
const curSpread = p.current_spread != null ? p.current_spread.toFixed(4) + '%' : '-';
|
||||
@@ -209,8 +215,12 @@ eventHandlers.stats = (stats) => {
|
||||
// Detailed PnL stats
|
||||
if (stats.detail) {
|
||||
const d = stats.detail;
|
||||
$('stat-total-pnl').textContent = (d.total_pnl != null) ? d.total_pnl.toFixed(2) + '%' : '—';
|
||||
$('stat-avg-pnl').textContent = (d.avg_pnl != null) ? d.avg_pnl.toFixed(2) + '%' : '—';
|
||||
// Total PnL: show both USD and percentage of capital
|
||||
const usdStr = (d.total_pnl_usd != null) ? '$' + d.total_pnl_usd.toFixed(2) : '—';
|
||||
const pctStr = (d.capital_pnl != null) ? d.capital_pnl.toFixed(4) + '%' : '—';
|
||||
$('stat-total-pnl').textContent = usdStr + ' (' + pctStr + ')';
|
||||
$('stat-total-pnl').className = pnlClass(d.capital_pnl);
|
||||
$('stat-capital').textContent = (stats.capital != null) ? '$' + stats.capital.toFixed(0) : '—';
|
||||
$('stat-win-rate').textContent = (d.win_rate != null) ? d.win_rate.toFixed(1) + '%' : '—';
|
||||
$('stat-max-profit').textContent = (d.max_profit != null) ? '+' + d.max_profit.toFixed(2) + '%' : '—';
|
||||
$('stat-max-loss').textContent = (d.max_loss != null) ? d.max_loss.toFixed(2) + '%' : '—';
|
||||
@@ -225,6 +235,18 @@ eventHandlers.stats = (stats) => {
|
||||
}).join(' ');
|
||||
els.connDetail.innerHTML = dots;
|
||||
}
|
||||
|
||||
// Blacklist — stale spread coins
|
||||
if (stats.blacklist && stats.blacklist.length > 0) {
|
||||
const html = stats.blacklist.map(b => {
|
||||
const minLeft = Math.floor(b.remaining_sec / 60);
|
||||
const secLeft = b.remaining_sec % 60;
|
||||
return `<span style="color:#f85149;margin-right:12px;font-size:13px">⛔ ${b.coin} (${b.since} 剩余 ${minLeft}:${secLeft.toString().padStart(2,'0')})</span>`;
|
||||
}).join('');
|
||||
$('bl-body').innerHTML = html;
|
||||
} else {
|
||||
$('bl-body').innerHTML = '<span class="text-dim">暂无</span>';
|
||||
}
|
||||
};
|
||||
|
||||
// P3-4: Real-time trade events
|
||||
|
||||
+12
-4
@@ -27,14 +27,14 @@
|
||||
<div class="stat"><label>收敛</label><span id="stat-converged" class="pct-green">0</span></div>
|
||||
<div class="stat"><label>发散</label><span id="stat-diverged" class="pct-red">0</span></div>
|
||||
<div class="stat"><label>持平</label><span id="stat-flat" class="pct-gray">0</span></div>
|
||||
<div class="stat"><label>持仓</label><span id="stat-positions" class="pct-yellow">0</span></div>
|
||||
<div class="stat"><label>持仓</label><span id="stat-positions" class="pct-yellow">0 / <span id="stat-max-pos">5</span></span></div>
|
||||
<div class="stat"><label>币种</label><span id="stat-coins" class="pct-blue">0</span></div>
|
||||
<div class="stat" id="conn-stats"><label>连接</label><span id="conn-detail"></span></div>
|
||||
</div>
|
||||
<!-- Detailed PnL stats -->
|
||||
<div class="stats-row detail-stats" style="margin-top:4px;font-size:12px;opacity:0.85">
|
||||
<div class="stat"><label>总PnL</label><span id="stat-total-pnl">—</span></div>
|
||||
<div class="stat"><label>平均PnL</label><span id="stat-avg-pnl">—</span></div>
|
||||
<div class="stat"><label>本金</label><span id="stat-capital">—</span></div>
|
||||
<div class="stat"><label>胜率</label><span id="stat-win-rate">—</span></div>
|
||||
<div class="stat"><label>最多盈利</label><span id="stat-max-profit">—</span></div>
|
||||
<div class="stat"><label>最多亏损</label><span id="stat-max-loss">—</span></div>
|
||||
@@ -42,16 +42,24 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Blacklist -->
|
||||
<section class="card" id="bl-card">
|
||||
<h2>⛔ 黑名单</h2>
|
||||
<div class="stats-row" id="bl-body">
|
||||
<span class="text-dim">暂无</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Price Table -->
|
||||
<section class="card" id="prices-card">
|
||||
<h2>💰 实时价格 <span id="prices-age" class="text-dim" style="font-size:11px"></span></h2>
|
||||
<div class="table-wrap">
|
||||
<table id="price-table">
|
||||
<thead>
|
||||
<tr><th>币种</th><th>Binance</th><th>HyperLiquid</th><th>Bitget</th><th>dYdX</th><th>BG↔HL价差</th></tr>
|
||||
<tr><th>币种</th><th>HyperLiquid</th><th>Bitget</th><th>BG↔HL价差</th></tr>
|
||||
</thead>
|
||||
<tbody id="price-body">
|
||||
<tr><td colspan="6" class="loading">等待数据...</td></tr>
|
||||
<tr><td colspan="4" class="loading">等待数据...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user