Add futures roll strategy with breakout monitoring and fixed-amount sizing.
Replace percent-based risk with system fixed amount, support market/breakout add modes only, allow pending submission outside trading hours, and fix short breakout geometry plus route registration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+90
-8
@@ -87,18 +87,71 @@
|
||||
function formatRoll(preview) {
|
||||
if (!preview) return '';
|
||||
var lines = [];
|
||||
if (preview.add_mode_label) lines.push('方式:' + preview.add_mode_label);
|
||||
if (preview.add_lots != null) lines.push('加仓手数:' + preview.add_lots);
|
||||
if (preview.qty_after != null) lines.push('合计手数:' + preview.qty_after);
|
||||
if (preview.avg_entry_after != null) lines.push('加仓后均价:' + preview.avg_entry_after);
|
||||
if (preview.new_stop_loss != null) lines.push('新止损:' + preview.new_stop_loss);
|
||||
if (preview.total_lots != null) lines.push('合计手数:' + preview.total_lots);
|
||||
if (preview.worst_loss != null) lines.push('最坏亏损:' + preview.worst_loss + ' 元');
|
||||
if (preview.trigger_price != null) lines.push('触发价:' + preview.trigger_price);
|
||||
if (preview.risk_budget != null) lines.push('风险预算(固定金额):' + preview.risk_budget + ' 元');
|
||||
if (preview.loss_at_sl != null) lines.push('打到止损亏损:' + preview.loss_at_sl + ' 元');
|
||||
if (preview.reward_at_tp != null) lines.push('止盈盈利:' + preview.reward_at_tp + ' 元');
|
||||
if (preview.margin_usage_pct != null) {
|
||||
lines.push('滚仓后保证金占用:' + preview.margin_usage_pct + '%');
|
||||
}
|
||||
if (preview.margin_cap_note) lines.push(preview.margin_cap_note);
|
||||
if (preview.message) lines.push(preview.message);
|
||||
if (preview.is_pending) lines.push('(提交后为程序监控,触价后自动市价加仓)');
|
||||
return lines.length ? lines.join('\n') : JSON.stringify(preview, null, 2);
|
||||
}
|
||||
|
||||
function syncRollModeUi() {
|
||||
var modeEl = document.getElementById('roll-add-mode');
|
||||
var breakEl = document.getElementById('roll-break-price');
|
||||
var execHint = document.getElementById('roll-exec-hint');
|
||||
var btnExec = document.getElementById('btn-roll-exec');
|
||||
if (!modeEl) return;
|
||||
var mode = modeEl.value || 'market';
|
||||
var isBreak = mode === 'breakout';
|
||||
if (breakEl) {
|
||||
breakEl.hidden = !isBreak;
|
||||
breakEl.required = isBreak;
|
||||
}
|
||||
if (execHint) execHint.hidden = false;
|
||||
if (btnExec) {
|
||||
btnExec.textContent = mode === 'market' ? '执行滚仓' : '提交监控';
|
||||
}
|
||||
}
|
||||
|
||||
function syncRollRiskHint() {
|
||||
/* 固定金额由服务端渲染在 #roll-risk-budget,切换监控单无需变更 */
|
||||
}
|
||||
|
||||
var rollCountdownTimer = null;
|
||||
|
||||
function startRollCountdown(btn, payload) {
|
||||
var sec = 10;
|
||||
btn.disabled = true;
|
||||
function tick() {
|
||||
btn.textContent = '确认执行 (' + sec + 's)';
|
||||
if (sec <= 0) {
|
||||
clearInterval(rollCountdownTimer);
|
||||
rollCountdownTimer = null;
|
||||
jsonPost('/api/strategy/roll/execute', payload).then(function (d) {
|
||||
if (!d.ok) { alert(d.error || d.message || '失败'); btn.disabled = false; btn.textContent = '执行滚仓'; return; }
|
||||
alert(d.message || '已提交');
|
||||
location.reload();
|
||||
}).catch(function () {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '执行滚仓';
|
||||
});
|
||||
return;
|
||||
}
|
||||
sec -= 1;
|
||||
}
|
||||
tick();
|
||||
rollCountdownTimer = setInterval(tick, 1000);
|
||||
}
|
||||
|
||||
var trendForm = document.getElementById('trend-form');
|
||||
var btnPreview = document.getElementById('btn-trend-preview');
|
||||
var btnExec = document.getElementById('btn-trend-exec');
|
||||
@@ -140,10 +193,20 @@
|
||||
var btnRollP = document.getElementById('btn-roll-preview');
|
||||
var btnRollE = document.getElementById('btn-roll-exec');
|
||||
var rollPrev = document.getElementById('roll-preview');
|
||||
var rollPayload = null;
|
||||
var rollMonitorSel = document.getElementById('roll-monitor-select');
|
||||
var rollModeSel = document.getElementById('roll-add-mode');
|
||||
|
||||
if (rollModeSel) rollModeSel.addEventListener('change', syncRollModeUi);
|
||||
if (rollMonitorSel) rollMonitorSel.addEventListener('change', syncRollRiskHint);
|
||||
syncRollModeUi();
|
||||
syncRollRiskHint();
|
||||
|
||||
if (btnRollP && rollForm) {
|
||||
btnRollP.addEventListener('click', function () {
|
||||
btnRollP.disabled = true;
|
||||
jsonPost('/api/strategy/roll/preview', formData(rollForm)).then(function (d) {
|
||||
rollPayload = formData(rollForm);
|
||||
jsonPost('/api/strategy/roll/preview', rollPayload).then(function (d) {
|
||||
if (!d.ok) {
|
||||
showPreview(rollPrev, d.error, false, false);
|
||||
btnRollE.hidden = true;
|
||||
@@ -158,18 +221,37 @@
|
||||
}
|
||||
if (btnRollE && rollForm) {
|
||||
btnRollE.addEventListener('click', function () {
|
||||
var payload = rollPayload || formData(rollForm);
|
||||
var mode = (payload.add_mode || 'market');
|
||||
if (mode === 'market') {
|
||||
if (!confirm('确认执行市价滚仓?')) return;
|
||||
startRollCountdown(btnRollE, payload);
|
||||
return;
|
||||
}
|
||||
btnRollE.disabled = true;
|
||||
btnRollE.textContent = '执行中…';
|
||||
jsonPost('/api/strategy/roll/execute', formData(rollForm)).then(function (d) {
|
||||
if (!d.ok) { alert(d.error); return; }
|
||||
btnRollE.textContent = '提交中…';
|
||||
jsonPost('/api/strategy/roll/execute', payload).then(function (d) {
|
||||
if (!d.ok) { alert(d.error || '失败'); return; }
|
||||
alert(d.message || '已提交监控');
|
||||
location.reload();
|
||||
}).finally(function () {
|
||||
btnRollE.disabled = false;
|
||||
btnRollE.textContent = '执行滚仓';
|
||||
syncRollModeUi();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('.roll-cancel-leg').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var legId = btn.getAttribute('data-leg-id');
|
||||
if (!legId || !confirm('删除该监控腿?')) return;
|
||||
jsonPost('/api/strategy/roll/cancel/' + legId, {}).then(function (d) {
|
||||
if (!d.ok) { alert(d.error); return; }
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var btnStop = document.getElementById('btn-trend-stop');
|
||||
if (btnStop) {
|
||||
btnStop.addEventListener('click', function () {
|
||||
|
||||
Reference in New Issue
Block a user