Go-live gate: refuse unsafe live trading
Setting MODE=live alone no longer starts trading. check_live_ready requires an active risk limit, MAX_POSITION_PCT < 1.0, LIVE_CONFIRMATION exactly "I UNDERSTAND THE RISK", a minimum balance, and exchange credentials. Enforced in Runtime.start_many (both start paths) and surfaced at GET /api/live-check; the dashboard shows a banner and blocks the start buttons. Adds tests.
This commit is contained in:
1 parent
b60cddf13d
commit
0173e46cfb
10 files changed
+241
-1
No files matched your search
+31
-1
@@ -33,6 +33,7 @@ const strategies = ref([])
|
||||
const strategiesLoading = ref(true)
|
||||
const presets = ref([])
|
||||
const multiStatus = ref({ running: false, strategies: [], total_pnl: 0, equity: 0 })
|
||||
const liveCheck = ref({ ok: true, mode: 'paper', blockers: [] })
|
||||
const positions = ref([])
|
||||
const orders = ref([])
|
||||
const events = ref([])
|
||||
@@ -97,6 +98,13 @@ async function handleDeletePreset(id) {
|
||||
}
|
||||
|
||||
async function handleStart(payload) {
|
||||
if (liveCheck.value.mode === 'live' && !liveCheck.value.ok) {
|
||||
status.value = {
|
||||
...status.value,
|
||||
error: 'Live trading blocked: ' + liveCheck.value.blockers.join('; '),
|
||||
}
|
||||
return
|
||||
}
|
||||
busy.value = true
|
||||
try {
|
||||
status.value = await api.startBot(payload)
|
||||
@@ -112,7 +120,18 @@ async function refreshMulti() {
|
||||
multiStatus.value = await api.getMultiStatus()
|
||||
}
|
||||
|
||||
async function refreshLiveCheck() {
|
||||
liveCheck.value = await api.getLiveCheck()
|
||||
}
|
||||
|
||||
async function handleStartMany(payload) {
|
||||
if (liveCheck.value.mode === 'live' && !liveCheck.value.ok) {
|
||||
status.value = {
|
||||
...status.value,
|
||||
error: 'Live trading blocked: ' + liveCheck.value.blockers.join('; '),
|
||||
}
|
||||
return
|
||||
}
|
||||
busy.value = true
|
||||
try {
|
||||
multiStatus.value = await api.startMany(payload)
|
||||
@@ -249,7 +268,8 @@ onMounted(() => {
|
||||
statusTimer = setInterval(
|
||||
() => {
|
||||
api.getStatus().then((s) => (status.value = s)).catch(() => {})
|
||||
refreshMulti().catch(() => {})
|
||||
refreshMulti().catch(() => {})
|
||||
refreshLiveCheck().catch(() => {})
|
||||
},
|
||||
3000,
|
||||
)
|
||||
@@ -307,6 +327,16 @@ onUnmounted(() => {
|
||||
flattened. Restart the bot to resume (limits reset).
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="liveCheck.mode === 'live' && !liveCheck.ok"
|
||||
class="rounded-lg border border-amber-500 bg-amber-500/15 p-3 text-sm text-amber-100"
|
||||
>
|
||||
<p class="font-semibold">Live trading is blocked by the go-live checks:</p>
|
||||
<ul class="mt-1 list-disc pl-5">
|
||||
<li v-for="(b, i) in liveCheck.blockers" :key="i">{{ b }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="status.error" class="rounded-lg border border-rose-500/40 bg-rose-500/10 p-3 text-sm text-rose-200">
|
||||
{{ status.error }}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user