Alvin3y1 commited on
Commit
3a80860
·
verified ·
1 Parent(s): bfed924

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -5
app.py CHANGED
@@ -4,6 +4,7 @@ import logging
4
  import time
5
  import bisect
6
  import math
 
7
  from aiohttp import web
8
  import websockets
9
 
@@ -14,6 +15,10 @@ BROADCAST_RATE = 0.1
14
  DECAY_LAMBDA = 100.0
15
  IMPACT_SENSITIVITY = 0.5
16
 
 
 
 
 
17
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
18
 
19
  market_state = {
@@ -28,6 +33,49 @@ market_state = {
28
 
29
  connected_clients = set()
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  def analyze_structure(diff_x, diff_y, current_mid):
32
  if not diff_y or len(diff_y) < 5:
33
  return None
@@ -65,8 +113,19 @@ def process_market_data():
65
 
66
  mid = market_state['current_mid']
67
 
68
- raw_bids = sorted(market_state['bids'].items(), key=lambda x: -x[0])[:300]
69
- raw_asks = sorted(market_state['asks'].items(), key=lambda x: x[0])[:300]
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  d_b_x, d_b_y, cum = [], [], 0
72
  for p, q in raw_bids:
@@ -119,7 +178,11 @@ def process_market_data():
119
  "depth_net": diff_y,
120
  "depth_bids": chart_bids,
121
  "depth_asks": chart_asks,
122
- "analysis": analysis
 
 
 
 
123
  }
124
 
125
  HTML_PAGE = f"""
@@ -166,10 +229,13 @@ HTML_PAGE = f"""
166
  .stat-box {{ margin-bottom: 20px; padding: 10px; background: rgba(255,255,255,0.02); border-radius: 4px; }}
167
  .stat-label {{ font-size: 11px; color: #666; display: block; margin-bottom: 4px; }}
168
  .stat-value {{ font-size: 24px; font-weight: bold; }}
 
169
  .green {{ color: var(--accent-green); }}
170
  .red {{ color: var(--accent-red); }}
171
 
172
  #loader {{ position: absolute; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.95); z-index: 999; display: flex; flex-direction: column; justify-content: center; align-items: center; color: var(--accent-green); }}
 
 
173
  </style>
174
  </head>
175
  <body>
@@ -181,7 +247,7 @@ HTML_PAGE = f"""
181
 
182
  <div class="grid-container">
183
  <div id="p-price" class="panel">
184
- <div class="panel-header"><span>BTC/USD Price Action</span><span id="live-price">---</span></div>
185
  <div id="tv-price"></div>
186
  </div>
187
 
@@ -207,6 +273,11 @@ HTML_PAGE = f"""
207
  <span class="stat-label" style="color:var(--accent-green);">IMPACT PROJECTION</span>
208
  <span id="proj-val" class="stat-value">---</span>
209
  </div>
 
 
 
 
 
210
  </div>
211
  </div>
212
  </div>
@@ -218,7 +289,8 @@ HTML_PAGE = f"""
218
  status: document.getElementById('loading-status'),
219
  price: document.getElementById('live-price'),
220
  scoreVal: document.getElementById('score-val'),
221
- projVal: document.getElementById('proj-val')
 
222
  }};
223
 
224
  const chartCommon = {{
@@ -265,6 +337,9 @@ HTML_PAGE = f"""
265
  lineWidth: 2
266
  }});
