Alvin3y1 commited on
Commit
6c45e3b
·
verified ·
1 Parent(s): c43af34

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -75
app.py CHANGED
@@ -28,7 +28,7 @@ HTML_PAGE = f"""
28
  <!DOCTYPE html>
29
  <html>
30
  <head>
31
- <title>BTC-USD 4-Way Analysis</title>
32
  <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
33
  <style>
34
  body {{ margin: 0; padding: 0; background-color: #0e0e0e; color: #ccc; font-family: sans-serif; overflow: hidden; }}
@@ -36,21 +36,21 @@ HTML_PAGE = f"""
36
  /* Layout Grid */
37
  #container {{ display: flex; flex-direction: column; height: 100vh; width: 100vw; }}
38
 
39
- /* Row 1: Depth (50%) */
40
- #row-top {{ flex: 2; width: 100%; border-bottom: 1px solid #333; }}
41
 
42
- /* Row 2: Split Middle (25%) */
43
- #row-mid {{ flex: 1; display: flex; width: 100%; border-bottom: 1px solid #333; }}
44
- #imbalance-vol {{ width: 50%; border-right: 1px solid #333; }}
45
- #imbalance-ratio {{ width: 50%; }}
46
-
47
- /* Row 3: History (25%) */
48
- #row-bot {{ flex: 1; width: 100%; }}
49
 
50
  /* Charts fill their containers */
51
  .chart {{ width: 100%; height: 100%; }}
52
 
53
- #status {{ position: absolute; top: 10px; left: 60px; z-index: 10; font-size: 14px; background: rgba(0,0,0,0.7); padding: 5px 10px; border-radius: 4px; pointer-events: none; border: 1px solid #333; }}
 
