oxarchive 0.6.0__tar.gz → 0.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oxarchive-0.6.0 → oxarchive-0.6.1}/PKG-INFO +39 -1
- {oxarchive-0.6.0 → oxarchive-0.6.1}/README.md +38 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/__init__.py +1 -1
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/types.py +18 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/websocket.py +34 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/pyproject.toml +1 -1
- {oxarchive-0.6.0 → oxarchive-0.6.1}/.gitignore +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/client.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/exchanges.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/http.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/__init__.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/candles.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/data_quality.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/funding.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/instruments.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/liquidations.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/openinterest.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/orderbook.py +0 -0
- {oxarchive-0.6.0 → oxarchive-0.6.1}/oxarchive/resources/trades.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oxarchive
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Official Python SDK for 0xarchive - Hyperliquid Historical Data API
|
|
5
5
|
Project-URL: Homepage, https://0xarchive.io
|
|
6
6
|
Project-URL: Documentation, https://0xarchive.io/docs/sdks
|
|
@@ -639,6 +639,43 @@ async def main():
|
|
|
639
639
|
asyncio.run(main())
|
|
640
640
|
```
|
|
641
641
|
|
|
642
|
+
### Gap Detection
|
|
643
|
+
|
|
644
|
+
During historical replay and bulk streaming, the server automatically detects gaps in the data and notifies the client. This helps identify periods where data may be missing.
|
|
645
|
+
|
|
646
|
+
```python
|
|
647
|
+
import asyncio
|
|
648
|
+
from oxarchive import OxArchiveWs, WsOptions
|
|
649
|
+
|
|
650
|
+
async def main():
|
|
651
|
+
ws = OxArchiveWs(WsOptions(api_key="ox_..."))
|
|
652
|
+
|
|
653
|
+
# Handle gap notifications during replay/stream
|
|
654
|
+
def handle_gap(channel, coin, gap_start, gap_end, duration_minutes):
|
|
655
|
+
print(f"Gap detected in {channel}/{coin}:")
|
|
656
|
+
print(f" From: {gap_start}")
|
|
657
|
+
print(f" To: {gap_end}")
|
|
658
|
+
print(f" Duration: {duration_minutes} minutes")
|
|
659
|
+
|
|
660
|
+
ws.on_gap(handle_gap)
|
|
661
|
+
|
|
662
|
+
await ws.connect()
|
|
663
|
+
|
|
664
|
+
# Start replay - gaps will be reported via on_gap callback
|
|
665
|
+
await ws.replay(
|
|
666
|
+
"orderbook", "BTC",
|
|
667
|
+
start=int(time.time() * 1000) - 86400000,
|
|
668
|
+
end=int(time.time() * 1000),
|
|
669
|
+
speed=10
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
asyncio.run(main())
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
Gap thresholds vary by channel:
|
|
676
|
+
- **orderbook**, **candles**, **liquidations**: 2 minutes
|
|
677
|
+
- **trades**: 60 minutes (trades can naturally have longer gaps during low activity periods)
|
|
678
|
+
|
|
642
679
|
### WebSocket Configuration
|
|
643
680
|
|
|
644
681
|
```python
|
|
@@ -659,6 +696,7 @@ ws = OxArchiveWs(WsOptions(
|
|
|
659
696
|
| `orderbook` | L2 order book updates | Yes | Yes |
|
|
660
697
|
| `trades` | Trade/fill updates | Yes | Yes |
|
|
661
698
|
| `candles` | OHLCV candle data | Yes | Yes (replay/stream only) |
|
|
699
|
+
| `liquidations` | Liquidation events (May 2025+) | Yes | Yes (replay/stream only) |
|
|
662
700
|
| `ticker` | Price and 24h volume | Yes | Real-time only |
|
|
663
701
|
| `all_tickers` | All market tickers | No | Real-time only |
|
|
664
702
|
|
|
@@ -602,6 +602,43 @@ async def main():
|
|
|
602
602
|
asyncio.run(main())
|
|
603
603
|
```
|
|
604
604
|
|
|
605
|
+
### Gap Detection
|
|
606
|
+
|
|
607
|
+
During historical replay and bulk streaming, the server automatically detects gaps in the data and notifies the client. This helps identify periods where data may be missing.
|
|
608
|
+
|
|
609
|
+
```python
|
|
610
|
+
import asyncio
|
|
611
|
+
from oxarchive import OxArchiveWs, WsOptions
|
|
612
|
+
|
|
613
|
+
async def main():
|
|
614
|
+
ws = OxArchiveWs(WsOptions(api_key="ox_..."))
|
|
615
|
+
|
|
616
|
+
# Handle gap notifications during replay/stream
|
|
617
|
+
def handle_gap(channel, coin, gap_start, gap_end, duration_minutes):
|
|
618
|
+
print(f"Gap detected in {channel}/{coin}:")
|
|
619
|
+
print(f" From: {gap_start}")
|
|
620
|
+
print(f" To: {gap_end}")
|
|
621
|
+
print(f" Duration: {duration_minutes} minutes")
|
|
622
|
+
|
|
623
|
+
ws.on_gap(handle_gap)
|
|
624
|
+
|
|
625
|
+
await ws.connect()
|
|
626
|
+
|
|
627
|
+
# Start replay - gaps will be reported via on_gap callback
|
|
628
|
+
await ws.replay(
|
|
629
|
+
"orderbook", "BTC",
|
|
630
|
+
start=int(time.time() * 1000) - 86400000,
|
|
631
|
+
end=int(time.time() * 1000),
|
|
632
|
+
speed=10
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
asyncio.run(main())
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Gap thresholds vary by channel:
|
|
639
|
+
- **orderbook**, **candles**, **liquidations**: 2 minutes
|
|
640
|
+
- **trades**: 60 minutes (trades can naturally have longer gaps during low activity periods)
|
|
641
|
+
|
|
605
642
|
### WebSocket Configuration
|
|
606
643
|
|
|
607
644
|
```python
|
|
@@ -622,6 +659,7 @@ ws = OxArchiveWs(WsOptions(
|
|
|
622
659
|
| `orderbook` | L2 order book updates | Yes | Yes |
|
|
623
660
|
| `trades` | Trade/fill updates | Yes | Yes |
|
|
624
661
|
| `candles` | OHLCV candle data | Yes | Yes (replay/stream only) |
|
|
662
|
+
| `liquidations` | Liquidation events (May 2025+) | Yes | Yes (replay/stream only) |
|
|
625
663
|
| `ticker` | Price and 24h volume | Yes | Real-time only |
|
|
626
664
|
| `all_tickers` | All market tickers | No | Real-time only |
|
|
627
665
|
|
|
@@ -555,6 +555,24 @@ class WsStreamStopped(BaseModel):
|
|
|
555
555
|
snapshots_sent: int
|
|
556
556
|
|
|
557
557
|
|
|
558
|
+
class WsGapDetected(BaseModel):
|
|
559
|
+
"""Gap detected in historical data stream.
|
|
560
|
+
|
|
561
|
+
Sent when there's a gap exceeding the threshold between consecutive data points.
|
|
562
|
+
Thresholds: 2 minutes for orderbook/candles/liquidations, 60 minutes for trades.
|
|
563
|
+
"""
|
|
564
|
+
|
|
565
|
+
type: Literal["gap_detected"]
|
|
566
|
+
channel: WsChannel
|
|
567
|
+
coin: str
|
|
568
|
+
gap_start: int
|
|
569
|
+
"""Start of the gap (last data point timestamp in ms)."""
|
|
570
|
+
gap_end: int
|
|
571
|
+
"""End of the gap (next data point timestamp in ms)."""
|
|
572
|
+
duration_minutes: int
|
|
573
|
+
"""Gap duration in minutes."""
|
|
574
|
+
|
|
575
|
+
|
|
558
576
|
# =============================================================================
|
|
559
577
|
# Error Types
|
|
560
578
|
# =============================================================================
|
|
@@ -64,6 +64,7 @@ from .types import (
|
|
|
64
64
|
WsHistoricalBatch,
|
|
65
65
|
WsStreamCompleted,
|
|
66
66
|
WsStreamStopped,
|
|
67
|
+
WsGapDetected,
|
|
67
68
|
TimestampedRecord,
|
|
68
69
|
)
|
|
69
70
|
|
|
@@ -122,6 +123,9 @@ StreamStartHandler = Callable[[WsChannel, str, int, int], None] # channel, coin
|
|
|
122
123
|
StreamProgressHandler = Callable[[int], None] # snapshots_sent
|
|
123
124
|
StreamCompleteHandler = Callable[[WsChannel, str, int], None] # channel, coin, snapshots_sent
|
|
124
125
|
|
|
126
|
+
# Gap detection handler
|
|
127
|
+
GapHandler = Callable[[WsChannel, str, int, int, int], None] # channel, coin, gap_start, gap_end, duration_minutes
|
|
128
|
+
|
|
125
129
|
|
|
126
130
|
def _transform_trade(coin: str, raw: dict) -> Trade:
|
|
127
131
|
"""Transform raw Hyperliquid trade format to SDK Trade type.
|
|
@@ -289,6 +293,9 @@ class OxArchiveWs:
|
|
|
289
293
|
self._on_stream_progress: Optional[StreamProgressHandler] = None
|
|
290
294
|
self._on_stream_complete: Optional[StreamCompleteHandler] = None
|
|
291
295
|
|
|
296
|
+
# Gap detection handler
|
|
297
|
+
self._on_gap: Optional[GapHandler] = None
|
|
298
|
+
|
|
292
299
|
@property
|
|
293
300
|
def state(self) -> WsConnectionState:
|
|
294
301
|
"""Get current connection state."""
|
|
@@ -626,6 +633,23 @@ class OxArchiveWs:
|
|
|
626
633
|
"""
|
|
627
634
|
self._on_stream_complete = handler
|
|
628
635
|
|
|
636
|
+
def on_gap(self, handler: GapHandler) -> None:
|
|
637
|
+
"""Set handler for gap detected events during replay or streaming.
|
|
638
|
+
|
|
639
|
+
Called when there's a gap in the historical data exceeding the threshold.
|
|
640
|
+
Thresholds: 2 minutes for orderbook/candles/liquidations, 60 minutes for trades.
|
|
641
|
+
|
|
642
|
+
Handler receives: (channel, coin, gap_start, gap_end, duration_minutes)
|
|
643
|
+
|
|
644
|
+
Example:
|
|
645
|
+
>>> def handle_gap(channel, coin, gap_start, gap_end, duration_minutes):
|
|
646
|
+
... print(f"Gap detected in {channel} {coin}: {duration_minutes} minutes")
|
|
647
|
+
... print(f" From: {datetime.fromtimestamp(gap_start/1000)}")
|
|
648
|
+
... print(f" To: {datetime.fromtimestamp(gap_end/1000)}")
|
|
649
|
+
>>> ws.on_gap(handle_gap)
|
|
650
|
+
"""
|
|
651
|
+
self._on_gap = handler
|
|
652
|
+
|
|
629
653
|
# Private methods
|
|
630
654
|
|
|
631
655
|
async def _send(self, msg: dict) -> None:
|
|
@@ -778,6 +802,16 @@ class OxArchiveWs:
|
|
|
778
802
|
elif msg_type == "stream_completed" and self._on_stream_complete:
|
|
779
803
|
self._on_stream_complete(data["channel"], data["coin"], data["snapshots_sent"])
|
|
780
804
|
|
|
805
|
+
# Gap detection
|
|
806
|
+
elif msg_type == "gap_detected" and self._on_gap:
|
|
807
|
+
self._on_gap(
|
|
808
|
+
data["channel"],
|
|
809
|
+
data["coin"],
|
|
810
|
+
data["gap_start"],
|
|
811
|
+
data["gap_end"],
|
|
812
|
+
data["duration_minutes"],
|
|
813
|
+
)
|
|
814
|
+
|
|
781
815
|
except Exception as e:
|
|
782
816
|
logger.error(f"Error handling message: {e}")
|
|
783
817
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|