Alvin3y1 commited on
Commit
b3cc425
·
verified ·
1 Parent(s): ad85368

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +19 -208
app.py CHANGED
@@ -108,9 +108,9 @@ def calculate_polr(bids, asks, mid):
108
 
109
  path_points = []
110
 
111
- # MODIFIED: Reduced count to 15 distinct lines for performance/clarity
112
- # Spaced out volume steps to capture depth
113
- volume_steps = [i * 1.5 for i in range(1, 16)]
114
 
115
  current_time = time.time()
116
 
@@ -143,16 +143,21 @@ def calculate_polr(bids, asks, mid):
143
 
144
  projected_p = mid
145
  if ask_cost_dist > bid_cost_dist:
146
- # UP is harder, Down is easier -> Price goes UP (slip is normally against liquidity, but POLR suggests price follows ease)
147
- # Actually, conventionally POLR means price goes where resistance is lowest.
148
- # If Ask side is THICK (High dist), and Bid side is THIN (Low dist), price falls.
149
- # Logic revised for visualization: We project where price SLIPS to.
 
 
 
 
 
150
  projected_p = target_ask_price
151
  else:
152
  projected_p = target_bid_price
153
 
154
  path_points.append({
155
- 'index': i, # Mark the horizon index
156
  'p': projected_p
157
  })
158
 
@@ -391,7 +396,7 @@ HTML_PAGE = f"""
391
 
392
  <div id="p-chart" class="panel">
393
  <div class="chart-header">
394
- PRICE (BLUE) // <span class="c-purp">POLR RIVER (PURPLE FADE)</span> // <span style="color:var(--yellow)">PRED (YELLOW)</span>
395
  </div>
396
  <div id="tv-price" style="flex: 1; width: 100%;"></div>
397
  </div>
@@ -471,13 +476,13 @@ HTML_PAGE = f"""
471
  const priceChart = LightweightCharts.createChart(document.getElementById('tv-price'), chartOpts);
472
 
473
  // --- POLR RIVER INITIALIZATION ---
474
- // We create an array of series. Index 0 = Nearest Term, Index N = Long Term
475
  const polrLines = [];
476
- const polrCount = 15; // Matches backend
477
 
478
  for(let i=0; i<polrCount; i++) {{
479
- // Calculate opacity: Nearest = 1.0, Furthest = 0.1
480
- const opacity = 1.0 - (i / (polrCount + 2));
481
  const color = `rgba(213, 0, 249, ${{opacity.toFixed(2)}})`; // Purple fade
482
 
483
  polrLines.push(
@@ -578,10 +583,7 @@ HTML_PAGE = f"""
578
  }}
579
 
580
  // 2. Purple River (POLR)
581
- // We update each line individually based on the received array
582
  if (data.polr && data.polr.length) {{
583
- // We use .update() here to build the history of the prediction lines
584
- // corresponding to the current time tick
585
  data.polr.forEach((point, index) => {{
586
  if (index < polrLines.length) {{
587
  polrLines[index].update({{
@@ -656,195 +658,4 @@ HTML_PAGE = f"""
656
  }});
657
  </script>
658
  </body>