54
  .green {{ color: #00e676; }}
55
  .red {{ color: #ff1744; }}
56
  </style>
@@ -59,36 +59,49 @@ HTML_PAGE = f"""
59
  <div id="status">Connecting...</div>
60
 
61
  <div id="container">
 
62
  <div id="row-top">
63
- <div id="depth-chart" class="chart"></div>
64
- </div>
65
- <div id="row-mid">
66
- <div id="imbalance-vol" class="chart"></div>
67
- <div id="imbalance-ratio" class="chart"></div>
 
68
  </div>
 
 
69
  <div id="row-bot">
70
- <div id="price-chart" class="chart"></div>
 
 
 
 
 
71
  </div>
72
  </div>
73
 
74
  <script>
75
- const depthDiv = document.getElementById('depth-chart');
76
- const volDiv = document.getElementById('imbalance-vol');
77
- const ratioDiv = document.getElementById('imbalance-ratio');
78
  const priceDiv = document.getElementById('price-chart');
 
 
 
 
79
  const statusDiv = document.getElementById('status');
80
 
81
- let initDepth = false;
82
- let initVol = false;
83
- let initRatio = false;
84
  let initPrice = false;
 
 
 
85
 
 
86
  const commonConfig = {{ responsive: true, displayModeBar: false }};
87
  const commonLayout = {{
88
  paper_bgcolor: '#0e0e0e',
89
  plot_bgcolor: '#0e0e0e',
90
  font: {{ color: '#aaa' }},
91
- margin: {{ t: 30, b: 30, l: 50, r: 20 }},
92
  showlegend: false,
93
  xaxis: {{ gridcolor: '#222' }},
94
  yaxis: {{ gridcolor: '#222' }}
@@ -104,71 +117,84 @@ HTML_PAGE = f"""
104
  return;
105
  }}
106
 
107
- statusDiv.innerHTML = `Mid: <span class="${{data.mid >= data.prev_mid ? 'green' : 'red'}}">$${{data.mid.toLocaleString(undefined, {{minimumFractionDigits: 2}})}}</span> | Ratio: ${{data.ratio.last_val ? data.ratio.last_val.toFixed(2) : '-'}}`;
 
 
 
 
108
 
109
- // 1. DEPTH CHART
110
- if (!initDepth) {{
111
- Plotly.newPlot(depthDiv, [
112
- {{ x: data.depth.bids_x, y: data.depth.bids_y, fill: 'tozeroy', type: 'scatter', name: 'Bids', line: {{color: '#00e676'}} }},
113
- {{ x: data.depth.asks_x, y: data.depth.asks_y, fill: 'tozeroy', type: 'scatter', name: 'Asks', line: {{color: '#ff1744'}} }}
114
- ], {{ ...commonLayout, title: 'Orderbook Depth' }}, commonConfig);
115
- initDepth = true;
116
- }} else {{
117
- Plotly.react(depthDiv, [
118
- {{ x: data.depth.bids_x, y: data.depth.bids_y, fill: 'tozeroy', type: 'scatter', name: 'Bids', line: {{color: '#00e676'}} }},
119
- {{ x: data.depth.asks_x, y: data.depth.asks_y, fill: 'tozeroy', type: 'scatter', name: 'Asks', line: {{color: '#ff1744'}} }}
120
- ], {{ ...commonLayout, title: 'Orderbook Depth' }}, commonConfig);
121
- }}
122
 
123
- // 2. IMBALANCE VOL
124
- const layoutVol = {{ ...commonLayout, title: 'Liquidity (Volume vs Distance)', xaxis: {{title: 'Distance ($)', gridcolor:'#222'}} }};
125
- if (!initVol) {{
126
- Plotly.newPlot(volDiv, [
127
- {{ x: data.imbalance.dist_bids, y: data.imbalance.vol_bids, type: 'scatter', name: 'Bid Vol', line: {{color: '#00e676'}} }},
128
- {{ x: data.imbalance.dist_asks, y: data.imbalance.vol_asks, type: 'scatter', name: 'Ask Vol', line: {{color: '#ff1744'}} }}
129
- ], layoutVol, commonConfig);
130
- initVol = true;
 
 
 
 
131
  }} else {{
132
- Plotly.react(volDiv, [
133
- {{ x: data.imbalance.dist_bids, y: data.imbalance.vol_bids, type: 'scatter', name: 'Bid Vol', line: {{color: '#00e676'}} }},
134
- {{ x: data.imbalance.dist_asks, y: data.imbalance.vol_asks, type: 'scatter', name: 'Ask Vol', line: {{color: '#ff1744'}} }}
135
- ], layoutVol, commonConfig);
136
  }}
137
 
138
- // 3. IMBALANCE RATIO
 
 
139
  const layoutRatio = {{ ...commonLayout,
140
- title: 'Buy/Sell Ratio (Bid/Ask)',
141
  xaxis: {{title: 'Distance ($)', gridcolor:'#222'}},
142
  shapes: [{{ type: 'line', x0: 0, x1: 1, xref: 'paper', y0: 1, y1: 1, line: {{color: '#666', width: 1, dash:'dot'}} }}]
143
  }};
144
 
145
- // Color the line based on if it's mostly bullish or bearish?
146
- // We'll just use a neutral color or gradient. Let's use Cyan.
147
  if (!initRatio) {{
148
  Plotly.newPlot(ratioDiv, [
149
- {{ x: data.ratio.x, y: data.ratio.y, type: 'scatter', name: 'Ratio', line: {{color: '#00bcd4', width:2}} }}
150
  ], layoutRatio, commonConfig);
151
  initRatio = true;
152
  }} else {{
153
  Plotly.react(ratioDiv, [
154
- {{ x: data.ratio.x, y: data.ratio.y, type: 'scatter', name: 'Ratio', line: {{color: '#00bcd4', width:2}} }}
155
  ], layoutRatio, commonConfig);
156
  }}
157
 
158
- // 4. HISTORY
159
- const timeData = data.history.map(d => new Date(d.t * 1000));
160
- const priceData = data.history.map(d => d.p);
161
- const layoutHist = {{ ...commonLayout, title: 'Midprice History', xaxis: {{type:'date', gridcolor:'#222'}} }};
162
-
163
- if (!initPrice) {{
164
- Plotly.newPlot(priceDiv, [
165
- {{ x: timeData, y: priceData, type: 'scatter', name: 'Mid', line: {{color: '#29b6f6'}} }}
166
- ], layoutHist, commonConfig);
167
- initPrice = true;
168
  }} else {{
169
- Plotly.react(priceDiv, [
170
- {{ x: timeData, y: priceData, type: 'scatter', name: 'Mid', line: {{color: '#29b6f6'}} }}
171
- ], layoutHist, commonConfig);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  }}
173
 
174
  }} catch (e) {{ console.error("Fetch error:", e); }}
