Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,6 @@ import json
|
|
| 3 |
import logging
|
| 4 |
import time
|
| 5 |
import bisect
|
| 6 |
-
import random
|
| 7 |
from aiohttp import web
|
| 8 |
import websockets
|
| 9 |
|
|
@@ -22,89 +21,116 @@ market_state = {
|
|
| 22 |
"history": [],
|
| 23 |
"current_mid": 0.0,
|
| 24 |
"prev_mid": 0.0,
|
| 25 |
-
"ready": False
|
| 26 |
-
"last_comment_time": 0
|
| 27 |
}
|
| 28 |
|
| 29 |
# --- AI Logic Helper ---
|
| 30 |
-
def generate_ai_commentary(diff_y
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
|
| 40 |
-
# 3. Logic Engine
|
| 41 |
msg = ""
|
| 42 |
sentiment = "neutral"
|
| 43 |
-
|
| 44 |
-
# -- SCENARIO 1: EXTREME IMBALANCE --
|
| 45 |
-
if net_total > 50:
|
| 46 |
-
sentiment = "bullish"
|
| 47 |
-
msg = f"π <b>STRONG BUY WALL:</b> Net surplus of {int(net_total)} BTC. Orderbook tilted heavily to Bids."
|
| 48 |
-
elif net_total < -50:
|
| 49 |
-
sentiment = "bearish"
|
| 50 |
-
msg = f"π <b>HEAVY SELL WALL:</b> Net deficit of {int(net_total)} BTC. Sellers dominating."
|
| 51 |
-
|
| 52 |
-
# -- SCENARIO 2: ABSORPTION (Price moving vs Liquidity) --
|
| 53 |
-
elif price_delta < -5 and net_total > 20:
|
| 54 |
-
sentiment = "warning"
|
| 55 |
-
msg = f"π‘οΈ <b>ABSORPTION:</b> Price dropping, but Buyers are stepping in (+{int(net_total)} BTC surplus)."
|
| 56 |
-
elif price_delta > 5 and net_total < -20:
|
| 57 |
-
sentiment = "warning"
|
| 58 |
-
msg = f"π§± <b>RESISTANCE:</b> Price rising into Sell liquidity ({int(net_total)} BTC deficit)."
|
| 59 |
|
| 60 |
-
#
|
| 61 |
-
elif abs(net_total) < 10:
|
| 62 |
-
sentiment = "neutral"
|
| 63 |
-
msg = f"βοΈ <b>EQUILIBRIUM:</b> Liquidity is balanced. Waiting for a breakout trigger."
|
| 64 |
|
| 65 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
else:
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
else:
|
| 71 |
-
msg =
|
| 72 |
-
sentiment = "bearish"
|
| 73 |
|
| 74 |
-
return {"text": msg, "sentiment": sentiment
|
| 75 |
|
| 76 |
# --- HTML Frontend ---
|
| 77 |
HTML_PAGE = f"""
|
| 78 |
<!DOCTYPE html>
|
| 79 |
<html>
|
| 80 |
<head>
|
| 81 |
-
<title>BTC-USD AI
|
| 82 |
<script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
|
| 83 |
<style>
|
| 84 |
body {{ margin: 0; padding: 0; background-color: #0e0e0e; color: #ccc; font-family: 'Courier New', monospace; overflow: hidden; }}
|
| 85 |
|
| 86 |
-
/* Layout Grid */
|
| 87 |
#container {{ display: flex; flex-direction: column; height: 100vh; width: 100vw; }}
|
| 88 |
|
| 89 |
-
/* Row 1:
|
| 90 |
#row-top {{ flex: 1; display: flex; width: 100%; border-bottom: 1px solid #333; }}
|
| 91 |
|
| 92 |
-
/* Row 2:
|
| 93 |
#row-bot {{ flex: 1; display: flex; width: 100%; }}
|
| 94 |
|
| 95 |
.col {{ width: 50%; height: 100%; border-right: 1px solid #333; position: relative; }}
|
| 96 |
-
.col-ai {{ width: 50%; height: 100%; background-color: #
|
| 97 |
|
| 98 |
-
/*
|
| 99 |
-
.terminal-header {{ color: #00bcd4; font-weight: bold; border-bottom: 1px dashed #333; padding-bottom: 5px; margin-bottom: 10px; }}
|
| 100 |
-
.log-entry {{ margin-bottom:
|
| 101 |
-
.log-time {{ color: #555; font-size: 11px; margin-
|
| 102 |
|
| 103 |
/* Sentiments */
|
| 104 |
-
.bullish {{ border-left-color: #00e676;
|
| 105 |
-
.bearish {{ border-left-color: #ff1744;
|
| 106 |
-
.neutral {{ border-left-color: #999;
|
| 107 |
-
.warning {{ border-left-color: #ff9800;
|
| 108 |
|
| 109 |
.chart {{ width: 100%; height: 100%; }}
|
| 110 |
|
|
@@ -135,7 +161,7 @@ HTML_PAGE = f"""
|
|
| 135 |
</div>
|
| 136 |
<div class="col-ai">
|
| 137 |
<!-- AI TERMINAL -->
|
| 138 |
-
<div class="terminal-header">>
|
| 139 |
<div id="terminal-logs"></div>
|
| 140 |
</div>
|
| 141 |
</div>
|
|
@@ -163,6 +189,7 @@ HTML_PAGE = f"""
|
|
| 163 |
}};
|
| 164 |
|
| 165 |
function addLog(data) {{
|
|
|
|
| 166 |
if (data.comment.text === lastLogText) return;
|
| 167 |
lastLogText = data.comment.text;
|
| 168 |
|
|
@@ -172,7 +199,8 @@ HTML_PAGE = f"""
|
|
| 172 |
div.innerHTML = `<span class="log-time">${{timeStr}}</span> ${{data.comment.text}}`;
|
| 173 |
|
| 174 |
termLogs.prepend(div);
|
| 175 |
-
|
|
|
|
| 176 |
}}
|
| 177 |
|
| 178 |
async function updateCharts() {{
|
|
@@ -200,7 +228,7 @@ HTML_PAGE = f"""
|
|
| 200 |
if (!initVol) {{ Plotly.newPlot(volDiv, tracesVol, {{ ...commonLayout, title: '<b>Cumulative Volume</b>', xaxis: {{title:'Distance ($)'}} }}, commonConfig); initVol = true; }}
|
| 201 |
else {{ Plotly.react(volDiv, tracesVol, {{ ...commonLayout, title: '<b>Cumulative Volume</b>', xaxis: {{title:'Distance ($)'}} }}, commonConfig); }}
|
| 202 |
|
| 203 |
-
// 3. Net Difference (
|
| 204 |
const traceDiff = {{
|
| 205 |
x: data.diff.x,
|
| 206 |
y: data.diff.y,
|
|
@@ -209,8 +237,16 @@ HTML_PAGE = f"""
|
|
| 209 |
fill: 'tozeroy',
|
| 210 |
line: {{color: '#e040fb', width: 2}}
|
| 211 |
}};
|
| 212 |
-
|
| 213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
// 4. AI Log
|
| 216 |
addLog(data);
|
|
@@ -218,7 +254,7 @@ HTML_PAGE = f"""
|
|
| 218 |
}} catch (e) {{ console.error("Fetch error:", e); }}
|
| 219 |
}}
|
| 220 |
|
| 221 |
-
setInterval(updateCharts,
|
| 222 |
</script>
|
| 223 |
</body>
|
| 224 |
</html>
|
|
@@ -317,7 +353,7 @@ async def handle_data(request):
|
|
| 317 |
diff_y.append(val)
|
| 318 |
|
| 319 |
# --- Generate AI Commentary ---
|
| 320 |
-
ai_output = generate_ai_commentary(diff_y
|
| 321 |
|
| 322 |
return web.json_response({
|
| 323 |
"mid": mid,
|
|
@@ -348,7 +384,7 @@ async def main():
|
|
| 348 |
site = web.TCPSite(runner, '0.0.0.0', PORT)
|
| 349 |
await site.start()
|
| 350 |
|
| 351 |
-
print(f"π BTC-USD
|
| 352 |
await asyncio.Event().wait()
|
| 353 |
|
| 354 |
if __name__ == "__main__":
|
|
|
|
| 3 |
import logging
|
| 4 |
import time
|
| 5 |
import bisect
|
|
|
|
| 6 |
from aiohttp import web
|
| 7 |
import websockets
|
| 8 |
|
|
|
|
| 21 |
"history": [],
|
| 22 |
"current_mid": 0.0,
|
| 23 |
"prev_mid": 0.0,
|
| 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 |
|
|
|
|
| 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>
|
|
|
|
| 189 |
}};
|
| 190 |
|
| 191 |
function addLog(data) {{
|
| 192 |
+
// De-duplicate messages
|
| 193 |
if (data.comment.text === lastLogText) return;
|
| 194 |
lastLogText = data.comment.text;
|
| 195 |
|
|
|
|
| 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() {{
|
|
|
|
| 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,
|
|
|
|
| 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);
|
|
|
|
| 254 |
}} catch (e) {{ console.error("Fetch error:", e); }}
|
| 255 |
}}
|
| 256 |
|
| 257 |
+
setInterval(updateCharts, 1000);
|
| 258 |
</script>
|
| 259 |
</body>
|
| 260 |
</html>
|
|
|
|
| 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,
|
|
|
|
| 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__":
|