Alvin3y1 commited on
Commit
e5d67bc
Β·
verified Β·
1 Parent(s): 2324f1b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -129
app.py CHANGED
@@ -3,6 +3,7 @@ import json
3
  import logging
4
  import time
5
  import bisect
 
6
  from aiohttp import web
7
  import websockets
8
 
@@ -24,113 +25,77 @@ market_state = {
24
  "ready": False
25
  }
26
 
27
- # --- AI Logic Helper ---
28
- def generate_ai_commentary(diff_y):
29
  """
30
- Analyzes the shape of the Liquidity Curve (Bids - Asks).
31
- Splits data into 'Near' (Close to price) and 'Deep' (Far from price).
32
  """
33
- if not diff_y or len(diff_y) < 10:
34
- return {"text": "Gathering depth data...", "sentiment": "neutral"}
35
-
36
- # 1. Split the data
37
- # First 20% of steps = Near-term (Active trading zone)
38
- # Last 20% of steps = Deep-term (Macro walls)
39
- split_idx = int(len(diff_y) * 0.2)
40
-
41
- near_data = diff_y[:split_idx]
42
- deep_data = diff_y[-split_idx:]
 
 
 
43
 
44
- near_avg = sum(near_data) / len(near_data)
45
- deep_avg = sum(deep_data) / len(deep_data)
46
 
47
- msg = ""
48
- sentiment = "neutral"
49
-
50
- # --- PATTERN RECOGNITION ---
51
-
52
- # 1. POSITIVE -> NEGATIVE (The "False Floor" or "Overhead Resistance")
53
- if near_avg > 0 and deep_avg < 0:
54
- sentiment = "warning"
55
- msg = (
56
- f"πŸ“‰ <b>STRUCTURAL FLIP (Pos ➝ Neg):</b><br>"
57
- f"Near-term buyers are holding the price, BUT deeper liquidity reveals heavy sell pressure.<br>"
58
- f"<i>Why?</i> Traders are defending the immediate level, but whales have placed large Ask walls higher up. "
59
- f"Upside is capped; price may rally briefly then fail."
60
- )
61
-
62
- # 2. NEGATIVE -> POSITIVE (The "Bear Trap" or "Safety Net")
63
- elif near_avg < 0 and deep_avg > 0:
64
- sentiment = "bullish"
65
- msg = (
66
- f"πŸ“ˆ <b>STRUCTURAL FLIP (Neg ➝ Pos):</b><br>"
67
- f"Immediate sell pressure is visible, BUT massive buy walls exist deeper down.<br>"
68
- f"<i>Why?</i> Price is being suppressed locally, but smart money is waiting lower to absorb the dip. "
69
- f"Downside is limited; this often precedes a bounce."
70
- )
71
-
72
- # 3. CONSISTENTLY POSITIVE (Full Bull)
73
- elif near_avg > 0 and deep_avg > 0:
74
- sentiment = "bullish"
75
- strength = "Mild" if deep_avg < 50 else "Extreme"
76
- msg = (
77
- f"πŸš€ <b>FULL BULLISH CONVICTION:</b><br>"
78
- f"Bids exceed Asks at both local and deep levels.<br>"
79
- f"<i>Why?</i> There is no overhead supply. Buyers are aggressive at the current price AND stacking orders below. "
80
- f"Path of least resistance is purely UP."
81
- )
82
-
83
- # 4. CONSISTENTLY NEGATIVE (Full Bear)
84
- elif near_avg < 0 and deep_avg < 0:
85
- sentiment = "bearish"
86
- msg = (
87
- f"πŸ”» <b>FULL BEARISH CONVICTION:</b><br>"
88
- f"Asks exceed Bids at all levels.<br>"
89
- f"<i>Why?</i> There is no support below. Sellers are active at the current price AND stacking orders higher. "
90
- f"Price is likely to drift lower until buyers step in."
91
- )
92
-
93
- # 5. MIXED / CHOP
94
- else:
95
- diff = near_avg - deep_avg
96
- if abs(diff) < 5:
97
- msg = "βš–οΈ <b>PERFECT BALANCE:</b> Liquidity is symmetrical. Market is waiting for a trigger."
98
- else:
99
- msg = "πŸ”„ <b>TRANSITIONING:</b> Orderbook structure is shifting. Watch for a dominant side to emerge."
100
-
101
- return {"text": msg, "sentiment": sentiment}
102
 
