Alvin3y1 commited on
Commit
6dbbeb8
·
verified ·
1 Parent(s): 7252530

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -64
app.py CHANGED
@@ -58,7 +58,7 @@ def analyze_structure(diff_x, diff_y, current_mid):
58
  "net_score": net_total
59
  }
60
 
61
- # --- HTML Frontend (Fixed CDN & Logic) ---
62
  HTML_PAGE = f"""
63
  <!DOCTYPE html>
64
  <html lang="en">
@@ -66,9 +66,8 @@ HTML_PAGE = f"""
66
  <meta charset="UTF-8">
67
  <title>AI Liquidity Dashboard | {SYMBOL_KRAKEN}</title>
68
 
69
- <!-- FIXED: Pinned version 4.1.1 to ensure API compatibility -->
70
  <script src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js"></script>
71
- <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
72
 
73
  <style>
74
  :root {{
@@ -90,7 +89,8 @@ HTML_PAGE = f"""
90
 
91
  .panel-header {{ padding: 8px 12px; background: #0f1116; border-bottom: 1px solid var(--border); font-size: 12px; font-weight: bold; display: flex; justify-content: space-between; color: var(--accent-green); }}
92
 
93
- #tv-chart, #depth-chart {{ flex: 1; width: 100%; position: relative; }}
 
94
  .stats-content {{ padding: 15px; overflow-y: auto; flex: 1; }}
95
  .stat-box {{ margin-bottom: 20px; padding: 10px; background: rgba(255,255,255,0.02); border-radius: 4px; }}
96
  .stat-label {{ font-size: 11px; color: #666; display: block; margin-bottom: 4px; }}
@@ -120,14 +120,19 @@ HTML_PAGE = f"""
120
  </div>
121
 
122
  <div class="grid-container">
 
123
  <div id="p-price" class="panel">
124
  <div class="panel-header"><span>BTC/USD Price Action</span><span id="live-price">---</span></div>
125
- <div id="tv-chart"></div>
126
  </div>
 
 
127
  <div id="p-depth" class="panel">
128
- <div class="panel-header"><span>Liquidity Structure</span><span>DEPTH 300</span></div>
129
- <div id="depth-chart"></div>
130
  </div>
 
 
131
  <div id="p-stats" class="panel">
132
  <div class="panel-header">ANALYTICS ENGINE</div>
133
  <div class="stats-content">
@@ -154,7 +159,7 @@ HTML_PAGE = f"""
154
  </div>
155
 
156
  <script>
157
- // Global Error Handler
158
  window.onerror = function(msg, url, lineNo, columnNo, error) {{
159
  const errDiv = document.getElementById('error-log');
160
  errDiv.innerHTML += `ERROR: ${{msg}} (Line: ${{lineNo}})<br>`;
@@ -176,40 +181,64 @@ HTML_PAGE = f"""
176
  logs: document.getElementById('term-logs')
177
  }};
178
 
179
- // --- 1. SETUP LIGHTWEIGHT CHART ---
180
- if (typeof LightweightCharts === 'undefined') throw new Error("Chart Library not loaded");
181
-
182
- const chartContainer = document.getElementById('tv-chart');
183
- const chart = LightweightCharts.createChart(chartContainer, {{
184
  layout: {{ background: {{ type: 'solid', color: '#12141a' }}, textColor: '#888' }},
185
  grid: {{ vertLines: {{ color: '#1f2833' }}, horzLines: {{ color: '#1f2833' }} }},
186
- timeScale: {{ timeVisible: true, secondsVisible: true, borderColor: '#2d3842' }},
187
  rightPriceScale: {{ borderColor: '#2d3842' }},
188
- }});
 
189
 
190
- const lineSeries = chart.addLineSeries({{ color: '#2962FF', lineWidth: 2 }});
191
- const predSeries = chart.addLineSeries({{ color: '#ff9800', lineWidth: 2, lineStyle: 2 }}); // Dotted
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
- let supportLine = null;
194
- let resistanceLine = null;
 
 
 
 
 
 
 
 
 
195
 
196
  // Resize Logic
