RayMelius Claude Sonnet 4.6 commited on
Commit
e2e5ea7
Β·
1 Parent(s): d185c3e

Fix frontend disconnected, FIX UI reloader, add dashboard clock

Browse files

Frontend (disconnected root cause):
- All fetch('/endpoint') calls were hitting nginx '/' (dashboard) instead
of '/frontend/endpoint'. Added BASE detection at page load:
const BASE = window.location.pathname === '/' ? '' : pathname.replace(/\/$/, '');
Applied to /securities, /order, /book, /trades

FIX UI (session disrupted by reloader):
- debug=True caused Flask reloader to spawn a child process and restart
the app, breaking the QuickFIX session on startup
- Changed to debug=False, use_reloader=False

Entrypoint timing:
- FIX OEG sleep increased from 3s β†’ 6s (allow full socket bind)
- FIX UI sleep increased from 2s β†’ 3s (allow QuickFIX initiator to start)

Dashboard:
- Local clock added to header bar (updates every second, browser timezone)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

dashboard/templates/index.html CHANGED
@@ -201,6 +201,7 @@
201
  Trading Dashboard
202
  <span id="status" class="status connecting"><span class="dot"></span><span id="status-text">Connecting...</span></span>
203
  <span id="session-badge" class="status idle"><span class="dot"></span><span id="session-text">IDLE</span></span>
 
204
  <button id="day-btn" onclick="toggleDay()" class="btn-day btn-start" disabled>Start of Day</button>
205
  <button id="suspend-btn" onclick="toggleSuspend()" class="btn-day btn-suspend" disabled>Suspend</button>
206
  <button id="mode-btn" onclick="toggleMode()" class="btn-day btn-automatic">Automatic</button>
@@ -1383,6 +1384,16 @@
1383
  }
1384
 
1385
  // Initial load: fetch data via REST, then connect SSE
 
 
 
 
 
 
 
 
 
 
1386
  async function init() {
1387
  await fetchData();
1388
  connectSSE();
 
201
  Trading Dashboard
202
  <span id="status" class="status connecting"><span class="dot"></span><span id="status-text">Connecting...</span></span>
203
  <span id="session-badge" class="status idle"><span class="dot"></span><span id="session-text">IDLE</span></span>
204
+ <span id="local-clock" style="font-size:13px; font-weight:normal; color:#555; background:#f0f0f0; padding:3px 10px; border-radius:12px; font-variant-numeric:tabular-nums;"></span>
205
  <button id="day-btn" onclick="toggleDay()" class="btn-day btn-start" disabled>Start of Day</button>
206
  <button id="suspend-btn" onclick="toggleSuspend()" class="btn-day btn-suspend" disabled>Suspend</button>
207
  <button id="mode-btn" onclick="toggleMode()" class="btn-day btn-automatic">Automatic</button>
 
1384
  }
1385
 
1386
  // Initial load: fetch data via REST, then connect SSE