267
 
 
 
 
268
  const resizeObserver = new ResizeObserver(entries => {{
269
  for(let entry of entries) {{
270
  const {{width, height}} = entry.contentRect;
@@ -288,6 +363,7 @@ HTML_PAGE = f"""
288
  if (data.error) return;
289
  dom.loader.style.display = 'none';
290
 
 
291
  const cleanHistory = [];
292
  const seen = new Set();
293
  data.history.forEach(d => {{
@@ -326,6 +402,46 @@ HTML_PAGE = f"""
326
  }}
327
  }}
328
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  if (data.depth_x && data.depth_x.length) {{
330
  const netData = [];
331
  const rawBids = [], rawAsks = [];
 
4
  import time
5
  import bisect
6
  import math
7
+ import statistics
8
  from aiohttp import web
9
  import websockets
10
 
 
15
  DECAY_LAMBDA = 100.0
16
  IMPACT_SENSITIVITY = 0.5
17
 
18
+ # Wall Detection Parameters
19
+ Z_SCORE_THRESHOLD = 3.0 # Statistical significance threshold (3 sigma)
20
+ WALL_LOOKBACK = 200 # How many order book levels to scan
21
+
22
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
23
 
24
  market_state = {
 
33
 
34
  connected_clients = set()
35
 
36
+ def detect_anomalies(orders, scan_depth):
37
+ """
38
+ Mathematically detects liquidity walls using Z-Score outlier detection.
39
+ Returns the top significant walls.
40
+ """
41
+ if len(orders) < 10:
42
+ return []
43
+
44
+ # Get volumes for the scan depth
45
+ relevant_orders = orders[:scan_depth]
46
+ volumes = [q for p, q in relevant_orders]
47
+
48
+ if not volumes:
49
+ return []
50
+
51
+ # Calculate Distribution Statistics
52
+ try:
53
+ avg_vol = statistics.mean(volumes)
54
+ stdev_vol = statistics.stdev(volumes)
55
+ except statistics.StatisticsError:
56
+ return []
57
+
58
+ if stdev_vol == 0:
59
+ return []
60
+
61
+ walls = []
62
+
63
+ for price, qty in relevant_orders:
64
+ # Calculate Z-Score
65
+ z_score = (qty - avg_vol) / stdev_vol
66
+
67
+ # If Z-Score > Threshold, it is a mathematically significant wall
68
+ if z_score > Z_SCORE_THRESHOLD:
69
+ walls.append({
70
+ "price": price,
71
+ "vol": qty,
72
+ "z_score": z_score
73
+ })
74
+
75
+ # Sort by Z-Score descending (strongest walls first) and take top 3
76
+ walls.sort(key=lambda x: x['z_score'], reverse=True)
77
+ return walls[:3]
78
+
79
  def analyze_structure(diff_x, diff_y, current_mid):
80
  if not diff_y or len(diff_y) < 5:
81
  return None
 
113
 
114
  mid = market_state['current_mid']
115
 
116
+ # Sort orders for processing
117
+ # Bids: High to Low
118
+ sorted_bids = sorted(market_state['bids'].items(), key=lambda x: -x[0])
119
+ # Asks: Low to High
120
+ sorted_asks = sorted(market_state['asks'].items(), key=lambda x: x[0])
121
+
122
+ raw_bids = sorted_bids[:300]
123
+ raw_asks = sorted_asks[:300]
124
+
125
+ # --- WALL DETECTION LOGIC ---
126
+ bid_walls = detect_anomalies(sorted_bids, WALL_LOOKBACK)
127
+ ask_walls = detect_anomalies(sorted_asks, WALL_LOOKBACK)
128
+ # ----------------------------
129
 
130
  d_b_x, d_b_y, cum = [], [], 0
131
  for p, q in raw_bids:
 
178
  "depth_net": diff_y,
179
  "depth_bids": chart_bids,
180
  "depth_asks": chart_asks,
181
+ "analysis": analysis,
182
+ "walls": {
183
+ "bids": bid_walls,
184
+ "asks": ask_walls
185
+ }
186
  }
187
 
188
  HTML_PAGE = f"""
 
229
  .stat-box {{ margin-bottom: 20px; padding: 10px; background: rgba(255,255,255,0.02); border-radius: 4px; }}
230
  .stat-label {{ font-size: 11px; color: #666; display: block; margin-bottom: 4px; }}
231
  .stat-value {{ font-size: 24px; font-weight: bold; }}
232
+ .stat-sub {{ font-size: 10px; color: #888; margin-top: 5px; }}
233
  .green {{ color: var(--accent-green); }}
234
  .red {{ color: var(--accent-red); }}
235
 
236
  #loader {{ position: absolute; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.95); z-index: 999; display: flex; flex-direction: column; justify-content: center; align-items: center; color: var(--accent-green); }}
237
+
238
+ .wall-item {{ display: flex; justify-content: space-between; font-size: 11px; margin-bottom: 4px; padding: 4px; background: rgba(0,0,0,0.2); }}
239
  </style>
240
  </head>
241
  <body>
 
247
 
248
  <div class="grid-container">
249
  <div id="p-price" class="panel">
250
+ <div class="panel-header"><span>BTC/USD Price + Liquidity Walls</span><span id="live-price">---</span></div>
251
  <div id="tv-price"></div>
252
  </div>
253
 
 
273
  <span class="stat-label" style="color:var(--accent-green);">IMPACT PROJECTION</span>
274
  <span id="proj-val" class="stat-value">---</span>
275
  </div>
276
+
277
+ <div class="stat-box">
278
+ <span class="stat-label">DETECTED WALLS (Z-SCORE > 3)</span>
279
+ <div id="wall-list">Waiting for data...</div>
280
+ </div>
281
  </div>
282
  </div>
283
  </div>
 
289
  status: document.getElementById('loading-status'),
290
  price: document.getElementById('live-price'),
291
  scoreVal: document.getElementById('score-val'),
292
+ projVal: document.getElementById('proj-val'),
293
+ wallList: document.getElementById('wall-list')
294
  }};
295
 
296
  const chartCommon = {{
 
337
  lineWidth: 2
338
  }});
339
 
340
+ // Refs for Price Lines (Walls)
341
+ let activePriceLines = [];
342
+
343
  const resizeObserver = new ResizeObserver(entries => {{
344
  for(let entry of entries) {{
345
  const {{width, height}} = entry.contentRect;
 
363
  if (data.error) return;
364
  dom.loader.style.display = 'none';
365
 
366
+ // --- History & Prediction Plotting ---
367
  const cleanHistory = [];
368
  const seen = new Set();
369
  data.history.forEach(d => {{
 
402
  }}
403
  }}
404
 
405
+ // --- Wall Visualization ---
406
+ if (data.walls) {{
407
+ // Clear old lines
408
+ activePriceLines.forEach(line => priceSeries.removePriceLine(line));
409
+ activePriceLines = [];
410
+
411
+ let wallHtml = "";
412
+
413
+ // Draw Bid Walls
414
+ data.walls.bids.forEach(w => {{
415
+ const line = priceSeries.createPriceLine({{
416
+ price: w.price,
417
+ color: 'rgba(0, 230, 118, 0.8)',
418
+ lineWidth: 1,
419
+ lineStyle: 2,
420
+ axisLabelVisible: true,
421
+ title: `BUY WALL (Z: ${{w.z_score.toFixed(1)}})`,
422
+ }});
423
+ activePriceLines.push(line);
424
+ wallHtml += `<div class="wall-item"><span class="green">BUY ${{w.price}}</span><span>Z: ${{w.z_score.toFixed(1)}}</span></div>`;
425
+ }});
426
+
427
+ // Draw Ask Walls
428
+ data.walls.asks.forEach(w => {{
429
+ const line = priceSeries.createPriceLine({{
430
+ price: w.price,
431
+ color: 'rgba(255, 23, 68, 0.8)',
432
+ lineWidth: 1,
433
+ lineStyle: 2,
434
+ axisLabelVisible: true,
435
+ title: `SELL WALL (Z: ${{w.z_score.toFixed(1)}})`,
436
+ }});
437
+ activePriceLines.push(line);
438
+ wallHtml += `<div class="wall-item"><span class="red">SELL ${{w.price}}</span><span>Z: ${{w.z_score.toFixed(1)}}</span></div>`;
439
+ }});
440
+
441
+ dom.wallList.innerHTML = wallHtml || "No significant walls.";
442
+ }}
443
+
444
+ // --- Depth Charts ---
445
  if (data.depth_x && data.depth_x.length) {{
446
  const netData = [];
447
  const rawBids = [], rawAsks = [];