Alvin3y1 commited on
Commit
6eaed7c
·
verified ·
1 Parent(s): 8b9c571

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -76
app.py CHANGED
@@ -150,25 +150,25 @@ def process_market_data():
150
  "walls": {"bids": bid_walls, "asks": ask_walls}
151
  }
152
 
153
- # --- FRONTEND (MINIMALIST UI) ---
154
  HTML_PAGE = f"""
155
  <!DOCTYPE html>
156
  <html lang="en">
157
  <head>
158
  <meta charset="UTF-8">
159
- <title>Terminal | {SYMBOL_KRAKEN}</title>
160
  <script src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js"></script>
161
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
162
  <style>
163
  :root {{
164
- --bg-base: #050505;
165
  --bg-panel: #0a0a0a;
166
- --border: #1a1a1a;
167
- --text-main: #e0e0e0;
168
- --text-dim: #555555;
169
- --green: #2ebd85;
170
- --red: #f6465d;
171
- --blue: #3772ff;
172
  }}
173
  body {{
174
  margin: 0; padding: 0;
@@ -182,15 +182,15 @@ HTML_PAGE = f"""
182
  /* THE GRID */
183
  .layout {{
184
  display: grid;
185
- grid-template-rows: 28px 1fr 1fr; /* Slim header */
186
  grid-template-columns: 3fr 1fr;
187
- gap: 1px; /* The 'border' is just the gap showing the background */
188
- background-color: var(--border); /* Acts as border color */
189
  height: 100vh;
190
  box-sizing: border-box;
191
  }}
192
 
193
- .panel {{ background: var(--bg-panel); position: relative; display: flex; flex-direction: column; overflow: hidden; }}
194
 
195
  /* STATUS BAR HEADER */
196
  .status-bar {{
@@ -200,15 +200,17 @@ HTML_PAGE = f"""
200
  display: flex;
201
  align-items: center;
202
  justify-content: space-between;
203
- padding: 0 15px;
204
  font-family: 'JetBrains Mono', monospace;
205
- font-size: 11px;
206
  text-transform: uppercase;
 
 
207
  }}
208
- .status-left {{ display: flex; gap: 15px; align-items: center; }}
209
- .status-right {{ color: var(--text-dim); }}
210
- .live-dot {{ width: 6px; height: 6px; background-color: var(--green); border-radius: 50%; display: inline-block; box-shadow: 0 0 5px var(--green); }}
211
- .symbol-tag {{ color: var(--text-dim); font-weight: 600; color: #fff; }}
212
 
213
  /* MAIN CHART AREA */
214
  #p-chart {{ grid-column: 1 / 2; grid-row: 2 / 3; }}
@@ -221,7 +223,7 @@ HTML_PAGE = f"""
221
  gap: 1px;
222
  background: var(--border);
223
  }}
224
- .depth-sub {{ background: var(--bg-panel); display: flex; flex-direction: column; }}
225
 
226
  /* SIDEBAR */
227
  #p-sidebar {{
@@ -230,15 +232,31 @@ HTML_PAGE = f"""
230
  padding: 20px;
231
  display: flex;
232
  flex-direction: column;
233
- gap: 30px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }}
235
 
236
  /* COMPONENT STYLES */
237
  .data-group {{ display: flex; flex-direction: column; gap: 4px; }}
238
- .label {{ font-size: 10px; color: var(--text-dim); font-weight: 500; letter-spacing: 0.5px; text-transform: uppercase; }}
239
- .value {{ font-family: 'JetBrains Mono', monospace; font-size: 20px; font-weight: 400; color: #fff; }}
240
- .value-lg {{ font-size: 28px; font-weight: 500; }}
241
- .value-sub {{ font-family: 'JetBrains Mono', monospace; font-size: 11px; margin-top: 2px; }}
242
 
243
  .divider {{ height: 1px; background: var(--border); width: 100%; }}
244
 
@@ -248,23 +266,17 @@ HTML_PAGE = f"""
248
  .c-dim {{ color: var(--text-dim); }}
249
 
250
  /* LISTS */