197
  new ResizeObserver(entries => {{
198
- if (entries.length === 0 || entries[0].target !== chartContainer) return;
199
- const newRect = entries[0].contentRect;
200
- chart.applyOptions({{ width: newRect.width, height: newRect.height }});
201
- }}).observe(chartContainer);
202
-
203
- // --- 2. SETUP PLOTLY ---
204
- Plotly.newPlot('depth-chart', [], {{
205
- paper_bgcolor: '#12141a', plot_bgcolor: '#12141a',
206
- font: {{ color: '#888', family: 'monospace' }},
207
- margin: {{ t: 10, b: 30, l: 40, r: 20 }},
208
- xaxis: {{ gridcolor: '#1f2833' }}, yaxis: {{ gridcolor: '#1f2833' }},
209
- showlegend: false
210
- }}, {{ responsive: true, displayModeBar: false }});
211
-
212
- // --- 3. HELPERS ---
213
  function log(msg, type='neutral') {{
214
  const div = document.createElement('div');
215
  div.className = 'log-line';
@@ -219,20 +248,19 @@ HTML_PAGE = f"""
219
  if (dom.logs.children.length > 20) dom.logs.removeChild(dom.logs.lastChild);
220
  }}
221
 
222
- // --- 4. MAIN LOOP ---
223
  async function fetchData() {{
224
  try {{
225
  const res = await fetch('/data?t=' + Date.now());
226
  const data = await res.json();
227
 
228
  if (data.error) {{
229
- dom.status.innerText = "Processing Market Data...";
230
  return;
231
  }}
232
-
233
  dom.loader.style.display = 'none';
234
 
235
- // Update Price Chart
236
  const uniqueHistory = [];
237
  const seen = new Set();
238
  data.history.forEach(d => {{
@@ -241,38 +269,37 @@ HTML_PAGE = f"""
241
  }});
242
 