@@ -271,26 +297,21 @@ async def handle_data(request):
271
  d_a_y.append(cum_d)
272
 
273
  # --- 3. Ratio Calculation (Interp) ---
274
- # We need a common distance grid to compute Ratio = B_vol / A_vol
275
  r_x, r_y = [], []
276
  if d_b_x and d_a_x:
277
  max_dist = min(d_b_x[-1], d_a_x[-1])
278
- # Generate 100 steps
279
  step_size = max_dist / 100
280
  steps = [i * step_size for i in range(1, 101)]
281
 
282
  for s in steps:
283
- # Find B vol at distance s
284
  idx_b = bisect.bisect_right(d_b_x, s)
285
  vol_b = d_b_y[idx_b-1] if idx_b > 0 else 0
286
 
287
- # Find A vol at distance s
288
  idx_a = bisect.bisect_right(d_a_x, s)
289
  vol_a = d_a_y[idx_a-1] if idx_a > 0 else 0
290
 
291
  if vol_a > 0:
292
  ratio = vol_b / vol_a
293
- # Cap extreme ratios for display sanity
294
  if ratio > 5: ratio = 5
295
  r_x.append(s)
296
  r_y.append(ratio)
@@ -324,7 +345,7 @@ async def main():
324
  site = web.TCPSite(runner, '0.0.0.0', PORT)
325
  await site.start()
326
 
327
- print(f"🚀 BTC-USD 4-Way Dashboard: http://localhost:{PORT}")
328
  await asyncio.Event().wait()
329
 
330
  if __name__ == "__main__":
 
28
  <!DOCTYPE html>
29
  <html>
30
  <head>
31
+ <title>BTC-USD Premium Dashboard</title>
32
  <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
33
  <style>