251
- .list-container {{ display: flex; flex-direction: column; gap: 8px; }}
252
  .list-item {{
253
  display: flex; justify-content: space-between;
254
  font-family: 'JetBrains Mono', monospace;
255
  font-size: 11px;
256
- color: #888;
257
- }}
258
- .list-item span:first-child {{ color: #fff; }}
259
-
260
- /* CHART OVERLAYS */
261
- .chart-label {{
262
- position: absolute; top: 10px; left: 15px;
263
- z-index: 10;
264
- font-size: 10px;
265
- color: var(--text-dim);
266
- font-weight: 600;
267
  }}
 
 
 
268
  </style>
269
  </head>
270
  <body>
@@ -274,27 +286,27 @@ HTML_PAGE = f"""
274
  <div class="status-bar">
275
  <div class="status-left">
276
  <span class="live-dot"></span>
277
- <span class="symbol-tag">{SYMBOL_KRAKEN}</span>
278
- <span id="price-ticker" style="color: #fff;">---</span>
279
  </div>
280
- <div class="status-right" id="clock">--:--:-- UTC</div>
281
  </div>
282
 
283
  <!-- PRICE CHART -->
284
  <div id="p-chart" class="panel">
285
- <div class="chart-label">PRICE ACTION // ANOMALIES</div>
286
- <div id="tv-price" style="flex: 1;"></div>
287
  </div>
288
 
289
  <!-- DEPTH CHARTS -->
290
  <div id="p-depth">
291
  <div class="depth-sub">
292
- <div class="chart-label">LIQUIDITY DENSITY</div>
293
- <div id="tv-raw" style="flex: 1;"></div>
294
  </div>
295
  <div class="depth-sub">
296
- <div class="chart-label">NET IMBALANCE</div>
297
- <div id="tv-net" style="flex: 1;"></div>
298
  </div>
299
  </div>
300
 
@@ -304,9 +316,9 @@ HTML_PAGE = f"""
304
  <!-- 1. PREDICTED IMPACT -->
305
  <div class="data-group">
306
  <span class="label">Projected Impact (5s)</span>
307
- <div style="display:flex; align-items: baseline; gap: 8px;">
308
  <span id="proj-pct" class="value value-lg">--%</span>
309
- <span id="proj-val" class="value-sub c-dim">---</span>
310
  </div>
311
  </div>
312
 
@@ -314,7 +326,7 @@ HTML_PAGE = f"""
314
 
315
  <!-- 2. IMBALANCE SCORE -->
316
  <div class="data-group">
317
- <span class="label">Order Flow Imbalance</span>
318
  <span id="score-val" class="value">0.00</span>
319
  </div>
320
 
@@ -322,15 +334,15 @@ HTML_PAGE = f"""
322
 
323
  <!-- 3. WALLS -->
324
  <div class="data-group" style="flex: 1;">
325
- <span class="label" style="margin-bottom: 10px;">Significant Structures (Z > 3.0)</span>
326
  <div id="wall-list" class="list-container">
327
- <span class="c-dim" style="font-size: 11px;">Scanning...</span>
328
  </div>
329
  </div>
330
 
331
  <div style="margin-top: auto;">
332
  <span class="label">System Latency</span>
333
- <div class="value-sub c-green">12ms</div>
334
  </div>
335
  </div>
336
  </div>
@@ -351,45 +363,55 @@ HTML_PAGE = f"""
351
  wallList: document.getElementById('wall-list')
352
  }};
353
 
354
- // MINIMALIST CHART CONFIG
355
  const chartOpts = {{
356
- layout: {{ background: {{ type: 'solid', color: '#0a0a0a' }}, textColor: '#444', fontFamily: 'JetBrains Mono' }},
357
- grid: {{ vertLines: {{ color: '#111' }}, horzLines: {{ color: '#111' }} }},
358
- rightPriceScale: {{ borderColor: '#111', scaleMargins: {{ top: 0.1, bottom: 0.1 }} }},
359
- timeScale: {{ borderColor: '#111', timeVisible: true, secondsVisible: true }},
360
- crosshair: {{ mode: 1, vertLine: {{ color: '#333', labelBackgroundColor: '#333' }}, horzLine: {{ color: '#333', labelBackgroundColor: '#333' }} }}
361
  }};
362
 
363
  // 1. PRICE
364
  const priceChart = LightweightCharts.createChart(document.getElementById('tv-price'), chartOpts);
365
- const priceSeries = priceChart.addLineSeries({{ color: '#e0e0e0', lineWidth: 1, title: 'Price' }});
366
- const predSeries = priceChart.addLineSeries({{ color: '#3772ff', lineWidth: 1, lineStyle: 2, title: 'Forecast' }});
367
 
368
  // 2. RAW
369
  const rawChart = LightweightCharts.createChart(document.getElementById('tv-raw'), {{
370
  ...chartOpts,
371
  localization: {{ timeFormatter: t => '$' + t.toFixed(2) }}
372
  }});
373
- const bidSeries = rawChart.addAreaSeries({{ lineColor: '#2ebd85', topColor: 'rgba(46, 189, 133, 0.1)', bottomColor: 'rgba(0,0,0,0)', lineWidth: 1 }});
374
- const askSeries = rawChart.addAreaSeries({{ lineColor: '#f6465d', topColor: 'rgba(246, 70, 93, 0.1)', bottomColor: 'rgba(0,0,0,0)', lineWidth: 1 }});
375
 
376
  // 3. NET
377
  const netChart = LightweightCharts.createChart(document.getElementById('tv-net'), {{
378
  ...chartOpts,
379
  localization: {{ timeFormatter: t => '$' + t.toFixed(2) }}
380
  }});
381
- const netSeries = netChart.addHistogramSeries({{ color: '#3772ff' }});
382
 
383
  let activeLines = [];
384
 
385
- // RESIZE
386
- new ResizeObserver(e => {{
387
- e.forEach(x => {{
388
- if(x.target.id === 'tv-price') priceChart.applyOptions({{ width: x.contentRect.width, height: x.contentRect.height }});
389
- if(x.target.id === 'tv-raw') rawChart.applyOptions({{ width: x.contentRect.width, height: x.contentRect.height }});
390
- if(x.target.id === 'tv-net') netChart.applyOptions({{ width: x.contentRect.width, height: x.contentRect.height }});
391
- }});
392
- }}).observe(document.body);
 
 
 
 
 
 
 
 
 
 
393
 
394
  function connect() {{
395
  const ws = new WebSocket((location.protocol === 'https:' ? 'wss' : 'ws') + '://' + location.host + '/ws');
@@ -438,18 +460,18 @@ HTML_PAGE = f"""
438
  let html = "";
439
 
440
  const addWall = (w, type) => {{
441
- const color = type === 'BID' ? '#2ebd85' : '#f6465d';
442
  activeLines.push(priceSeries.createPriceLine({{ price: w.price, color: color, lineWidth: 1, lineStyle: 2, axisLabelVisible: false }}));
443
  html += `<div class="list-item">
444
  <span style="color:${{color}}">${{type}} ${{w.price}}</span>
445
- <span>Z:${{w.z_score.toFixed(1)}}</span>
446
  </div>`;
447
  }};
448
 
449
  data.walls.asks.forEach(w => addWall(w, 'ASK'));
450
  data.walls.bids.forEach(w => addWall(w, 'BID'));
451
 
452
- dom.wallList.innerHTML = html || '<span class="c-dim" style="font-size:11px">No anomalies.</span>';
453
  }}
