Update app.py
Browse files
app.py
CHANGED
|
@@ -11,7 +11,10 @@ SYMBOL_KRAKEN = "BTC/USD"
|
|
| 11 |
PORT = 7860
|
| 12 |
HISTORY_LENGTH = 300
|
| 13 |
BROADCAST_RATE = 0.1
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
| 15 |
IMPACT_SENSITIVITY = 0.5
|
| 16 |
|
| 17 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
|
|
@@ -20,7 +23,8 @@ market_state = {
|
|
| 20 |
"bids": {},
|
| 21 |
"asks": {},
|
| 22 |
"history": [],
|
| 23 |
-
"
|
|
|
|
| 24 |
"current_mid": 0.0,
|
| 25 |
"prev_mid": 0.0,
|
| 26 |
"ready": False
|
|
@@ -32,7 +36,8 @@ def analyze_structure(diff_x, diff_y, current_mid):
|
|
| 32 |
if not diff_y or len(diff_y) < 5:
|
| 33 |
return None
|
| 34 |
|
| 35 |
-
|
|
|
|
| 36 |
prev_vol = 0.0
|
| 37 |
|
| 38 |
for i in range(len(diff_x)):
|
|
@@ -42,21 +47,26 @@ def analyze_structure(diff_x, diff_y, current_mid):
|
|
| 42 |
marginal_vol = cum_vol - prev_vol
|
| 43 |
prev_vol = cum_vol
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
-
if
|
| 49 |
-
|
| 50 |
-
if
|
| 51 |
-
|
| 52 |
-
else:
|
| 53 |
-
impact = 0.0
|
| 54 |
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
return {
|
| 58 |
-
"
|
| 59 |
-
"
|
|
|
|
| 60 |
}
|
| 61 |
|
| 62 |
def process_market_data():
|
|
@@ -106,15 +116,20 @@ def process_market_data():
|
|
| 106 |
|
| 107 |
now = time.time()
|
| 108 |
if analysis:
|
| 109 |
-
if not market_state['
|
| 110 |
-
market_state['
|
| 111 |
-
if len(market_state['
|
| 112 |
-
market_state['
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
return {
|
| 115 |
"mid": mid,
|
| 116 |
"history": market_state['history'],
|
| 117 |
-
"
|
|
|
|
| 118 |
"depth_x": diff_x,
|
| 119 |
"depth_net": diff_y,
|
| 120 |
"depth_bids": chart_bids,
|
|
@@ -136,6 +151,7 @@ HTML_PAGE = f"""
|
|
| 136 |
--text-main: #c5c6c7;
|
| 137 |
--accent-green: #66fcf1;
|
| 138 |
--accent-red: #ff3b3b;
|
|
|
|
| 139 |
--border: #2d3842;
|
| 140 |
}}
|
| 141 |
body {{ margin: 0; padding: 0; background-color: var(--bg-color); color: var(--text-main); font-family: monospace; overflow: hidden; height: 100vh; width: 100vw; }}
|
|
@@ -168,6 +184,7 @@ HTML_PAGE = f"""
|
|
| 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>
|
|
@@ -200,12 +217,16 @@ HTML_PAGE = f"""
|
|
| 200 |
<div class="panel-header">HFT ANALYTICS ENGINE</div>
|
| 201 |
<div class="stats-content">
|
| 202 |
<div class="stat-box">
|
| 203 |
-
<span class="stat-label">WEIGHTED IMBALANCE
|
| 204 |
<span id="score-val" class="stat-value">0</span>
|
| 205 |
</div>
|
| 206 |
<div class="stat-box" style="border: 1px solid #444;">
|
| 207 |
-
<span class="stat-label" style="color
|
| 208 |
-
<span id="proj-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
</div>
|
| 210 |
</div>
|
| 211 |
</div>
|
|
@@ -218,7 +239,8 @@ HTML_PAGE = f"""
|
|
| 218 |
status: document.getElementById('loading-status'),
|
| 219 |
price: document.getElementById('live-price'),
|
| 220 |
scoreVal: document.getElementById('score-val'),
|
| 221 |
-
|
|
|
|
| 222 |
}};
|
| 223 |
|
| 224 |
const chartCommon = {{
|
|
@@ -230,28 +252,22 @@ HTML_PAGE = f"""
|
|
| 230 |
}};
|
| 231 |
|
| 232 |
const priceChart = LightweightCharts.createChart(document.getElementById('tv-price'), chartCommon);
|
|
|
|
| 233 |
const priceSeries = priceChart.addLineSeries({{ color: '#2962FF', lineWidth: 2, title: 'Price' }});
|
| 234 |
|
| 235 |
-
const
|
| 236 |
-
|
| 237 |
-
lineWidth: 1,
|
| 238 |
-
title: 'Past Prediction'
|
| 239 |
-
}});
|
| 240 |
|
| 241 |
-
const
|
| 242 |
-
|
| 243 |
-
lineWidth: 2,
|
| 244 |
-
lineStyle: 2,
|
| 245 |
-
title: 'Projection'
|
| 246 |
-
}});
|
| 247 |
|
| 248 |
const rawChart = LightweightCharts.createChart(document.getElementById('tv-raw'), {{
|
| 249 |
...chartCommon,
|
| 250 |
timeScale: {{ tickMarkFormatter: (time) => parseFloat(time).toFixed(0) }},
|
| 251 |
localization: {{ timeFormatter: (time) => 'Dist: $' + parseFloat(time).toFixed(2) }}
|
| 252 |
}});
|
| 253 |
-
const rawBidSeries = rawChart.addAreaSeries({{ lineColor: '#00e676', topColor: 'rgba(0, 230, 118, 0.2)', bottomColor: 'rgba(0, 230, 118, 0.0)', lineWidth: 2
|
| 254 |
-
const rawAskSeries = rawChart.addAreaSeries({{ lineColor: '#ff1744', topColor: 'rgba(255, 23, 68, 0.2)', bottomColor: 'rgba(255, 23, 68, 0.0)', lineWidth: 2
|
| 255 |
|
| 256 |
const netChart = LightweightCharts.createChart(document.getElementById('tv-net'), {{
|
| 257 |
...chartCommon,
|
|
@@ -295,31 +311,40 @@ HTML_PAGE = f"""
|
|
| 295 |
if (!seen.has(t)) {{ seen.add(t); cleanHistory.push({{ time: t, value: d.p }}); }}
|
| 296 |
}});
|
| 297 |
|
| 298 |
-
const
|
| 299 |
-
const
|
| 300 |
-
if(data.
|
| 301 |
-
data.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
const t = Math.floor(d.t);
|
| 303 |
-
if(!
|
| 304 |
}});
|
| 305 |
}}
|
| 306 |
|
| 307 |
if (cleanHistory.length) {{
|
| 308 |
priceSeries.setData(cleanHistory);
|
| 309 |
-
|
|
|
|
| 310 |
|
| 311 |
const last = cleanHistory[cleanHistory.length-1];
|
| 312 |
dom.price.innerText = last.value.toLocaleString(undefined, {{minimumFractionDigits: 2}});
|
| 313 |
|
| 314 |
if (data.analysis) {{
|
| 315 |
-
const {{
|
| 316 |
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
{{ time: last.time + 60, value: projected }}
|
| 320 |
-
]);
|
| 321 |
|
| 322 |
-
dom.
|
|
|
|
| 323 |
|
| 324 |
dom.scoreVal.innerText = net_score.toFixed(2);
|
| 325 |
dom.scoreVal.className = net_score > 0 ? "stat-value green" : "stat-value red";
|
|
|
|
| 11 |
PORT = 7860
|
| 12 |
HISTORY_LENGTH = 300
|
| 13 |
BROADCAST_RATE = 0.1
|
| 14 |
+
|
| 15 |
+
DECAY_LAMBDA_SHORT = 100.0
|
| 16 |
+
DECAY_LAMBDA_LONG = 500.0
|
| 17 |
+
|
| 18 |
IMPACT_SENSITIVITY = 0.5
|
| 19 |
|
| 20 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
|
|
|
|
| 23 |
"bids": {},
|
| 24 |
"asks": {},
|
| 25 |
"history": [],
|
| 26 |
+
"pred_history_short": [],
|
| 27 |
+
"pred_history_long": [],
|
| 28 |
"current_mid": 0.0,
|
| 29 |
"prev_mid": 0.0,
|
| 30 |
"ready": False
|
|
|
|
| 36 |
if not diff_y or len(diff_y) < 5:
|
| 37 |
return None
|
| 38 |
|
| 39 |
+
w_imbalance_short = 0.0
|
| 40 |
+
w_imbalance_long = 0.0
|
| 41 |
prev_vol = 0.0
|
| 42 |
|
| 43 |
for i in range(len(diff_x)):
|
|
|
|
| 47 |
marginal_vol = cum_vol - prev_vol
|
| 48 |
prev_vol = cum_vol
|
| 49 |
|
| 50 |
+
weight_short = math.exp(-dist / DECAY_LAMBDA_SHORT)
|
| 51 |
+
w_imbalance_short += marginal_vol * weight_short
|
| 52 |
+
|
| 53 |
+
weight_long = math.exp(-dist / DECAY_LAMBDA_LONG)
|
| 54 |
+
w_imbalance_long += marginal_vol * weight_long
|
| 55 |
|
| 56 |
+
if w_imbalance_short != 0:
|
| 57 |
+
impact_s = math.sqrt(abs(w_imbalance_short)) * IMPACT_SENSITIVITY
|
| 58 |
+
if w_imbalance_short < 0: impact_s = -impact_s
|
| 59 |
+
else: impact_s = 0.0
|
|
|
|
|
|
|
| 60 |
|
| 61 |
+
if w_imbalance_long != 0:
|
| 62 |
+
impact_l = math.sqrt(abs(w_imbalance_long)) * IMPACT_SENSITIVITY
|
| 63 |
+
if w_imbalance_long < 0: impact_l = -impact_l
|
| 64 |
+
else: impact_l = 0.0
|
| 65 |
|
| 66 |
return {
|
| 67 |
+
"proj_short": current_mid + impact_s,
|
| 68 |
+
"proj_long": current_mid + impact_l,
|
| 69 |
+
"net_score": w_imbalance_short
|
| 70 |
}
|
| 71 |
|
| 72 |
def process_market_data():
|
|
|
|
| 116 |
|
| 117 |
now = time.time()
|
| 118 |
if analysis:
|
| 119 |
+
if not market_state['pred_history_short'] or (now - market_state['pred_history_short'][-1]['t'] > 0.5):
|
| 120 |
+
market_state['pred_history_short'].append({'t': now, 'p': analysis['proj_short']})
|
| 121 |
+
if len(market_state['pred_history_short']) > HISTORY_LENGTH:
|
| 122 |
+
market_state['pred_history_short'].pop(0)
|
| 123 |
+
|
| 124 |
+
market_state['pred_history_long'].append({'t': now, 'p': analysis['proj_long']})
|
| 125 |
+
if len(market_state['pred_history_long']) > HISTORY_LENGTH:
|
| 126 |
+
market_state['pred_history_long'].pop(0)
|
| 127 |
|
| 128 |
return {
|
| 129 |
"mid": mid,
|
| 130 |
"history": market_state['history'],
|
| 131 |
+
"pred_history_short": market_state['pred_history_short'],
|
| 132 |
+
"pred_history_long": market_state['pred_history_long'],
|
| 133 |
"depth_x": diff_x,
|
| 134 |
"depth_net": diff_y,
|
| 135 |
"depth_bids": chart_bids,
|
|
|
|
| 151 |
--text-main: #c5c6c7;
|
| 152 |
--accent-green: #66fcf1;
|
| 153 |
--accent-red: #ff3b3b;
|
| 154 |
+
--accent-purple: #ce93d8;
|
| 155 |
--border: #2d3842;
|
| 156 |
}}
|
| 157 |
body {{ margin: 0; padding: 0; background-color: var(--bg-color); color: var(--text-main); font-family: monospace; overflow: hidden; height: 100vh; width: 100vw; }}
|
|
|
|
| 184 |
.stat-value {{ font-size: 24px; font-weight: bold; }}
|
| 185 |
.green {{ color: var(--accent-green); }}
|
| 186 |
.red {{ color: var(--accent-red); }}
|
| 187 |
+
.purple {{ color: var(--accent-purple); }}
|
| 188 |
|
| 189 |
#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); }}
|
| 190 |
</style>
|
|
|
|
| 217 |
<div class="panel-header">HFT ANALYTICS ENGINE</div>
|
| 218 |
<div class="stats-content">
|
| 219 |
<div class="stat-box">
|
| 220 |
+
<span class="stat-label">WEIGHTED IMBALANCE (SHORT)</span>
|
| 221 |
<span id="score-val" class="stat-value">0</span>
|
| 222 |
</div>
|
| 223 |
<div class="stat-box" style="border: 1px solid #444;">
|
| 224 |
+
<span class="stat-label" style="color:#aaa;">SHORT TERM TARGET</span>
|
| 225 |
+
<span id="proj-short" class="stat-value">---</span>
|
| 226 |
+
</div>
|
| 227 |
+
<div class="stat-box" style="border: 1px solid #7b1fa2;">
|
| 228 |
+
<span class="stat-label" style="color:var(--accent-purple);">LONG TERM TARGET (CENTER OF MASS)</span>
|
| 229 |
+
<span id="proj-long" class="stat-value purple">---</span>
|
| 230 |
</div>
|
| 231 |
</div>
|
| 232 |
</div>
|
|
|
|
| 239 |
status: document.getElementById('loading-status'),
|
| 240 |
price: document.getElementById('live-price'),
|
| 241 |
scoreVal: document.getElementById('score-val'),
|
| 242 |
+
projShort: document.getElementById('proj-short'),
|
| 243 |
+
projLong: document.getElementById('proj-long')
|
| 244 |
}};
|
| 245 |
|
| 246 |
const chartCommon = {{
|
|
|
|
| 252 |
}};
|
| 253 |
|
| 254 |
const priceChart = LightweightCharts.createChart(document.getElementById('tv-price'), chartCommon);
|
| 255 |
+
|
| 256 |
const priceSeries = priceChart.addLineSeries({{ color: '#2962FF', lineWidth: 2, title: 'Price' }});
|
| 257 |
|
| 258 |
+
const pastShortSeries = priceChart.addLineSeries({{ color: '#555555', lineWidth: 1, title: 'Short Term (Past)' }});
|
| 259 |
+
const futureShortSeries = priceChart.addLineSeries({{ color: '#ff9800', lineWidth: 2, lineStyle: 2, title: 'Short Term (Fut)' }});
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
+
const pastLongSeries = priceChart.addLineSeries({{ color: '#7b1fa2', lineWidth: 1, title: 'Long Term (Past)' }});
|
| 262 |
+
const futureLongSeries = priceChart.addLineSeries({{ color: '#ce93d8', lineWidth: 2, lineStyle: 2, title: 'Long Term (Fut)' }});
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
|
| 264 |
const rawChart = LightweightCharts.createChart(document.getElementById('tv-raw'), {{
|
| 265 |
...chartCommon,
|
| 266 |
timeScale: {{ tickMarkFormatter: (time) => parseFloat(time).toFixed(0) }},
|
| 267 |
localization: {{ timeFormatter: (time) => 'Dist: $' + parseFloat(time).toFixed(2) }}
|
| 268 |
}});
|
| 269 |
+
const rawBidSeries = rawChart.addAreaSeries({{ lineColor: '#00e676', topColor: 'rgba(0, 230, 118, 0.2)', bottomColor: 'rgba(0, 230, 118, 0.0)', lineWidth: 2 }});
|
| 270 |
+
const rawAskSeries = rawChart.addAreaSeries({{ lineColor: '#ff1744', topColor: 'rgba(255, 23, 68, 0.2)', bottomColor: 'rgba(255, 23, 68, 0.0)', lineWidth: 2 }});
|
| 271 |
|
| 272 |
const netChart = LightweightCharts.createChart(document.getElementById('tv-net'), {{
|
| 273 |
...chartCommon,
|
|
|
|
| 311 |
if (!seen.has(t)) {{ seen.add(t); cleanHistory.push({{ time: t, value: d.p }}); }}
|
| 312 |
}});
|
| 313 |
|
| 314 |
+
const predShort = [];
|
| 315 |
+
const seenS = new Set();
|
| 316 |
+
if(data.pred_history_short) {{
|
| 317 |
+
data.pred_history_short.forEach(d => {{
|
| 318 |
+
const t = Math.floor(d.t);
|
| 319 |
+
if(!seenS.has(t)) {{ seenS.add(t); predShort.push({{ time: t, value: d.p }}); }}
|
| 320 |
+
}});
|
| 321 |
+
}}
|
| 322 |
+
|
| 323 |
+
const predLong = [];
|
| 324 |
+
const seenL = new Set();
|
| 325 |
+
if(data.pred_history_long) {{
|
| 326 |
+
data.pred_history_long.forEach(d => {{
|
| 327 |
const t = Math.floor(d.t);
|
| 328 |
+
if(!seenL.has(t)) {{ seenL.add(t); predLong.push({{ time: t, value: d.p }}); }}
|
| 329 |
}});
|
| 330 |
}}
|
| 331 |
|
| 332 |
if (cleanHistory.length) {{
|
| 333 |
priceSeries.setData(cleanHistory);
|
| 334 |
+
pastShortSeries.setData(predShort);
|
| 335 |
+
pastLongSeries.setData(predLong);
|
| 336 |
|
| 337 |
const last = cleanHistory[cleanHistory.length-1];
|
| 338 |
dom.price.innerText = last.value.toLocaleString(undefined, {{minimumFractionDigits: 2}});
|
| 339 |
|
| 340 |
if (data.analysis) {{
|
| 341 |
+
const {{ proj_short, proj_long, net_score }} = data.analysis;
|
| 342 |
|
| 343 |
+
futureShortSeries.setData([last, {{ time: last.time + 60, value: proj_short }}]);
|
| 344 |
+
futureLongSeries.setData([last, {{ time: last.time + 300, value: proj_long }}]);
|
|
|
|
|
|
|
| 345 |
|
| 346 |
+
dom.projShort.innerText = proj_short.toLocaleString(undefined, {{minimumFractionDigits: 0, maximumFractionDigits: 0}});
|
| 347 |
+
dom.projLong.innerText = proj_long.toLocaleString(undefined, {{minimumFractionDigits: 0, maximumFractionDigits: 0}});
|
| 348 |
|
| 349 |
dom.scoreVal.innerText = net_score.toFixed(2);
|
| 350 |
dom.scoreVal.className = net_score > 0 ? "stat-value green" : "stat-value red";
|