103
  # --- HTML Frontend ---
104
  HTML_PAGE = f"""
105
  <!DOCTYPE html>
106
  <html>
107
  <head>
108
- <title>BTC-USD Structural AI</title>
109
  <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
110
  <style>
111
  body {{ margin: 0; padding: 0; background-color: #0e0e0e; color: #ccc; font-family: 'Courier New', monospace; overflow: hidden; }}
112
 
113
  #container {{ display: flex; flex-direction: column; height: 100vh; width: 100vw; }}
114
 
115
- /* Row 1: Charts */
116
  #row-top {{ flex: 1; display: flex; width: 100%; border-bottom: 1px solid #333; }}
117
-
118
- /* Row 2: Charts + AI */
119
  #row-bot {{ flex: 1; display: flex; width: 100%; }}
120
 
121
  .col {{ width: 50%; height: 100%; border-right: 1px solid #333; position: relative; }}
122
- .col-ai {{ width: 50%; height: 100%; background-color: #080808; display: flex; flex-direction: column; padding: 10px; overflow-y: auto; }}
123
 
124
- /* AI Text Styling */
125
- .terminal-header {{ color: #00bcd4; font-weight: bold; border-bottom: 1px dashed #333; padding-bottom: 5px; margin-bottom: 10px; font-size: 16px; }}
126
- .log-entry {{ margin-bottom: 12px; font-size: 13px; border-left: 4px solid transparent; padding-left: 10px; background: #111; padding: 8px; border-radius: 4px; }}
127
- .log-time {{ color: #555; font-size: 11px; display: block; margin-bottom: 4px; }}
128
 
129
- /* Sentiments */
130
- .bullish {{ border-left-color: #00e676; }}
131
- .bearish {{ border-left-color: #ff1744; }}
132
- .neutral {{ border-left-color: #999; }}
133
- .warning {{ border-left-color: #ff9800; }}
134
 
135
  .chart {{ width: 100%; height: 100%; }}
136
 
@@ -140,12 +105,13 @@ HTML_PAGE = f"""
140
  </style>
141
  </head>
142
  <body>
143
- <div id="status">Connecting...</div>
144
 
145
  <div id="container">
146
  <!-- ROW 1 -->
147
  <div id="row-top">
148
  <div class="col">
 
149
  <div id="price-chart" class="chart"></div>
150
  </div>
151
  <div class="col">
@@ -156,12 +122,10 @@ HTML_PAGE = f"""
156
  <!-- ROW 2 -->
157
  <div id="row-bot">
158
  <div class="col">
159
- <!-- NET DIFFERENCE CHART -->
160
  <div id="diff-chart" class="chart"></div>
161
  </div>
162
  <div class="col-ai">
163
- <!-- AI TERMINAL -->
164
- <div class="terminal-header">> DEPTH STRUCTURE ANALYSIS</div>
165
  <div id="terminal-logs"></div>
166
  </div>
167
  </div>
@@ -188,19 +152,15 @@ HTML_PAGE = f"""
188
  yaxis: {{ gridcolor: '#222' }}
189
  }};
190
 
191
- function addLog(data) {{
192
- // De-duplicate messages
193
- if (data.comment.text === lastLogText) return;
194
- lastLogText = data.comment.text;
195
 
196
  const div = document.createElement('div');
197
- div.className = `log-entry ${{data.comment.sentiment}}`;
198
- const timeStr = new Date().toLocaleTimeString();
199
- div.innerHTML = `<span class="log-time">${{timeStr}}</span> ${{data.comment.text}}`;
200
-
201
  termLogs.prepend(div);
202
- // Keep history short
203
- if (termLogs.children.length > 10) termLogs.removeChild(termLogs.lastChild);
204
  }}
205
 