454
 
455
  // DEPTH
@@ -459,7 +481,7 @@ HTML_PAGE = f"""
459
  const t = data.depth_x[i];
460
  bids.push({{ time: t, value: data.depth_bids[i] }});
461
  asks.push({{ time: t, value: data.depth_asks[i] }});
462
- nets.push({{ time: t, value: data.depth_net[i], color: data.depth_net[i] > 0 ? '#2ebd85' : '#f6465d' }});
463
  }}
464
  bidSeries.setData(bids);
465
  askSeries.setData(asks);
 
150
  "walls": {"bids": bid_walls, "asks": ask_walls}
151
  }
152
 
153
+ # --- FRONTEND (HIGH CONTRAST, NO OVERLAP) ---
154
  HTML_PAGE = f"""
155
  <!DOCTYPE html>
156
  <html lang="en">
157
  <head>
158
  <meta charset="UTF-8">
159
+ <title>{SYMBOL_KRAKEN}</title>
160
  <script src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js"></script>
161
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@500;600&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
162
  <style>
163
  :root {{
164
+ --bg-base: #000000;
165
  --bg-panel: #0a0a0a;
166
+ --border: #252525;
167
+ --text-main: #FFFFFF;
168
+ --text-dim: #999999;
169
+ --green: #00ff9d;
170
+ --red: #ff3b3b;
171
+ --blue: #2979ff;
172
  }}
