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
|
||||
|
||||
Reference in New Issue
Block a user