206
  async function updateCharts() {{
@@ -215,41 +175,105 @@ HTML_PAGE = f"""
215
 
216
  statusDiv.innerHTML = `Mid: <span class="${{data.mid >= data.prev_mid ? 'green' : 'red'}}">$${{data.mid.toLocaleString(undefined, {{minimumFractionDigits: 2}})}}</span>`;
217
 
218
- // 1. Price
219
- const tracePrice = {{ x: data.history.map(d=>new Date(d.t*1000)), y: data.history.map(d=>d.p), type: 'scatter', mode:'lines', line: {{color: '#29b6f6'}} }};
220
- if (!initPrice) {{ Plotly.newPlot(priceDiv, [tracePrice], {{ ...commonLayout, title: '<b>Midprice</b>', xaxis: {{type:'date', gridcolor:'#222'}} }}, commonConfig); initPrice = true; }}
221
- else {{ Plotly.react(priceDiv, [tracePrice], {{ ...commonLayout, title: '<b>Midprice</b>', xaxis: {{type:'date', gridcolor:'#222'}} }}, commonConfig); }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
- // 2. Volume
 
 
 
 
 
 
 
 
 
 
 
224
  const tracesVol = [
225
- {{ x: data.vol.dist_bids, y: data.vol.vol_bids, type: 'scatter', name: 'Bid', line: {{color: '#00e676'}} }},
226
  {{ x: data.vol.dist_asks, y: data.vol.vol_asks, type: 'scatter', name: 'Ask', line: {{color: '#ff1744'}} }}
227
  ];
228
  if (!initVol) {{ Plotly.newPlot(volDiv, tracesVol, {{ ...commonLayout, title: '<b>Cumulative Volume</b>', xaxis: {{title:'Distance ($)'}} }}, commonConfig); initVol = true; }}
229
  else {{ Plotly.react(volDiv, tracesVol, {{ ...commonLayout, title: '<b>Cumulative Volume</b>', xaxis: {{title:'Distance ($)'}} }}, commonConfig); }}
230
 
231
- // 3. Net Difference (Bids - Asks)
232
- const traceDiff = {{
233
- x: data.diff.x,
234
- y: data.diff.y,
235
- type: 'scatter',
236
- mode: 'lines',
237
- fill: 'tozeroy',
238
- line: {{color: '#e040fb', width: 2}}
239
- }};
240
- // Draw a zero line for reference
241
- const layoutDiff = {{
242
  ...commonLayout,
243
  title: '<b>Net Liquidity (Bids - Asks)</b>',
244
  xaxis: {{title:'Distance ($)'}},
245
- shapes: [{{ type: 'line', x0: 0, x1: 1, xref: 'paper', y0: 0, y1: 0, line: {{color: '#666', width: 1, dash:'dot'}} }}]
246
  }};
247
 
248
- if (!initDiff) {{ Plotly.newPlot(diffDiv, [traceDiff], layoutDiff, commonConfig); initDiff = true; }}
249
- else {{ Plotly.react(diffDiv, [traceDiff], layoutDiff, commonConfig); }}
250
-
251
- // 4. AI Log
252
- addLog(data);
253
 
254
  }} catch (e) {{ console.error("Fetch error:", e); }}
255
  }}
@@ -318,7 +342,7 @@ async def handle_data(request):
318
  raw_bids = sorted(market_state['bids'].items(), key=lambda x: -x[0])[:300]
319
  raw_asks = sorted(market_state['asks'].items(), key=lambda x: x[0])[:300]
320
 
321
- # Calculate Distances & Cum Volumes
322
  d_b_x, d_b_y, cum = [], [], 0
323
  for p, q in raw_bids:
324
  d = mid - p
@@ -348,19 +372,18 @@ async def handle_data(request):
348
  idx_a = bisect.bisect_right(d_a_x, s)
349
  vol_a = d_a_y[idx_a-1] if idx_a > 0 else 0
350
 
351
- val = vol_b - vol_a
352
  diff_x.append(s)
353
- diff_y.append(val)
354
 
355
- # --- Generate AI Commentary ---
356
- ai_output = generate_ai_commentary(diff_y)
357
 
358
  return web.json_response({
359
  "mid": mid,
360
  "prev_mid": market_state['prev_mid'],
361
  "vol": { "dist_bids": d_b_x, "vol_bids": d_b_y, "dist_asks": d_a_x, "vol_asks": d_a_y },
362
  "diff": { "x": diff_x, "y": diff_y },
363
- "comment": ai_output,
364
  "history": market_state['history']
365
  })
366
 
@@ -384,7 +407,7 @@ async def main():
384
  site = web.TCPSite(runner, '0.0.0.0', PORT)
385
  await site.start()
386
 
387
- print(f"πŸš€ BTC-USD Deep Structure Dashboard: http://localhost:{PORT}")
388
  await asyncio.Event().wait()
389
 
390
  if __name__ == "__main__":
 
3
  import logging
4
  import time
5
  import bisect
6
+ import random
7
  from aiohttp import web
8
  import websockets
9
 
 
25
  "ready": False
26
  }
27
 
28
+ # --- AI Logic Helper (Prediction & S/R) ---
29
+ def analyze_structure(diff_x, diff_y, current_mid):
30
  """
31
+ Analyzes the Net Liquidity Curve to find Support, Resistance, and Projected Trend.
 
32
  """
33
+ if not diff_y or len(diff_y) < 5:
34
+ return None
35
+
36
+ # 1. Momentum Projection (Weighted average of the whole book)
37
+ # If the book is net positive, price wants to go up.
38
+ net_total = diff_y[-1] # Cumulative sum at max depth
39
+ # Scaling factor: heavily damped to prevent unrealistic predictions
40
+ momentum_shift = net_total * 0.5
41
+ projected_price = current_mid + momentum_shift
42
+
43
+ # 2. Find Structural Reversals (Zero Crossings)
44
+ support_level = None
45
+ resistance_level = None
46
 
47
+ # We look at the 'Near' book (first 50% of depth) for immediate reversal levels
48
+ scan_limit = len(diff_y) // 2
49
 
50
+ for i in range(1, scan_limit):
51
+ prev_val = diff_y[i-1]
52
+ curr_val = diff_y[i]
53
+ dist = diff_x[i]
54
+
55
+ # Case A: Positive -> Negative (The "Ceiling")
56
+ # Buyers were in control, but suddenly Sellers take over at this distance.
57
+ # This implies a Resistance Wall at (Mid + Distance).
58
+ if prev_val > 0 and curr_val < 0 and resistance_level is None:
59
+ resistance_level = current_mid + dist
60
+
61
+ # Case B: Negative -> Positive (The "Floor")
62
+ # Sellers were in control, but suddenly Buyers take over at this distance.
63
+ # This implies a Support Wall at (Mid - Distance).
64
+ if prev_val < 0 and curr_val > 0 and support_level is None:
65
+ support_level = current_mid - dist
66
+
67
+ return {
68
+ "projected": projected_price,
69
+ "support": support_level,
70
+ "resistance": resistance_level,
71
+ "net_score": net_total
72
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  # --- HTML Frontend ---
75
  HTML_PAGE = f"""
76
  <!DOCTYPE html>
77
  <html>
78
  <head>
79
+ <title>BTC-USD Predictive Dashboard</title>
80
  <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
81
  <style>
82
  body {{ margin: 0; padding: 0; background-color: #0e0e0e; color: #ccc; font-family: 'Courier New', monospace; overflow: hidden; }}
83
 
84
  #container {{ display: flex; flex-direction: column; height: 100vh; width: 100vw; }}
85
 
 
86
  #row-top {{ flex: 1; display: flex; width: 100%; border-bottom: 1px solid #333; }}
 
 
87
  #row-bot {{ flex: 1; display: flex; width: 100%; }}
88
 
89
  .col {{ width: 50%; height: 100%; border-right: 1px solid #333; position: relative; }}
90
+ .col-ai {{ width: 50%; height: 100%; background-color: #050505; display: flex; flex-direction: column; padding: 10px; overflow-y: auto; }}
91
 
92
+ .terminal-header {{ color: #00bcd4; font-weight: bold; border-bottom: 1px dashed #333; padding-bottom: 5px; margin-bottom: 10px; }}
93
+ .log-entry {{ margin-bottom: 8px; font-size: 13px; border-left: 3px solid transparent; padding-left: 8px; }}
94
+ .log-time {{ color: #555; font-size: 11px; margin-right: 8px; }}
 
95
 
96
+ .bullish {{ border-left-color: #00e676; color: #e8f5e9; }}
97
+ .bearish {{ border-left-color: #ff1744; color: #ffebee; }}
98
+ .neutral {{ border-left-color: #999; color: #ccc; }}
 
 
99
 
100
  .chart {{ width: 100%; height: 100%; }}
101
 
 
105
  </style>
106
  </head>
107
  <body>
108
+ <div id="status">Initializing AI Model...</div>
109
 
110
  <div id="container">
111
  <!-- ROW 1 -->
112
  <div id="row-top">
113
  <div class="col">
114
+ <!-- PRICE + PREDICTION -->
115
  <div id="price-chart" class="chart"></div>
116
  </div>
117
  <div class="col">
 
122
  <!-- ROW 2 -->
123
  <div id="row-bot">
124
  <div class="col">
 
125
  <div id="diff-chart" class="chart"></div>
126
  </div>
127
  <div class="col-ai">
128
+ <div class="terminal-header">> PREDICTIVE ANALYTICS ENGINE</div>
 
129
  <div id="terminal-logs"></div>
130
  </div>
131
  </div>
 
152
  yaxis: {{ gridcolor: '#222' }}
153
  }};
154
 
155
+ function addLog(text, type) {{
156
+ if (text === lastLogText) return;
157
+ lastLogText = text;
 
158
 
159
  const div = document.createElement('div');
160
+ div.className = `log-entry ${{type}}`;
161
+ div.innerHTML = `<span class="log-time">${{new Date().toLocaleTimeString()}}</span> ${{text}}`;
 
 
162
  termLogs.prepend(div);
163
+ if (termLogs.children.length > 15) termLogs.removeChild(termLogs.lastChild);
 
164
  }}
165
 
166
  async function updateCharts() {{
 
175
 
176
  statusDiv.innerHTML = `Mid: <span class="${{data.mid >= data.prev_mid ? 'green' : 'red'}}">$${{data.mid.toLocaleString(undefined, {{minimumFractionDigits: 2}})}}</span>`;
177
 
178
+ // --- 1. PRICE CHART WITH PREDICTION & S/R LINES ---
179
+
180
+ // Historical Price Trace
181
+ const historyX = data.history.map(d => new Date(d.t*1000));
182
+ const historyY = data.history.map(d => d.p);
183
+ const tracePrice = {{ x: historyX, y: historyY, type: 'scatter', mode:'lines', line: {{color: '#29b6f6', width: 2}}, name: 'Price' }};
184
+
185
+ // Future Projection Trace
186
+ let traceProj = {{}};
187
+ const lastTime = historyX[historyX.length-1];
188
+ if (data.analysis && lastTime) {{
189
+ const futureTime = new Date(lastTime.getTime() + 60000); // +1 min
190
+ traceProj = {{
191
+ x: [lastTime, futureTime],
192
+ y: [historyY[historyY.length-1], data.analysis.projected],
193
+ type: 'scatter', mode: 'lines',
194
+ line: {{color: '#ff9800', width: 2, dash: 'dot'}},
195
+ name: 'Forecast'
196
+ }};
197
+ }}
198
+
199
+ // Support & Resistance Shapes
200
+ const shapes = [];
201
+ let logMsg = "";
202
+ let logType = "neutral";
203
+
204
+ if (data.analysis) {{
205
+ // Support Line (Green)
206
+ if (data.analysis.support) {{
207
+ shapes.push({{
208
+ type: 'line', x0: historyX[0], x1: lastTime,
209
+ y0: data.analysis.support, y1: data.analysis.support,
210
+ line: {{color: '#00e676', width: 1, dash: 'dash'}}
211
+ }});
212
+ // Add Annotation via Layout? keeping it simple with shapes for now
213
+ }}
214
+
215
+ // Resistance Line (Red)
216
+ if (data.analysis.resistance) {{
217
+ shapes.push({{
218
+ type: 'line', x0: historyX[0], x1: lastTime,
219
+ y0: data.analysis.resistance, y1: data.analysis.resistance,
220
+ line: {{color: '#ff1744', width: 1, dash: 'dash'}}
221
+ }});
222
+ }}
223
+
224
+ // AI Commentary Logic based on Prediction
225
+ const net = data.analysis.net_score;
226
+ if (data.analysis.support && !data.analysis.resistance) {{
227
+ logMsg = `🧱 <b>FLOOR DETECTED:</b> Strong Support at $${{data.analysis.support.toFixed(0)}}. Price expected to bounce.`;
228
+ logType = "bullish";
229
+ }} else if (!data.analysis.support && data.analysis.resistance) {{
230
+ logMsg = `🧱 <b>CEILING DETECTED:</b> Resistance at $${{data.analysis.resistance.toFixed(0)}}. Upside capped.`;
231
+ logType = "bearish";
232
+ }} else if (net > 30) {{
233
+ logMsg = `πŸš€ <b>MOMENTUM:</b> Liquidity is purely Bullish. Target: $${{data.analysis.projected.toFixed(0)}}`;
234
+ logType = "bullish";
235
+ }} else if (net < -30) {{
236
+ logMsg = `πŸ“‰ <b>MOMENTUM:</b> Liquidity is purely Bearish. Target: $${{data.analysis.projected.toFixed(0)}}`;
237
+ logType = "bearish";
238
+ }} else {{
239
+ logMsg = `βš–οΈ <b>RANGE BOUND:</b> Price stuck between flows.`;
240
+ }}
241
+ }}
242
+
243
+ if (logMsg) addLog(logMsg, logType);
244
 
245
+ const priceLayout = {{
246
+ ...commonLayout,
247
+ title: '<b>Price Analysis & Forecast</b>',
248
+ xaxis: {{type:'date', gridcolor:'#222'}},
249
+ shapes: shapes
250
+ }};
251
+
252
+ if (!initPrice) {{ Plotly.newPlot(priceDiv, [tracePrice, traceProj], priceLayout, commonConfig); initPrice = true; }}
253
+ else {{ Plotly.react(priceDiv, [tracePrice, traceProj], priceLayout, commonConfig); }}
254
+
255
+
256
+ // --- 2. VOLUME CHART ---
257
  const tracesVol = [
258
+ {{ x: data.vol.dist_bids, y: data.vol.vol_bids, type: 'scatter', name: 'Bids', line: {{color: '#00e676'}} }},
259
  {{ x: data.vol.dist_asks, y: data.vol.vol_asks, type: 'scatter', name: 'Ask', line: {{color: '#ff1744'}} }}
260
  ];
261
  if (!initVol) {{ Plotly.newPlot(volDiv, tracesVol, {{ ...commonLayout, title: '<b>Cumulative Volume</b>', xaxis: {{title:'Distance ($)'}} }}, commonConfig); initVol = true; }}
262
  else {{ Plotly.react(volDiv, tracesVol, {{ ...commonLayout, title: '<b>Cumulative Volume</b>', xaxis: {{title:'Distance ($)'}} }}, commonConfig); }}
263
 
264
+
265
+ // --- 3. NET DIFFERENCE ---
266
+ const traceDiff = {{ x: data.diff.x, y: data.diff.y, type: 'scatter', mode: 'lines', fill: 'tozeroy', line: {{color: '#e040fb', width: 2}} }};
267
+ // Add Zero Line
268
+ const diffLayout = {{
 
 
 
 
 
 
269
  ...commonLayout,
270
  title: '<b>Net Liquidity (Bids - Asks)</b>',
271
  xaxis: {{title:'Distance ($)'}},
272
+ shapes: [{{ type: 'line', x0: 0, x1: 1, xref: 'paper', y0: 0, y1: 0, line: {{color: '#666', width: 1}} }}]
273
  }};
274
 
275
+ if (!initDiff) {{ Plotly.newPlot(diffDiv, [traceDiff], diffLayout, commonConfig); initDiff = true; }}
276
+ else {{ Plotly.react(diffDiv, [traceDiff], diffLayout, commonConfig); }}
 
 
 
277
 
278
  }} catch (e) {{ console.error("Fetch error:", e); }}
279
  }}
 
342
  raw_bids = sorted(market_state['bids'].items(), key=lambda x: -x[0])[:300]
343
  raw_asks = sorted(market_state['asks'].items(), key=lambda x: x[0])[:300]
344
 
345
+ # Calculate Distances
346
  d_b_x, d_b_y, cum = [], [], 0
347
  for p, q in raw_bids:
348
  d = mid - p
 
372
  idx_a = bisect.bisect_right(d_a_x, s)
373
  vol_a = d_a_y[idx_a-1] if idx_a > 0 else 0
374
 
 
375
  diff_x.append(s)
376
+ diff_y.append(vol_b - vol_a)
377
 
378
+ # --- Run AI Analysis ---
379
+ analysis = analyze_structure(diff_x, diff_y, mid)
380
 
381
  return web.json_response({
382
  "mid": mid,
383
  "prev_mid": market_state['prev_mid'],
384
  "vol": { "dist_bids": d_b_x, "vol_bids": d_b_y, "dist_asks": d_a_x, "vol_asks": d_a_y },
385
  "diff": { "x": diff_x, "y": diff_y },
386
+ "analysis": analysis,
387
  "history": market_state['history']
388
  })
389
 
 
407
  site = web.TCPSite(runner, '0.0.0.0', PORT)
408
  await site.start()
409
 
410
+ print(f"πŸš€ BTC-USD Predictive Dashboard: http://localhost:{PORT}")
411
  await asyncio.Event().wait()
412
 
413
  if __name__ == "__main__":