hyperquant 0.22__tar.gz → 0.23__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.
- {hyperquant-0.22 → hyperquant-0.23}/.gitignore +0 -1
- {hyperquant-0.22 → hyperquant-0.23}/PKG-INFO +1 -1
- {hyperquant-0.22 → hyperquant-0.23}/pyproject.toml +1 -1
- hyperquant-0.23/src/hyperquant/broker/lib/hpstore.py +252 -0
- hyperquant-0.23/src/hyperquant/broker/lib/hyper_types.py +48 -0
- {hyperquant-0.22 → hyperquant-0.23}/uv.lock +1 -1
- {hyperquant-0.22 → hyperquant-0.23}/.python-version +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/README.md +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/pub.sh +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/requirements-dev.lock +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/requirements.lock +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/__init__.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/core.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/db.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/draw.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/logkit.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/src/hyperquant/notikit.py +0 -0
- {hyperquant-0.22 → hyperquant-0.23}/test.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hyperquant
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.23
|
4
4
|
Summary: A minimal yet hyper-efficient backtesting framework for quantitative trading
|
5
5
|
Project-URL: Homepage, https://github.com/yourusername/hyperquant
|
6
6
|
Project-URL: Issues, https://github.com/yourusername/hyperquant/issues
|
@@ -0,0 +1,252 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import time
|
4
|
+
from aiohttp import ClientWebSocketResponse
|
5
|
+
import aiohttp
|
6
|
+
|
7
|
+
from pybotters.store import DataStore
|
8
|
+
from pybotters.models.hyperliquid import HyperliquidDataStore
|
9
|
+
from typing import TYPE_CHECKING, Awaitable
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from pybotters.typedefs import Item
|
13
|
+
from pybotters.ws import ClientWebSocketResponse
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
# {'channel': 'orderUpdates', 'data': [{'order': {'coin': 'HYPE', 'side': 'A', 'limitPx': '22.887', 'sz': '1.12', 'oid': 29641480516, 'timestamp': 1746766108031, 'origSz': '1.12', 'reduceOnly': True}, 'status': 'rejected', 'statusTimestamp': 1746766108031}]}
|
18
|
+
class OrderStore(DataStore):
|
19
|
+
_KEYS = ["oid"]
|
20
|
+
|
21
|
+
def _onmessage(self, msg: Item) -> None:
|
22
|
+
|
23
|
+
for rec in msg:
|
24
|
+
order = rec["order"]
|
25
|
+
item = {
|
26
|
+
**order,
|
27
|
+
"status": rec.get("status"),
|
28
|
+
"px": None,
|
29
|
+
'fee': None,
|
30
|
+
"statusTimestamp": rec.get("statusTimestamp"),
|
31
|
+
}
|
32
|
+
|
33
|
+
if item["status"] == "open":
|
34
|
+
self._update([item])
|
35
|
+
else:
|
36
|
+
self._delete([item])
|
37
|
+
|
38
|
+
class FillStore(DataStore):
|
39
|
+
_KEYS = ["oid"]
|
40
|
+
|
41
|
+
def _onmessage(self, msg: Item) -> None:
|
42
|
+
for fill in msg:
|
43
|
+
self._update([fill])
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
class Account(DataStore):
|
48
|
+
_KEYS = ["marginCoin", "value"]
|
49
|
+
|
50
|
+
def _onmessage(self, data: list[Item]) -> None:
|
51
|
+
self._update(
|
52
|
+
[
|
53
|
+
{
|
54
|
+
"marginCoin": 'USDC',
|
55
|
+
'value': float(item['accountValue']),
|
56
|
+
'frozen': float(item['totalMarginUsed']),
|
57
|
+
'available': float(item['accountValue']) - float(item['totalMarginUsed']),
|
58
|
+
}
|
59
|
+
for item in data
|
60
|
+
]
|
61
|
+
)
|
62
|
+
|
63
|
+
class SpotAccount(DataStore):
|
64
|
+
|
65
|
+
_KEYS = ["coin"]
|
66
|
+
|
67
|
+
def _onmessage(self, data: list[Item]) -> None:
|
68
|
+
self._update(
|
69
|
+
[
|
70
|
+
{
|
71
|
+
"coin": item['coin'],
|
72
|
+
"total": float(item['total']),
|
73
|
+
"frozen": float(item['hold']),
|
74
|
+
"available": float(item['total']) - float(item['hold']),
|
75
|
+
"entryNtl": float(item['entryNtl']),
|
76
|
+
}
|
77
|
+
for item in data
|
78
|
+
]
|
79
|
+
)
|
80
|
+
|
81
|
+
class PositionStore(DataStore):
|
82
|
+
_KEYS = ["coin"]
|
83
|
+
|
84
|
+
def _onmessage(self, data: list[Item]) -> None:
|
85
|
+
|
86
|
+
if len(data) == 0 and self.__len__() > 0:
|
87
|
+
self._clear()
|
88
|
+
elif len(data) > 0:
|
89
|
+
self._update([
|
90
|
+
|
91
|
+
{
|
92
|
+
"coin": item['position']['coin'],
|
93
|
+
"sz": float(item['position']['szi']),
|
94
|
+
"px": float(item['position']['entryPx']),
|
95
|
+
'unpnl': float(item['position']['unrealizedPnl']),
|
96
|
+
'rt': float(item['position']['returnOnEquity']),
|
97
|
+
'lv': int(item['position']['leverage']['value']),
|
98
|
+
}
|
99
|
+
for item in data
|
100
|
+
])
|
101
|
+
|
102
|
+
|
103
|
+
class MyHyperStore(HyperliquidDataStore):
|
104
|
+
ORDER_TYPE = 'orderUpdates'
|
105
|
+
WEBDATA2_TYPE = 'webData2'
|
106
|
+
ORDER_FILL_TYPE = 'userFills'
|
107
|
+
|
108
|
+
def _init(self) -> None:
|
109
|
+
self._create("orders", datastore_class=OrderStore)
|
110
|
+
self._create("account", datastore_class=Account)
|
111
|
+
self._create("positions", datastore_class=PositionStore)
|
112
|
+
self._create("spot_account", datastore_class=SpotAccount)
|
113
|
+
self._create("fills", datastore_class=FillStore)
|
114
|
+
super()._init()
|
115
|
+
|
116
|
+
def _onmessage(self, msg: Item, ws: ClientWebSocketResponse | None = None) -> None:
|
117
|
+
|
118
|
+
if msg.get("channel") == self.ORDER_TYPE:
|
119
|
+
self.orders._onmessage(msg.get('data', []))
|
120
|
+
elif msg.get("channel") == self.WEBDATA2_TYPE:
|
121
|
+
# print(msg.get('data', {}).get('clearinghouseState', {}))
|
122
|
+
act_data = msg.get('data', {}).get('clearinghouseState', {}).get('crossMarginSummary', [])
|
123
|
+
if act_data:
|
124
|
+
self.account._onmessage([act_data])
|
125
|
+
|
126
|
+
pos_data = msg.get('data', {}).get('clearinghouseState', {}).get('assetPositions', [])
|
127
|
+
self.positions._onmessage(pos_data)
|
128
|
+
|
129
|
+
spot_act_data = msg.get('data', {}).get('spotState', {}).get('balances', [])
|
130
|
+
self.spot_account._onmessage(spot_act_data)
|
131
|
+
|
132
|
+
elif msg.get("channel") == self.ORDER_FILL_TYPE:
|
133
|
+
fills = msg.get('data', {}).get('fills', [])
|
134
|
+
is_snap = msg.get('data', {}).get('isSnapshot', False)
|
135
|
+
if not is_snap:
|
136
|
+
self.fills._onmessage(fills)
|
137
|
+
|
138
|
+
super()._onmessage(msg, ws)
|
139
|
+
|
140
|
+
async def initialize(self, *aws: tuple[str, Awaitable[aiohttp.ClientResponse]]) -> None:
|
141
|
+
|
142
|
+
for a in aws:
|
143
|
+
method, f = a
|
144
|
+
resp = await f
|
145
|
+
data = await resp.json()
|
146
|
+
if method == "orders":
|
147
|
+
|
148
|
+
self.orders._onmessage(
|
149
|
+
[
|
150
|
+
{
|
151
|
+
'order': o,
|
152
|
+
'status': "open",
|
153
|
+
'statusTimestamp': int(time.time() * 1000)
|
154
|
+
} for o in data
|
155
|
+
]
|
156
|
+
)
|
157
|
+
|
158
|
+
pass
|
159
|
+
|
160
|
+
@property
|
161
|
+
def orders(self) -> OrderStore:
|
162
|
+
"""``orders`` data stream.
|
163
|
+
|
164
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
165
|
+
|
166
|
+
Data structure:
|
167
|
+
|
168
|
+
.. code:: python
|
169
|
+
[
|
170
|
+
{
|
171
|
+
"coin": "HYPE",
|
172
|
+
"side": "A",
|
173
|
+
"limitPx": "22.887",
|
174
|
+
"sz": "1.12",
|
175
|
+
"oid": 29641480516,
|
176
|
+
"timestamp": 1746766108031,
|
177
|
+
"origSz": "1.12",
|
178
|
+
"reduceOnly": True
|
179
|
+
"status": "open",
|
180
|
+
"statusTimestamp": 1746766108031
|
181
|
+
}...
|
182
|
+
]
|
183
|
+
"""
|
184
|
+
return self._get("orders", OrderStore)
|
185
|
+
@property
|
186
|
+
def account(self) -> Account:
|
187
|
+
"""``account`` data stream.
|
188
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
189
|
+
Data structure:
|
190
|
+
.. code:: python
|
191
|
+
[
|
192
|
+
{
|
193
|
+
"marginCoin": 'USDC',
|
194
|
+
'value': float(item['accountValue']),
|
195
|
+
'frozen': float(item['totalMarginUsed']),
|
196
|
+
'available': float(item['accountValue']) - float(item['totalMarginUsed']),
|
197
|
+
}...
|
198
|
+
]
|
199
|
+
"""
|
200
|
+
return self._get("account", Account)
|
201
|
+
|
202
|
+
@property
|
203
|
+
def positions(self) -> PositionStore:
|
204
|
+
return self._get("positions", PositionStore)
|
205
|
+
|
206
|
+
@property
|
207
|
+
def spot_account(self) -> SpotAccount:
|
208
|
+
"""``spot_account`` data stream.
|
209
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
210
|
+
Data structure:
|
211
|
+
.. code:: python
|
212
|
+
[
|
213
|
+
{
|
214
|
+
"coin": 'FEUSD',
|
215
|
+
"sz": "21.0",
|
216
|
+
"px": "0.9719",
|
217
|
+
"unpnl": "0.0",
|
218
|
+
"rt": "0.0",
|
219
|
+
"lv": 1,
|
220
|
+
}...
|
221
|
+
]
|
222
|
+
"""
|
223
|
+
return self._get("spot_account", SpotAccount)
|
224
|
+
|
225
|
+
@property
|
226
|
+
def fills(self) -> FillStore:
|
227
|
+
"""``fills`` data stream.
|
228
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
229
|
+
Data structure:
|
230
|
+
.. code:: python
|
231
|
+
[
|
232
|
+
{
|
233
|
+
"coin": 'FEUSD',
|
234
|
+
"px": "0.9719",
|
235
|
+
"sz": "21.0",
|
236
|
+
"side": 'buy',
|
237
|
+
"time": 1679999999999,
|
238
|
+
"startPosition": '0.0',
|
239
|
+
"dir": 'buy',
|
240
|
+
"closedPnl": '0.0',
|
241
|
+
"hash": '0x123456789abcdef',
|
242
|
+
"oid": 123456789,
|
243
|
+
"crossed": True,
|
244
|
+
"fee": '-0.0001',
|
245
|
+
"tid": 987654321,
|
246
|
+
"liquidation": None,
|
247
|
+
"feeToken": 'USDC',
|
248
|
+
}...
|
249
|
+
]
|
250
|
+
"""
|
251
|
+
return self._get("fills", FillStore)
|
252
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
from typing import TypedDict, Dict
|
2
|
+
|
3
|
+
class Leverage(TypedDict):
|
4
|
+
rawUsd: str
|
5
|
+
type: str
|
6
|
+
value: int
|
7
|
+
|
8
|
+
class CumFunding(TypedDict):
|
9
|
+
allTime: str
|
10
|
+
sinceChange: str
|
11
|
+
sinceOpen: str
|
12
|
+
|
13
|
+
class Position(TypedDict):
|
14
|
+
coin: str
|
15
|
+
cumFunding: CumFunding
|
16
|
+
entryPx: str
|
17
|
+
leverage: Leverage
|
18
|
+
liquidationPx: str
|
19
|
+
marginUsed: str
|
20
|
+
maxLeverage: int
|
21
|
+
positionValue: str
|
22
|
+
returnOnEquity: str
|
23
|
+
szi: str
|
24
|
+
unrealizedPnl: str
|
25
|
+
|
26
|
+
class AssetPosition(TypedDict):
|
27
|
+
position: Position
|
28
|
+
type: str
|
29
|
+
|
30
|
+
class CrossMarginSummary(TypedDict):
|
31
|
+
accountValue: str
|
32
|
+
totalMarginUsed: str
|
33
|
+
totalNtlPos: str
|
34
|
+
totalRawUsd: str
|
35
|
+
|
36
|
+
class MarginSummary(TypedDict):
|
37
|
+
accountValue: str
|
38
|
+
totalMarginUsed: str
|
39
|
+
totalNtlPos: str
|
40
|
+
totalRawUsd: str
|
41
|
+
|
42
|
+
class AccountBalance(TypedDict):
|
43
|
+
assetPositions: list[AssetPosition]
|
44
|
+
crossMaintenanceMarginUsed: str
|
45
|
+
crossMarginSummary: CrossMarginSummary
|
46
|
+
marginSummary: MarginSummary
|
47
|
+
time: int
|
48
|
+
withdrawable: str
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|