173
  body {{
174
  margin: 0; padding: 0;
 
182
  /* THE GRID */
183
  .layout {{
184
  display: grid;
185
+ grid-template-rows: 34px 1fr 1fr; /* Explicit Header Height */
186
  grid-template-columns: 3fr 1fr;
187
+ gap: 1px;
188
+ background-color: var(--border);
189
  height: 100vh;
190
  box-sizing: border-box;
191
  }}
192
 
193
+ .panel {{ background: var(--bg-panel); display: flex; flex-direction: column; overflow: hidden; }}
194
 
195
  /* STATUS BAR HEADER */
196
  .status-bar {{
 
200
  display: flex;
201
  align-items: center;
202
  justify-content: space-between;
203
+ padding: 0 12px;
204
  font-family: 'JetBrains Mono', monospace;
205
+ font-size: 12px;
206
  text-transform: uppercase;
207
+ border-bottom: 1px solid var(--border);
208
+ z-index: 50;
209
  }}
210
+ .status-left {{ display: flex; gap: 20px; align-items: center; }}
211
+ .status-right {{ color: var(--text-dim); letter-spacing: 1px; }}
212
+ .live-dot {{ width: 8px; height: 8px; background-color: var(--green); border-radius: 50%; display: inline-block; box-shadow: 0 0 8px var(--green); }}
213
+ .ticker-val {{ font-weight: 700; color: #fff; font-size: 13px; }}
214
 
215
  /* MAIN CHART AREA */
216
  #p-chart {{ grid-column: 1 / 2; grid-row: 2 / 3; }}
 
223
  gap: 1px;
224
  background: var(--border);
225
  }}
226
+ .depth-sub {{ background: var(--bg-panel); display: flex; flex-direction: column; position: relative; }}
227
 
228
  /* SIDEBAR */
229
  #p-sidebar {{
 
232
  padding: 20px;
233
  display: flex;
234
  flex-direction: column;
235
+ gap: 25px;
236
+ border-left: 1px solid var(--border);
237
+ }}
238
+
239
+ /* CHART INTERNAL HEADER (PREVENTS OVERLAP) */
240
+ .chart-header {{
241
+ height: 24px;
242
+ min-height: 24px;
243
+ display: flex;
244
+ align-items: center;
245
+ padding-left: 12px;
246
+ font-size: 10px;
247
+ font-weight: 700;
248
+ color: var(--text-dim);
249
+ background: #050505; /* Slightly darker to separate */
250
+ border-bottom: 1px solid #151515;
251
+ letter-spacing: 0.5px;
252
  }}
253
 
254
  /* COMPONENT STYLES */
255
  .data-group {{ display: flex; flex-direction: column; gap: 4px; }}