659
- </html>
660
- """
661
-
662
- async def kraken_worker():
663
- global market_state
664
- try:
665
- # Fetch initial history
666
- async with aiohttp.ClientSession() as session:
667
- url = "https://api.kraken.com/0/public/OHLC?pair=XBTUSD&interval=1"
668
- async with session.get(url) as response:
669
- if response.status == 200:
670
- data = await response.json()
671
- if 'result' in data:
672
- for key in data['result']:
673
- if key != 'last':
674
- raw_candles = data['result'][key]
675
- market_state['ohlc_history'] = [
676
- {
677
- 'time': int(c[0]),
678
- 'open': float(c[1]),
679
- 'high': float(c[2]),
680
- 'low': float(c[3]),
681
- 'close': float(c[4])
682
- }
683
- for c in raw_candles[-120:]
684
- ]
685
- break
686
- except Exception as e:
687
- logging.error(f"History fetch failed: {e}")
688
-
689
- while True:
690
- try:
691
- async with websockets.connect("wss://ws.kraken.com/v2") as ws:
692
- logging.info(f"🔌 Connected to Kraken ({SYMBOL_KRAKEN})")
693
-
694
- await ws.send(json.dumps({
695
- "method": "subscribe",
696
- "params": {"channel": "book", "symbol": [SYMBOL_KRAKEN], "depth": 500}
697
- }))
698
- await ws.send(json.dumps({
699
- "method": "subscribe",
700
- "params": {"channel": "trade", "symbol": [SYMBOL_KRAKEN]}
701
- }))
702
- await ws.send(json.dumps({
703
- "method": "subscribe",
704
- "params": {"channel": "ohlc", "symbol": [SYMBOL_KRAKEN], "interval": 1}
705
- }))
706
-
707
- async for message in ws:
708
- payload = json.loads(message)
709
- channel = payload.get("channel")
710
- data = payload.get("data", [])
711
-
712
- if channel == "book":
713
- for item in data:
714
- for bid in item.get('bids', []):
715
- q, p = float(bid['qty']), float(bid['price'])
716
- if q == 0: market_state['bids'].pop(p, None)
717
- else: market_state['bids'][p] = q
718
- for ask in item.get('asks', []):
719
- q, p = float(ask['qty']), float(ask['price'])
720
- if q == 0: market_state['asks'].pop(p, None)
721
- else: market_state['asks'][p] = q
722
-
723
- if market_state['bids'] and market_state['asks']:
724
- best_bid = max(market_state['bids'].keys())
725
- best_ask = min(market_state['asks'].keys())
726
- mid = (best_bid + best_ask) / 2
727
- market_state['prev_mid'] = market_state['current_mid']
728
- market_state['current_mid'] = mid
729
- market_state['ready'] = True
730
-
731
- now = time.time()
732
- if not market_state['history'] or (now - market_state['history'][-1]['t'] > 0.5):
733
- market_state['history'].append({'t': now, 'p': mid})
734
- if len(market_state['history']) > HISTORY_LENGTH:
735
- market_state['history'].pop(0)
736
-
737
- elif channel == "trade":
738
- for trade in data:
739
- try:
740
- qty = float(trade['qty'])
741
- price = float(trade['price'])
742
- side = trade['side']
743
-
744
- # Update Vol stats
745
- if side == 'buy': market_state['current_vol_window']['buy'] += qty
746
- else: market_state['current_vol_window']['sell'] += qty
747
-
748
- # LIVE CANDLE UPDATE
749
- current_minute_start = int(time.time()) // 60 * 60
750
-
751
- if market_state['ohlc_history']:
752
- last_candle = market_state['ohlc_history'][-1]
753
-
754
- # If still in the same minute
755
- if last_candle['time'] == current_minute_start:
756
- last_candle['close'] = price
757
- if price > last_candle['high']: last_candle['high'] = price
758
- if price < last_candle['low']: last_candle['low'] = price
759
-
760
- # If new minute started
761
- elif current_minute_start > last_candle['time']:
762
- new_candle = {
763
- 'time': current_minute_start,
764
- 'open': price,
765
- 'high': price,
766
- 'low': price,
767
- 'close': price
768
- }
769
- market_state['ohlc_history'].append(new_candle)
770
- if len(market_state['ohlc_history']) > 200:
771
- market_state['ohlc_history'].pop(0)
772
- except: pass
773
-
774
- elif channel == "ohlc":
775
- for candle in data:
776
- try:
777
- # Kraken sends endtime, adjust to starttime
778
- start_time = int(float(candle['endtime'])) - 60
779
- c_data = {
780
- 'time': start_time,
781
- 'open': float(candle['open']),
782
- 'high': float(candle['high']),
783
- 'low': float(candle['low']),
784
- 'close': float(candle['close'])
785
- }
786
-
787
- if market_state['ohlc_history']:
788
- if market_state['ohlc_history'][-1]['time'] == start_time:
789
- market_state['ohlc_history'][-1] = c_data
790
- elif market_state['ohlc_history'][-1]['time'] < start_time:
791
- market_state['ohlc_history'].append(c_data)
792
- if len(market_state['ohlc_history']) > 200:
793
- market_state['ohlc_history'].pop(0)
794
- except Exception as e:
795
- pass
796
-
797
- except Exception as e:
798
- logging.warning(f"⚠️ Reconnecting: {e}")
799
- await asyncio.sleep(3)
800
-
801
- async def broadcast_worker():
802
- while True:
803
- if connected_clients and market_state['ready']:
804
- payload = process_market_data()
805
- msg = json.dumps(payload)
806
- for ws in list(connected_clients):
807
- try: await ws.send_str(msg)
808
- except: pass
809
- await asyncio.sleep(BROADCAST_RATE)
810
-
811
- async def websocket_handler(request):
812
- ws = web.WebSocketResponse()
813
- await ws.prepare(request)
814
- connected_clients.add(ws)
815
- try:
816
- async for msg in ws:
817
- pass
818
- finally:
819
- connected_clients.remove(ws)
820
- return ws
821
-
822
- async def handle_index(request):
823
- return web.Response(text=HTML_PAGE, content_type='text/html')
824
-
825
- async def start_background(app):
826
- app['kraken_task'] = asyncio.create_task(kraken_worker())
827
- app['broadcast_task'] = asyncio.create_task(broadcast_worker())
828
-
829
- async def cleanup_background(app):
830
- app['kraken_task'].cancel()
831
- app['broadcast_task'].cancel()
832
- try: await app['kraken_task']; await app['broadcast_task']
833
- except: pass
834
-
835
- async def main():
836
- app = web.Application()
837
- app.router.add_get('/', handle_index)
838
- app.router.add_get('/ws', websocket_handler)
839
- app.on_startup.append(start_background)
840
- app.on_cleanup.append(cleanup_background)
841
- runner = web.AppRunner(app)
842
- await runner.setup()
843
- site = web.TCPSite(runner, '0.0.0.0', PORT)
844
- await site.start()
845
- print(f"🚀 Quant Dashboard: http://localhost:{PORT}")
846
- await asyncio.Event().wait()
847
-
848
- if __name__ == "__main__":
849
- try: asyncio.run(main())
850
- except KeyboardInterrupt: pass
 
108
 
109
  path_points = []
110
 
111
+ # MODIFIED: Increased to 60 steps for high resolution
112
+ # Steps from 0.5 BTC up to 30.0 BTC
113
+ volume_steps = [i * 0.5 for i in range(1, 61)]
114
 
115
  current_time = time.time()
116
 
 
143
 
144
  projected_p = mid
145
  if ask_cost_dist > bid_cost_dist:
146
+ # UP is harder (more distance for same vol), so price slides to bid
147
+ # Conventional logic: Price takes path of least resistance.
148
+ # If ask is thin (low dist), bid is thick (high dist) -> price goes up.
149
+ # Here: Ask Dist > Bid Dist means Ask is THINNER? No.
150
+ # Distance = Price Impact.
151
+ # High Distance = High Impact = THIN Liquidity.
152
+ # Low Distance = Low Impact = THICK Liquidity.
153
+ # Price moves towards THIN liquidity (Least Resistance).
154
+ # So if Ask Cost Dist > Bid Cost Dist, Ask is THIN. Price goes UP.
155
  projected_p = target_ask_price
156
  else:
157
  projected_p = target_bid_price
158
 
159
  path_points.append({
160
+ 'index': i,
161
  'p': projected_p
162
  })
163
 
 
396
 
397
  <div id="p-chart" class="panel">
398
  <div class="chart-header">
399
+ PRICE (BLUE) // <span class="c-purp">POLR RIVER (HIGH RES)</span> // <span style="color:var(--yellow)">PRED (YELLOW)</span>
400
  </div>
401
  <div id="tv-price" style="flex: 1; width: 100%;"></div>
402
  </div>
 
476
  const priceChart = LightweightCharts.createChart(document.getElementById('tv-price'), chartOpts);
477
 
478
  // --- POLR RIVER INITIALIZATION ---
479
+ // Creating 60 distinct series for high resolution flow
480
  const polrLines = [];
481
+ const polrCount = 60; // Matches backend
482
 
483
  for(let i=0; i<polrCount; i++) {{
484
+ // Calculate opacity: Nearest = 1.0, Furthest = 0.05
485
+ const opacity = 1.0 - (i / (polrCount + 5));
486
  const color = `rgba(213, 0, 249, ${{opacity.toFixed(2)}})`; // Purple fade
487
 
488
  polrLines.push(
 
583
  }}
584
 
585
  // 2. Purple River (POLR)
 
586
  if (data.polr && data.polr.length) {{
 
 
587
  data.polr.forEach((point, index) => {{
588
  if (index < polrLines.length) {{
589
  polrLines[index].update({{
 
658
  }});
659
  </script>
660
  </body>
661
+ </html>