hyperquant 0.22__tar.gz → 0.24__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.24}/.gitignore +0 -1
- {hyperquant-0.22 → hyperquant-0.24}/PKG-INFO +2 -2
- {hyperquant-0.22 → hyperquant-0.24}/pyproject.toml +2 -2
- hyperquant-0.24/src/hyperquant/broker/lib/hpstore.py +252 -0
- hyperquant-0.24/src/hyperquant/broker/lib/hyper_types.py +48 -0
- {hyperquant-0.22 → hyperquant-0.24}/uv.lock +5 -5
- {hyperquant-0.22 → hyperquant-0.24}/.python-version +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/README.md +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/pub.sh +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/requirements-dev.lock +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/requirements.lock +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/__init__.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/core.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/db.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/draw.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/logkit.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/src/hyperquant/notikit.py +0 -0
- {hyperquant-0.22 → hyperquant-0.24}/test.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hyperquant
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.24
|
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
|
@@ -19,7 +19,7 @@ Requires-Dist: cryptography>=44.0.2
|
|
19
19
|
Requires-Dist: duckdb>=1.2.2
|
20
20
|
Requires-Dist: numpy>=1.21.0
|
21
21
|
Requires-Dist: pandas>=2.2.3
|
22
|
-
Requires-Dist: pybotters>=1.
|
22
|
+
Requires-Dist: pybotters>=1.9.0
|
23
23
|
Requires-Dist: pyecharts>=2.0.8
|
24
24
|
Description-Content-Type: text/markdown
|
25
25
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "hyperquant"
|
3
|
-
version = "0.
|
3
|
+
version = "0.24"
|
4
4
|
description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
|
5
5
|
authors = [
|
6
6
|
{ name = "MissinA", email = "1421329142@qq.com" }
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
13
13
|
"cryptography>=44.0.2",
|
14
14
|
"numpy>=1.21.0", # Added numpy as a new dependency
|
15
15
|
"duckdb>=1.2.2",
|
16
|
-
"pybotters>=1.
|
16
|
+
"pybotters>=1.9.0",
|
17
17
|
]
|
18
18
|
readme = "README.md"
|
19
19
|
requires-python = ">=3.9"
|
@@ -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
|
@@ -530,7 +530,7 @@ wheels = [
|
|
530
530
|
|
531
531
|
[[package]]
|
532
532
|
name = "hyperquant"
|
533
|
-
version = "0.
|
533
|
+
version = "0.23"
|
534
534
|
source = { editable = "." }
|
535
535
|
dependencies = [
|
536
536
|
{ name = "aiohttp" },
|
@@ -557,7 +557,7 @@ requires-dist = [
|
|
557
557
|
{ name = "duckdb", specifier = ">=1.2.2" },
|
558
558
|
{ name = "numpy", specifier = ">=1.21.0" },
|
559
559
|
{ name = "pandas", specifier = ">=2.2.3" },
|
560
|
-
{ name = "pybotters", specifier = ">=1.
|
560
|
+
{ name = "pybotters", specifier = ">=1.9.0" },
|
561
561
|
{ name = "pyecharts", specifier = ">=2.0.8" },
|
562
562
|
]
|
563
563
|
|
@@ -1221,15 +1221,15 @@ wheels = [
|
|
1221
1221
|
|
1222
1222
|
[[package]]
|
1223
1223
|
name = "pybotters"
|
1224
|
-
version = "1.
|
1224
|
+
version = "1.9.0"
|
1225
1225
|
source = { registry = "https://pypi.org/simple" }
|
1226
1226
|
dependencies = [
|
1227
1227
|
{ name = "aiohttp" },
|
1228
1228
|
{ name = "typing-extensions", marker = "python_full_version < '3.10'" },
|
1229
1229
|
]
|
1230
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
1230
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1c/32/c90531c4fab11030afba9343188b74bb56b009390e0b873af01a460e2cd2/pybotters-1.9.0.tar.gz", hash = "sha256:91f0d54ae60805ce408494f0ee0cb83c7d4a74f70d49eda8f550bde0aa71a7d1", size = 558643 }
|
1231
1231
|
wheels = [
|
1232
|
-
{ url = "https://files.pythonhosted.org/packages/
|
1232
|
+
{ url = "https://files.pythonhosted.org/packages/a0/6c/c382df909de72ad90bf9b319ccb67e7274a5bd2cb8f178c1322cf4b6da16/pybotters-1.9.0-py3-none-any.whl", hash = "sha256:91a70301e0b0e234351cda270638151a527bbe272d5de12466d8bb130f293ffd", size = 519494 },
|
1233
1233
|
]
|
1234
1234
|
|
1235
1235
|
[[package]]
|
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
|