243
  if (uniqueHistory.length > 0) {{
244
- lineSeries.setData(uniqueHistory);
245
  const last = uniqueHistory[uniqueHistory.length - 1];
246
-
247
  dom.price.innerText = last.value.toLocaleString(undefined, {{minimumFractionDigits: 2}});
248
 
249
  if (data.analysis) {{
250
  const {{ projected, support, resistance, net_score }} = data.analysis;
251
 
252
- // Prediction
253
  predSeries.setData([last, {{ time: last.time + 60, value: projected }}]);
254
  dom.projVal.innerText = projected.toFixed(0);
255
 
256
- // S/R Lines
257
  if (support) {{
258
  dom.supVal.innerText = support.toFixed(0);
259
- if (!supportLine) supportLine = lineSeries.createPriceLine({{ price: support, color: '#00e676', title: 'SUP' }});
260
  else supportLine.applyOptions({{ price: support }});
261
  }} else {{
262
  dom.supVal.innerText = '---';
263
- if (supportLine) {{ lineSeries.removePriceLine(supportLine); supportLine = null; }}
264
  }}
265
 
266
  if (resistance) {{
267
  dom.resVal.innerText = resistance.toFixed(0);
268
- if (!resistanceLine) resistanceLine = lineSeries.createPriceLine({{ price: resistance, color: '#ff1744', title: 'RES' }});
269
  else resistanceLine.applyOptions({{ price: resistance }});
270
  }} else {{
271
  dom.resVal.innerText = '---';
272
- if (resistanceLine) {{ lineSeries.removePriceLine(resistanceLine); resistanceLine = null; }}
273
  }}
274
 
275
- // Score Meter
276
  dom.scoreVal.innerText = net_score.toFixed(1);
277
  dom.scoreVal.className = net_score > 0 ? "stat-value green" : "stat-value red";
278
  let barW = Math.min(Math.abs(net_score)*2, 50);
@@ -280,29 +307,46 @@ HTML_PAGE = f"""
280
  dom.scoreBar.style.left = net_score > 0 ? '50%' : (50 - barW) + '%';
281
  dom.scoreBar.style.background = net_score > 0 ? '#66fcf1' : '#ff3b3b';
282
 
283
- // Random Logs
284
  if (Math.random() > 0.98) {{
285
- if (net_score > 30) log("Significant Bullish flow detected", 'bull');
286
- else if (net_score < -30) log("Significant Bearish flow detected", 'bear');
287
  }}
288
  }}
289
  }}
290
 
291
- // Update Depth Chart
292
- const trace = {{ x: data.diff.x, y: data.diff.y, type: 'scatter', fill: 'tozeroy', line: {{color: '#e040fb'}} }};
293
- Plotly.react('depth-chart', [trace], {{
294
- paper_bgcolor: '#12141a', plot_bgcolor: '#12141a',
295
- font: {{ color: '#888' }}, margin: {{ t: 10, b: 20, l: 30, r: 10 }},
296
- xaxis: {{ showgrid: false, title: '' }},
297
- yaxis: {{ showgrid: true, gridcolor: '#333' }},
298
- shapes: [{{ type: 'line', x0: 0, x1: 1, xref: 'paper', y0: 0, y1: 0, line: {{color: '#555'}} }}]
299
- }}, {{ displayModeBar: false }});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
 
301
  }} catch (e) {{
302
- console.error("Fetch loop error:", e);
303
  }}
304
  }}
305
-
306
  setInterval(fetchData, 1000);
307
  }});
308
  </script>
@@ -346,8 +390,7 @@ async def kraken_worker():
346
  market_state['ready'] = True
347
 
348
  now = time.time()
349
- # Throttle history (250ms)
350
- if not market_state['history'] or (now - market_state['history'][-1]['t'] > 0.25):
351
  market_state['history'].append({'t': now, 'p': mid})
352
  if len(market_state['history']) > HISTORY_LENGTH:
353
  market_state['history'].pop(0)
@@ -365,7 +408,6 @@ async def handle_data(request):
365
 
366
  mid = market_state['current_mid']
367
 
368
- # Snapshot
369
  raw_bids = sorted(market_state['bids'].items(), key=lambda x: -x[0])[:300]
370
  raw_asks = sorted(market_state['asks'].items(), key=lambda x: x[0])[:300]
371
 
 
58
  "net_score": net_total
59
  }
60
 
61
+ # --- HTML Frontend ---
62
  HTML_PAGE = f"""
63
  <!DOCTYPE html>
64
  <html lang="en">
 
66
  <meta charset="UTF-8">
67
  <title>AI Liquidity Dashboard | {SYMBOL_KRAKEN}</title>
68
 
69
+ <!-- Pinned Version for Stability -->
70
  <script src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js"></script>
 
71
 
72
  <style>
73
  :root {{
 
89
 
90
  .panel-header {{ padding: 8px 12px; background: #0f1116; border-bottom: 1px solid var(--border); font-size: 12px; font-weight: bold; display: flex; justify-content: space-between; color: var(--accent-green); }}
91
 
92
+ #tv-price, #tv-depth {{ flex: 1; width: 100%; position: relative; }}
93
+
94
  .stats-content {{ padding: 15px; overflow-y: auto; flex: 1; }}
95
  .stat-box {{ margin-bottom: 20px; padding: 10px; background: rgba(255,255,255,0.02); border-radius: 4px; }}
96
  .stat-label {{ font-size: 11px; color: #666; display: block; margin-bottom: 4px; }}
 
120
  </div>
121
 
122
  <div class="grid-container">
123
+ <!-- Price Chart Panel -->
124
  <div id="p-price" class="panel">
125
  <div class="panel-header"><span>BTC/USD Price Action</span><span id="live-price">---</span></div>
126
+ <div id="tv-price"></div>
127
  </div>
128
+
129
+ <!-- Depth Chart Panel (Now using Lightweight Charts) -->
130
  <div id="p-depth" class="panel">
131
+ <div class="panel-header"><span>Net Liquidity (Green=Bids, Red=Asks)</span><span>DEPTH 300</span></div>
132
+ <div id="tv-depth"></div>
133
  </div>
134
+
135
+ <!-- Analytics Panel -->
136
  <div id="p-stats" class="panel">
137
  <div class="panel-header">ANALYTICS ENGINE</div>
138
  <div class="stats-content">
 
159
  </div>
160
 
161
  <script>
162
+ // --- ERROR HANDLING ---
163
  window.onerror = function(msg, url, lineNo, columnNo, error) {{
164
  const errDiv = document.getElementById('error-log');
165
  errDiv.innerHTML += `ERROR: ${{msg}} (Line: ${{lineNo}})<br>`;
 
181
  logs: document.getElementById('term-logs')
182
  }};
183
 
184
+ // --- CHART CONFIGURATION ---
185
+ const chartOptionsCommon = {{
 
 
 
186
  layout: {{ background: {{ type: 'solid', color: '#12141a' }}, textColor: '#888' }},
187
  grid: {{ vertLines: {{ color: '#1f2833' }}, horzLines: {{ color: '#1f2833' }} }},
 
188
  rightPriceScale: {{ borderColor: '#2d3842' }},
189
+ timeScale: {{ borderColor: '#2d3842' }},
190
+ }};
191
 
192
+ // 1. PRICE CHART
193
+ const priceContainer = document.getElementById('tv-price');
194
+ const priceChart = LightweightCharts.createChart(priceContainer, {{
195
+ ...chartOptionsCommon,
196
+ timeScale: {{ timeVisible: true, secondsVisible: true }},
197
+ }});
198
+ const priceSeries = priceChart.addLineSeries({{ color: '#2962FF', lineWidth: 2 }});
199
+ const predSeries = priceChart.addLineSeries({{ color: '#ff9800', lineWidth: 2, lineStyle: 2 }});
200
+ let supportLine = null, resistanceLine = null;
201
+
202
+ // 2. DEPTH CHART (Hacked for Non-Time Series)
203
+ const depthContainer = document.getElementById('tv-depth');
204
+ const depthChart = LightweightCharts.createChart(depthContainer, {{
205
+ ...chartOptionsCommon,
206
+ timeScale: {{
207
+ // Override formatted date to just show raw distance
208
+ tickMarkFormatter: (time) => parseFloat(time).toFixed(0),
209
+ }},
210
+ localization: {{
211
+ // Override tooltip formatter
212
+ timeFormatter: (time) => 'Dist: $' + parseFloat(time).toFixed(2),
213
+ }}
214
+ }});
215
 
216
+ // We use two Area series: one for Positive (Green), one for Negative (Red)
217
+ const bullSeries = depthChart.addAreaSeries({{
218
+ topColor: 'rgba(102, 252, 241, 0.4)',
219
+ bottomColor: 'rgba(102, 252, 241, 0.0)',
220
+ lineColor: '#66fcf1', lineWidth: 2
221
+ }});
222
+ const bearSeries = depthChart.addAreaSeries({{
223
+ topColor: 'rgba(255, 59, 59, 0.4)',
224
+ bottomColor: 'rgba(255, 59, 59, 0.0)',
225
+ lineColor: '#ff3b3b', lineWidth: 2
226
+ }});
227
 
228
  // Resize Logic
229
  new ResizeObserver(entries => {{
230
+ for (let entry of entries) {{
231
+ if (entry.target === priceContainer) priceChart.applyOptions({{ width: entry.contentRect.width, height: entry.contentRect.height }});
232
+ if (entry.target === depthContainer) depthChart.applyOptions({{ width: entry.contentRect.width, height: entry.contentRect.height }});
233
+ }}
234
+ }}).observe(priceContainer);
235
+ new ResizeObserver(entries => {{
236
+ for (let entry of entries) {{
237
+ if (entry.target === depthContainer) depthChart.applyOptions({{ width: entry.contentRect.width, height: entry.contentRect.height }});
238
+ }}
239
+ }}).observe(depthContainer);
240
+
241
+ // --- HELPERS ---
 
 
 
242
  function log(msg, type='neutral') {{
243
  const div = document.createElement('div');
244
  div.className = 'log-line';
 
248
  if (dom.logs.children.length > 20) dom.logs.removeChild(dom.logs.lastChild);
249
  }}
250
 
251
+ // --- DATA LOOP ---
252
  async function fetchData() {{
253
  try {{
254
  const res = await fetch('/data?t=' + Date.now());
255
  const data = await res.json();
256
 
257
  if (data.error) {{
258
+ dom.status.innerText = "Synchronizing Market State...";
259
  return;
260
  }}
 
261
  dom.loader.style.display = 'none';
262
 
263
+ // -- UPDATE PRICE CHART --
264
  const uniqueHistory = [];
265
  const seen = new Set();
266
  data.history.forEach(d => {{
 
269
  }});
270
 
271
  if (uniqueHistory.length > 0) {{
272
+ priceSeries.setData(uniqueHistory);
273
  const last = uniqueHistory[uniqueHistory.length - 1];
 
274
  dom.price.innerText = last.value.toLocaleString(undefined, {{minimumFractionDigits: 2}});
275
 
276
  if (data.analysis) {{
277
  const {{ projected, support, resistance, net_score }} = data.analysis;
278
 
279
+ // Projection
280
  predSeries.setData([last, {{ time: last.time + 60, value: projected }}]);
281
  dom.projVal.innerText = projected.toFixed(0);
282
 
283
+ // S/R Lines (Using createPriceLine)
284
  if (support) {{
285
  dom.supVal.innerText = support.toFixed(0);
286
+ if (!supportLine) supportLine = priceSeries.createPriceLine({{ price: support, color: '#00e676', title: 'SUP' }});
287
  else supportLine.applyOptions({{ price: support }});
288
  }} else {{
289
  dom.supVal.innerText = '---';
290
+ if (supportLine) {{ priceSeries.removePriceLine(supportLine); supportLine = null; }}
291
  }}
292
 
293
  if (resistance) {{
294
  dom.resVal.innerText = resistance.toFixed(0);
295
+ if (!resistanceLine) resistanceLine = priceSeries.createPriceLine({{ price: resistance, color: '#ff1744', title: 'RES' }});
296
  else resistanceLine.applyOptions({{ price: resistance }});
297
  }} else {{
298
  dom.resVal.innerText = '---';
299
+ if (resistanceLine) {{ priceSeries.removePriceLine(resistanceLine); resistanceLine = null; }}
300
  }}
301
 
302
+ // Meter & Logs
303
  dom.scoreVal.innerText = net_score.toFixed(1);
304
  dom.scoreVal.className = net_score > 0 ? "stat-value green" : "stat-value red";
305
  let barW = Math.min(Math.abs(net_score)*2, 50);
 
307
  dom.scoreBar.style.left = net_score > 0 ? '50%' : (50 - barW) + '%';
308
  dom.scoreBar.style.background = net_score > 0 ? '#66fcf1' : '#ff3b3b';
309
 
 
310
  if (Math.random() > 0.98) {{
311
+ if (net_score > 40) log("Strong Buyers Detected", 'bull');
312
+ else if (net_score < -40) log("Strong Sellers Detected", 'bear');
313
  }}
314
  }}
315
  }}
316
 
317
+ // -- UPDATE DEPTH CHART --
318
+ // Map diff_x (distance) to 'time', diff_y to 'value'
319
+ // Split into Bull (Positive) and Bear (Negative) series
320
+ if (data.diff && data.diff.x.length > 0) {{
321
+ const bullData = [];
322
+ const bearData = [];
323
+
324
+ for (let i = 0; i < data.diff.x.length; i++) {{
325
+ const xVal = data.diff.x[i]; // Distance
326
+ const yVal = data.diff.y[i]; // Net Volume
327
+
328
+ // Ensure strictly unique and ascending (already guaranteed by logic, but safe to cast)
329
+ // We use xVal as 'time'
330
+
331
+ if (yVal >= 0) {{
332
+ bullData.push({{ time: xVal, value: yVal }});
333
+ bearData.push({{ time: xVal, value: 0 }}); // Clamp other series to 0
334
+ }} else {{
335
+ bullData.push({{ time: xVal, value: 0 }});
336
+ bearData.push({{ time: xVal, value: yVal }});
337
+ }}
338
+ }}
339
+
340
+ bullSeries.setData(bullData);
341
+ bearSeries.setData(bearData);
342
+ depthChart.timeScale().fitContent();
343
+ }}
344
 
345
  }} catch (e) {{
346
+ console.error(e);
347
  }}
348
  }}
349
+
350
  setInterval(fetchData, 1000);
351
  }});
352
  </script>
 
390
  market_state['ready'] = True
391
 
392
  now = time.time()
393
+ if not market_state['history'] or (now - market_state['history'][-1]['t'] > 0.5):
 
394
  market_state['history'].append({'t': now, 'p': mid})
395
  if len(market_state['history']) > HISTORY_LENGTH:
396
  market_state['history'].pop(0)
 
408
 
409
  mid = market_state['current_mid']
410
 
 
411
  raw_bids = sorted(market_state['bids'].items(), key=lambda x: -x[0])[:300]
412
  raw_asks = sorted(market_state['asks'].items(), key=lambda x: x[0])[:300]
413