1387
+ // ── Local clock ───────────────────────────────────────────────────────────
1388
+ (function tickClock() {
1389
+ const el = document.getElementById("local-clock");
1390
+ if (el) {
1391
+ const now = new Date();
1392
+ el.textContent = now.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit", second: "2-digit"});
1393
+ }
1394
+ setTimeout(tickClock, 1000);
1395
+ })();
1396
+
1397
  async function init() {
1398
  await fetchData();
1399
  connectSSE();
entrypoint.sh CHANGED
@@ -59,11 +59,11 @@ python3 /app/mdf_simulator.py &
59
 
60
  echo "[startup] Starting FIX OEG on port 5001..."
61
  (cd /app/fix_oeg && python3 /app/fix_oeg/fix_oeg_server.py) &
62
- sleep 3
63
 
64
  echo "[startup] Starting FIX UI Client on port 5002..."
65
  python3 /app/fix_ui/fix_ui_client.py &
66
- sleep 2
67
 
68
  echo "[startup] Starting Frontend on port 5003..."
69
  PORT=$FRONTEND_PORT TEMPLATE_FOLDER=/app/frontend_templates python3 /app/frontend.py &
 
59
 
60
  echo "[startup] Starting FIX OEG on port 5001..."
61
  (cd /app/fix_oeg && python3 /app/fix_oeg/fix_oeg_server.py) &
62
+ sleep 6
63
 
64
  echo "[startup] Starting FIX UI Client on port 5002..."
65
  python3 /app/fix_ui/fix_ui_client.py &
66
+ sleep 3
67
 
68
  echo "[startup] Starting Frontend on port 5003..."
69
  PORT=$FRONTEND_PORT TEMPLATE_FOLDER=/app/frontend_templates python3 /app/frontend.py &
fix-ui-client/fix-ui-client.py CHANGED
@@ -197,4 +197,4 @@ CONFIG_FILE = os.getenv("FIX_CONFIG", "client.cfg")
197
  PORT = int(os.getenv("UI_PORT", "5002"))
198
 
199
  if __name__ == "__main__":
200
- app.run(host="0.0.0.0", port=PORT, debug=True)
 
197
  PORT = int(os.getenv("UI_PORT", "5002"))
198
 
199
  if __name__ == "__main__":
200
+ app.run(host="0.0.0.0", port=PORT, debug=False, use_reloader=False)
frontend/templates/index.html CHANGED
@@ -211,6 +211,9 @@
211
  </div>
212
 
213
  <script>
 
 
 
214
  let pollInterval = null;
215
 
216
  function setStatus(cls, text) {
@@ -223,7 +226,7 @@
223
 
224
  async function loadSecurities() {
225
  try {
226
- const r = await fetch('/securities');
227
  const symbols = await r.json();
228
  const sel = document.getElementById('symbol-select');
229
  sel.innerHTML = '';
@@ -251,7 +254,7 @@
251
  timestamp: Date.now() / 1000
252
  };
253
  try {
254
- const r = await fetch('/order', {
255
  method: 'POST',
256
  headers: {'Content-Type': 'application/json'},
257
  body: JSON.stringify(data)
@@ -324,7 +327,7 @@
324
  async function loadBook() {
325
  const now = new Date().toLocaleTimeString();
326
  try {
327
- const r = await fetch('/book');
328
  const b = await r.json();
329
  renderBook(b);
330
  document.getElementById('book-updated').textContent = 'Updated: ' + now;
@@ -335,7 +338,7 @@
335
  setStatus('disconnected', 'Disconnected');
336
  }
337
  try {
338
- const r2 = await fetch('/trades');
339
  const t = await r2.json();
340
  renderTrades(Array.isArray(t) ? t : (t.trades || []));
341
  document.getElementById('trades-updated').textContent = 'Updated: ' + now;
 
211
  </div>
212
 
213
  <script>
214
+ // Detect API base path: works whether served at / or under /frontend/
215
+ const BASE = window.location.pathname === '/' ? '' : window.location.pathname.replace(/\/$/, '');
216
+
217
  let pollInterval = null;
218
 
219
  function setStatus(cls, text) {
 
226
 
227
  async function loadSecurities() {
228
  try {
229
+ const r = await fetch(BASE + '/securities');
230
  const symbols = await r.json();
231
  const sel = document.getElementById('symbol-select');
232
  sel.innerHTML = '';
 
254
  timestamp: Date.now() / 1000
255
  };
256
  try {
257
+ const r = await fetch(BASE + '/order', {
258
  method: 'POST',
259
  headers: {'Content-Type': 'application/json'},
260
  body: JSON.stringify(data)
 
327
  async function loadBook() {
328
  const now = new Date().toLocaleTimeString();
329
  try {
330
+ const r = await fetch(BASE + '/book');
331
  const b = await r.json();
332
  renderBook(b);
333
  document.getElementById('book-updated').textContent = 'Updated: ' + now;
 
338
  setStatus('disconnected', 'Disconnected');
339
  }
340
  try {
341
+ const r2 = await fetch(BASE + '/trades');
342
  const t = await r2.json();
343
  renderTrades(Array.isArray(t) ? t : (t.trades || []));
344
  document.getElementById('trades-updated').textContent = 'Updated: ' + now;