256
+ .label {{ font-size: 10px; color: var(--text-dim); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }}
257
+ .value {{ font-family: 'JetBrains Mono', monospace; font-size: 20px; font-weight: 700; color: #fff; }}
258
+ .value-lg {{ font-size: 26px; }}
259
+ .value-sub {{ font-family: 'JetBrains Mono', monospace; font-size: 11px; margin-top: 2px; color: #666; }}
260
 
261
  .divider {{ height: 1px; background: var(--border); width: 100%; }}
262
 
 
266
  .c-dim {{ color: var(--text-dim); }}
267
 
268
  /* LISTS */
269
+ .list-container {{ display: flex; flex-direction: column; gap: 8px; overflow-y: hidden; }}
270
  .list-item {{
271
  display: flex; justify-content: space-between;
272
  font-family: 'JetBrains Mono', monospace;
273
  font-size: 11px;
274
+ border-bottom: 1px solid #151515;
275
+ padding-bottom: 4px;
 
 
 
 
 
 
 
 
 
276
  }}
277
+ .list-item span:first-child {{ color: #e0e0e0; }}
278
+ .list-item:last-child {{ border: none; }}
279
+
280
  </style>
281
  </head>
282
  <body>
 
286
  <div class="status-bar">
287
  <div class="status-left">
288
  <span class="live-dot"></span>
289
+ <span style="font-weight:700; color:#fff;">{SYMBOL_KRAKEN}</span>
290
+ <span id="price-ticker" class="ticker-val">---</span>
291
  </div>
292
+ <div class="status-right" id="clock">00:00:00 UTC</div>
293
  </div>
294
 
295
  <!-- PRICE CHART -->
296
  <div id="p-chart" class="panel">
297
+ <div class="chart-header">PRICE ACTION // ANOMALIES</div>
298
+ <div id="tv-price" style="flex: 1; width: 100%;"></div>
299
  </div>
300
 
301
  <!-- DEPTH CHARTS -->
302
  <div id="p-depth">
303
  <div class="depth-sub">
304
+ <div class="chart-header">LIQUIDITY DENSITY</div>
305
+ <div id="tv-raw" style="flex: 1; width: 100%;"></div>
306
  </div>
307
  <div class="depth-sub">
308
+ <div class="chart-header">NET IMBALANCE</div>
309
+ <div id="tv-net" style="flex: 1; width: 100%;"></div>
310
  </div>
311
  </div>
312
 
 
316
  <!-- 1. PREDICTED IMPACT -->
317
  <div class="data-group">
318
  <span class="label">Projected Impact (5s)</span>
319
+ <div style="display:flex; align-items: baseline; gap: 10px;">
320
  <span id="proj-pct" class="value value-lg">--%</span>
321
+ <span id="proj-val" class="value-sub">---</span>
322
  </div>
323
  </div>
324
 
 
326
 
327
  <!-- 2. IMBALANCE SCORE -->
328
  <div class="data-group">
329
+ <span class="label">Order Flow Score</span>
330
  <span id="score-val" class="value">0.00</span>
331
  </div>
332
 
 
334
 
335
  <!-- 3. WALLS -->
336
  <div class="data-group" style="flex: 1;">
337
+ <span class="label" style="margin-bottom: 10px;">Detected Walls (Z > 3.0)</span>
338
  <div id="wall-list" class="list-container">
339
+ <span class="c-dim" style="font-size: 11px;">Initializing scan...</span>
340
  </div>
341
  </div>
342
 
343
  <div style="margin-top: auto;">
344
  <span class="label">System Latency</span>
345
+ <div class="value-sub c-green">Connected</div>
346
  </div>
347
  </div>
348
  </div>
 
363
  wallList: document.getElementById('wall-list')
364
  }};
365
 
366
+ // HIGH CONTRAST CHART CONFIG
367
  const chartOpts = {{
368
+ layout: {{ background: {{ type: 'solid', color: '#0a0a0a' }}, textColor: '#888', fontFamily: 'JetBrains Mono' }},
369
+ grid: {{ vertLines: {{ color: '#151515' }}, horzLines: {{ color: '#151515' }} }},
370
+ rightPriceScale: {{ borderColor: '#222', scaleMargins: {{ top: 0.1, bottom: 0.1 }} }},
371
+ timeScale: {{ borderColor: '#222', timeVisible: true, secondsVisible: true }},
372
+ crosshair: {{ mode: 1, vertLine: {{ color: '#444', labelBackgroundColor: '#444' }}, horzLine: {{ color: '#444', labelBackgroundColor: '#444' }} }}
373
  }};
374
 
375
  // 1. PRICE
376
  const priceChart = LightweightCharts.createChart(document.getElementById('tv-price'), chartOpts);
377
+ const priceSeries = priceChart.addLineSeries({{ color: '#FFFFFF', lineWidth: 1, title: 'Price' }});
378
+ const predSeries = priceChart.addLineSeries({{ color: '#2979ff', lineWidth: 1, lineStyle: 2, title: 'Forecast' }});
379
 
380
  // 2. RAW
381
  const rawChart = LightweightCharts.createChart(document.getElementById('tv-raw'), {{
382
  ...chartOpts,
383
  localization: {{ timeFormatter: t => '$' + t.toFixed(2) }}
384
  }});
385
+ const bidSeries = rawChart.addAreaSeries({{ lineColor: '#00ff9d', topColor: 'rgba(0, 255, 157, 0.15)', bottomColor: 'rgba(0,0,0,0)', lineWidth: 1 }});
386
+ const askSeries = rawChart.addAreaSeries({{ lineColor: '#ff3b3b', topColor: 'rgba(255, 59, 59, 0.15)', bottomColor: 'rgba(0,0,0,0)', lineWidth: 1 }});
387
 
388
  // 3. NET
389
  const netChart = LightweightCharts.createChart(document.getElementById('tv-net'), {{
390
  ...chartOpts,
391
  localization: {{ timeFormatter: t => '$' + t.toFixed(2) }}
392
  }});
393
+ const netSeries = netChart.addHistogramSeries({{ color: '#2979ff' }});
394
 
395
  let activeLines = [];
396
 
397
+ // RESIZE HANDLER
398
+ new ResizeObserver(entries => {{
399
+ for(let entry of entries) {{
400
+ const {{width, height}} = entry.contentRect;
401
+ if(entry.target.id === 'tv-price') priceChart.applyOptions({{width, height}});
402
+ if(entry.target.id === 'tv-raw') rawChart.applyOptions({{width, height}});
403
+ if(entry.target.id === 'tv-net') netChart.applyOptions({{width, height}});
404
+ }}
405
+ }}).observe(document.body); // Observer attached to body to catch layout shifts, logic handles IDs
406
+
407
+ // Specific element observers
408
+ ['tv-price', 'tv-raw', 'tv-net'].forEach(id => {{
409
+ new ResizeObserver(e => {{
410
+ if(id === 'tv-price') priceChart.applyOptions({{ width: e[0].contentRect.width, height: e[0].contentRect.height }});
411
+ if(id === 'tv-raw') rawChart.applyOptions({{ width: e[0].contentRect.width, height: e[0].contentRect.height }});
412
+ if(id === 'tv-net') netChart.applyOptions({{ width: e[0].contentRect.width, height: e[0].contentRect.height }});
413
+ }}).observe(document.getElementById(id));
414
+ }});
415
 
416
  function connect() {{
417
  const ws = new WebSocket((location.protocol === 'https:' ? 'wss' : 'ws') + '://' + location.host + '/ws');
 
460
  let html = "";
461
 
462
  const addWall = (w, type) => {{
463
+ const color = type === 'BID' ? '#00ff9d' : '#ff3b3b';
464
  activeLines.push(priceSeries.createPriceLine({{ price: w.price, color: color, lineWidth: 1, lineStyle: 2, axisLabelVisible: false }}));
465
  html += `<div class="list-item">
466
  <span style="color:${{color}}">${{type}} ${{w.price}}</span>
467
+ <span class="c-dim">Z:${{w.z_score.toFixed(1)}}</span>
468
  </div>`;
469
  }};
470
 
471
  data.walls.asks.forEach(w => addWall(w, 'ASK'));
472
  data.walls.bids.forEach(w => addWall(w, 'BID'));
473
 
474
+ dom.wallList.innerHTML = html || '<span class="c-dim" style="font-size:11px">No significant walls.</span>';
475
  }}
476
 
477
  // DEPTH
 
481
  const t = data.depth_x[i];
482
  bids.push({{ time: t, value: data.depth_bids[i] }});
483
  asks.push({{ time: t, value: data.depth_asks[i] }});
484
+ nets.push({{ time: t, value: data.depth_net[i], color: data.depth_net[i] > 0 ? '#00ff9d' : '#ff3b3b' }});
485
  }}
486
  bidSeries.setData(bids);
487
  askSeries.setData(asks);