34
  body {{ margin: 0; padding: 0; background-color: #0e0e0e; color: #ccc; font-family: sans-serif; overflow: hidden; }}
 
36
  /* Layout Grid */
37
  #container {{ display: flex; flex-direction: column; height: 100vh; width: 100vw; }}
38
 
39
+ /* Row 1: High Priority (Price & Ratio) */
40
+ #row-top {{ flex: 1; display: flex; width: 100%; border-bottom: 1px solid #333; }}
41
 
42
+ /* Row 2: Secondary Data (Depth & Imbalance) */
43
+ #row-bot {{ flex: 1; display: flex; width: 100%; }}
44
+
45
+ /* Columns */
46
+ .col-left {{ width: 50%; border-right: 1px solid #333; position: relative; }}
47
+ .col-right {{ width: 50%; position: relative; }}
 
48
 
49
  /* Charts fill their containers */
50
  .chart {{ width: 100%; height: 100%; }}
51
 
52
+ /* Status Badge */
53
+ #status {{ position: absolute; top: 10px; left: 60px; z-index: 100; font-size: 14px; background: rgba(0,0,0,0.7); padding: 5px 10px; border-radius: 4px; pointer-events: none; border: 1px solid #333; }}
54
  .green {{ color: #00e676; }}
55
  .red {{ color: #ff1744; }}
56
  </style>
 
59
  <div id="status">Connecting...</div>
60
 
61
  <div id="container">
62
+ <!-- TOP ROW: PRICE & RATIO -->
63
  <div id="row-top">
64
+ <div class="col-left">
65
+ <div id="price-chart" class="chart"></div>
66
+ </div>
67
+ <div class="col-right">
68
+ <div id="ratio-chart" class="chart"></div>
69
+ </div>
70
  </div>
71
+
72
+ <!-- BOTTOM ROW: DEPTH & IMBALANCE -->
73
  <div id="row-bot">
74
+ <div class="col-left">
75
+ <div id="depth-chart" class="chart"></div>
76
+ </div>
77
+ <div class="col-right">
78
+ <div id="imbalance-chart" class="chart"></div>
79
+ </div>
80
  </div>
81
  </div>
82
 
83
  <script>
84
+ // Chart Targets
 
 
85
  const priceDiv = document.getElementById('price-chart');
86
+ const ratioDiv = document.getElementById('ratio-chart');
87
+ const depthDiv = document.getElementById('depth-chart');
88
+ const imbDiv = document.getElementById('imbalance-chart');
89
+
90
  const statusDiv = document.getElementById('status');
91
 
92
+ // Init Flags
 
 
93
  let initPrice = false;
94
+ let initRatio = false;
95
+ let initDepth = false;
96
+ let initImb = false;
97
 
98
+ // Shared Config
99
  const commonConfig = {{ responsive: true, displayModeBar: false }};
100
  const commonLayout = {{
101
  paper_bgcolor: '#0e0e0e',
102
  plot_bgcolor: '#0e0e0e',
103
  font: {{ color: '#aaa' }},
104
+ margin: {{ t: 40, b: 30, l: 50, r: 20 }},
105
  showlegend: false,
106
  xaxis: {{ gridcolor: '#222' }},
107
  yaxis: {{ gridcolor: '#222' }}
 
117
  return;
118
  }}
119
 
120
+ // Status Header
121
+ const lastRatio = data.ratio.last_val;
122
+ let ratioColor = '#aaa';
123
+ if(lastRatio > 1.2) ratioColor = '#00e676'; // Bullish
124
+ if(lastRatio < 0.8) ratioColor = '#ff1744'; // Bearish
125
 
126
+ statusDiv.innerHTML = `Mid: <span class="${{data.mid >= data.prev_mid ? 'green' : 'red'}}">$${{data.mid.toLocaleString(undefined, {{minimumFractionDigits: 2}})}}</span> | Ratio: <span style="color:${{ratioColor}}">${{lastRatio ? lastRatio.toFixed(2) : '-'}}</span>`;
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ // ----------------------------------------------------
129
+ // 1. PRICE HISTORY (Top Left)
130
+ // ----------------------------------------------------
131
+ const timeData = data.history.map(d => new Date(d.t * 1000));
132
+ const priceData = data.history.map(d => d.p);
133
+ const layoutPrice = {{ ...commonLayout, title: '<b>Midprice History</b>', xaxis: {{type:'date', gridcolor:'#222'}} }};
134
+
135
+ if (!initPrice) {{
136
+ Plotly.newPlot(priceDiv, [
137
+ {{ x: timeData, y: priceData, type: 'scatter', mode:'lines', name: 'Mid', line: {{color: '#29b6f6', width: 2}} }}
138
+ ], layoutPrice, commonConfig);
139
+ initPrice = true;
140
  }} else {{
141
+ Plotly.react(priceDiv, [
142
+ {{ x: timeData, y: priceData, type: 'scatter', mode:'lines', name: 'Mid', line: {{color: '#29b6f6', width: 2}} }}
143
+ ], layoutPrice, commonConfig);
 
144
  }}
145
 
146
+ // ----------------------------------------------------
147
+ // 2. RATIO (Top Right)
148
+ // ----------------------------------------------------
149
  const layoutRatio = {{ ...commonLayout,
150
+ title: '<b>Buy/Sell Liquidity Ratio</b>',
151
  xaxis: {{title: 'Distance ($)', gridcolor:'#222'}},
152
  shapes: [{{ type: 'line', x0: 0, x1: 1, xref: 'paper', y0: 1, y1: 1, line: {{color: '#666', width: 1, dash:'dot'}} }}]
153
  }};
154
 
 
 
155
  if (!initRatio) {{
156
  Plotly.newPlot(ratioDiv, [
157
+ {{ x: data.ratio.x, y: data.ratio.y, type: 'scatter', mode:'lines', name: 'Ratio', line: {{color: '#00bcd4', width:2}} }}
158
  ], layoutRatio, commonConfig);
159
  initRatio = true;
160
  }} else {{
161
  Plotly.react(ratioDiv, [
162
+ {{ x: data.ratio.x, y: data.ratio.y, type: 'scatter', mode:'lines', name: 'Ratio', line: {{color: '#00bcd4', width:2}} }}
163
  ], layoutRatio, commonConfig);
164
  }}
165
 
166
+ // ----------------------------------------------------
167
+ // 3. DEPTH CHART (Bottom Left)
168
+ // ----------------------------------------------------
169
+ const layoutDepth = {{ ...commonLayout, title: 'Orderbook Depth', xaxis: {{title: 'Price', gridcolor:'#222'}} }};
170
+ if (!initDepth) {{
171
+ Plotly.newPlot(depthDiv, [
172
+ {{ x: data.depth.bids_x, y: data.depth.bids_y, fill: 'tozeroy', type: 'scatter', name: 'Bids', line: {{color: '#00e676'}} }},
173
+ {{ x: data.depth.asks_x, y: data.depth.asks_y, fill: 'tozeroy', type: 'scatter', name: 'Asks', line: {{color: '#ff1744'}} }}
174
+ ], layoutDepth, commonConfig);
175
+ initDepth = true;
176
  }} else {{
177
+ Plotly.react(depthDiv, [
178
+ {{ x: data.depth.bids_x, y: data.depth.bids_y, fill: 'tozeroy', type: 'scatter', name: 'Bids', line: {{color: '#00e676'}} }},
179
+ {{ x: data.depth.asks_x, y: data.depth.asks_y, fill: 'tozeroy', type: 'scatter', name: 'Asks', line: {{color: '#ff1744'}} }}
180
+ ], layoutDepth, commonConfig);
181
+ }}
182
+
183
+ // ----------------------------------------------------
184
+ // 4. IMBALANCE VOL (Bottom Right)
185
+ // ----------------------------------------------------
186
+ const layoutImb = {{ ...commonLayout, title: 'Volume by Distance', xaxis: {{title: 'Distance ($)', gridcolor:'#222'}} }};
187
+ if (!initImb) {{
188
+ Plotly.newPlot(imbDiv, [
189
+ {{ x: data.imbalance.dist_bids, y: data.imbalance.vol_bids, type: 'scatter', name: 'Bid Vol', line: {{color: '#00e676'}} }},
190
+ {{ x: data.imbalance.dist_asks, y: data.imbalance.vol_asks, type: 'scatter', name: 'Ask Vol', line: {{color: '#ff1744'}} }}
191
+ ], layoutImb, commonConfig);
192
+ initImb = true;
193
+ }} else {{
194
+ Plotly.react(imbDiv, [
195
+ {{ x: data.imbalance.dist_bids, y: data.imbalance.vol_bids, type: 'scatter', name: 'Bid Vol', line: {{color: '#00e676'}} }},
196
+ {{ x: data.imbalance.dist_asks, y: data.imbalance.vol_asks, type: 'scatter', name: 'Ask Vol', line: {{color: '#ff1744'}} }}
197
+ ], layoutImb, commonConfig);
198
  }}
199
 
200
  }} catch (e) {{ console.error("Fetch error:", e); }}
 
297
  d_a_y.append(cum_d)
298
 
299
  # --- 3. Ratio Calculation (Interp) ---
 
300
  r_x, r_y = [], []
301
  if d_b_x and d_a_x:
302
  max_dist = min(d_b_x[-1], d_a_x[-1])
 
303
  step_size = max_dist / 100
304
  steps = [i * step_size for i in range(1, 101)]
305
 
306
  for s in steps:
 
307
  idx_b = bisect.bisect_right(d_b_x, s)
308
  vol_b = d_b_y[idx_b-1] if idx_b > 0 else 0
309
 
 
310
  idx_a = bisect.bisect_right(d_a_x, s)
311
  vol_a = d_a_y[idx_a-1] if idx_a > 0 else 0
312
 
313
  if vol_a > 0:
314
  ratio = vol_b / vol_a
 
315
  if ratio > 5: ratio = 5
316
  r_x.append(s)
317
  r_y.append(ratio)
 
345
  site = web.TCPSite(runner, '0.0.0.0', PORT)
346
  await site.start()
347
 
348
+ print(f"🚀 BTC-USD Premium Dashboard: http://localhost:{PORT}")
349
  await asyncio.Event().wait()
350
 
351
  if __name__ == "__main__":