hyperquant 0.26__py3-none-any.whl → 0.32__py3-none-any.whl

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.
@@ -0,0 +1,291 @@
1
+ from typing import Literal, Optional
2
+ import pybotters
3
+ from .models.ourbit import OurbitSwapDataStore
4
+
5
+
6
+ class OurbitSwap:
7
+
8
+ def __init__(self, client: pybotters.Client):
9
+ """
10
+ ✅ 完成:
11
+ 下单, 撤单, 查询资金, 查询持有订单, 查询历史订单
12
+
13
+ """
14
+ self.client = client
15
+ self.store = OurbitSwapDataStore()
16
+ self.api_url = "https://futures.ourbit.com"
17
+ self.ws_url = "wss://futures.ourbit.com/edge"
18
+
19
+ async def __aenter__(self) -> "OurbitSwap":
20
+ client = self.client
21
+ await self.store.initialize(
22
+ client.get(f"{self.api_url}/api/v1/contract/detailV2?client=web")
23
+ )
24
+ return self
25
+
26
+ async def update(
27
+ self, update_type: Literal["position", "orders", "balance", "ticker", "all"] = "all"
28
+ ):
29
+ """由于交易所很多不支持ws推送,这里使用Rest"""
30
+ all_urls = [
31
+ f"{self.api_url}/api/v1/private/position/open_positions",
32
+ f"{self.api_url}/api/v1/private/order/list/open_orders?page_size=200",
33
+ f"{self.api_url}/api/v1/private/account/assets",
34
+ f"{self.api_url}/api/v1/contract/ticker",
35
+ ]
36
+
37
+ url_map = {
38
+ "position": [all_urls[0]],
39
+ "orders": [all_urls[1]],
40
+ "balance": [all_urls[2]],
41
+ "ticker": [all_urls[3]],
42
+ "all": all_urls,
43
+ }
44
+
45
+ try:
46
+ urls = url_map[update_type]
47
+ except KeyError:
48
+ raise ValueError(f"update_type err: {update_type}")
49
+
50
+ # 直接传协程进去,initialize 会自己 await
51
+ await self.store.initialize(*(self.client.get(url) for url in urls))
52
+
53
+ async def sub_tickers(self):
54
+ self.client.ws_connect(
55
+ self.ws_url,
56
+ send_json={
57
+ "method": "sub.tickers",
58
+ "param": {
59
+ "timezone": "UTC+8"
60
+ }
61
+ },
62
+ hdlr_json=self.store.onmessage
63
+ )
64
+
65
+ async def sub_order_book(self, symbols: str | list[str]):
66
+ if isinstance(symbols, str):
67
+ symbols = [symbols]
68
+
69
+ send_jsons = []
70
+ # send_json={"method":"sub.depth.step","param":{"symbol":"BTC_USDT","step":"0.1"}},
71
+
72
+ for symbol in symbols:
73
+ step = self.store.detail.find({"symbol": symbol})[0].get("tick_size")
74
+
75
+ send_jsons.append({
76
+ "method": "sub.depth.step",
77
+ "param": {
78
+ "symbol": symbol,
79
+ "step": str(step)
80
+ }
81
+ })
82
+
83
+ await self.client.ws_connect(
84
+ self.ws_url,
85
+ send_json=send_jsons,
86
+ hdlr_json=self.store.onmessage
87
+ )
88
+
89
+ def ret_content(self, res: pybotters.FetchResult):
90
+ match res.data:
91
+ case {"success": True}:
92
+ return res.data["data"]
93
+ case _:
94
+ raise Exception(f"Failed api {res.response.url}: {res.data}")
95
+
96
+
97
+ async def place_order(
98
+ self,
99
+ symbol: str,
100
+ side: Literal["buy", "sell", "close_buy", "close_sell"],
101
+ size: float = None,
102
+ price: float = None,
103
+ order_type: Literal["market", "limit_GTC", "limit_IOC"] = "market",
104
+ usdt_amount: Optional[float] = None,
105
+ leverage: Optional[int] = 20,
106
+ position_id: Optional[int] = None,
107
+ ):
108
+ """
109
+ size为合约张数
110
+
111
+ .. code ::
112
+ {
113
+ "orderId": "219602019841167810",
114
+ "ts": 1756395601543
115
+ }
116
+
117
+ """
118
+ if (size is None) == (usdt_amount is None):
119
+ raise ValueError("params err")
120
+
121
+ max_lev = self.store.detail.find({"symbol": symbol})[0].get("max_lev")
122
+
123
+ if usdt_amount is not None:
124
+ cs = self.store.detail.find({"symbol": symbol})[0].get("contract_sz")
125
+ size = max(int(usdt_amount / cs / price), 1)
126
+
127
+
128
+ leverage = max(max_lev, leverage)
129
+
130
+ data = {
131
+ "symbol": symbol,
132
+ "side": 1 if side == "buy" else 3,
133
+ "openType": 1,
134
+ "type": "5",
135
+ "vol": size,
136
+ "leverage": leverage,
137
+ "marketCeiling": False,
138
+ "priceProtect": "0",
139
+ }
140
+
141
+ if order_type == "limit_IOC":
142
+ data["type"] = 3
143
+ data["price"] = str(price)
144
+ if order_type == "limit_GTC":
145
+ data["type"] = "1"
146
+ data["price"] = str(price)
147
+
148
+ if "close" in side:
149
+ if side == 'close_buy':
150
+ data["side"] = 4
151
+ elif side == 'close_sell':
152
+ data["side"] = 2
153
+
154
+ if position_id is None:
155
+ raise ValueError("position_id is required for closing position")
156
+ data["positionId"] = position_id
157
+ import time
158
+ print(time.time(), '下单')
159
+ res = await self.client.fetch(
160
+ "POST", f"{self.api_url}/api/v1/private/order/create", data=data
161
+ )
162
+ return self.ret_content(res)
163
+
164
+ async def place_tpsl(self,
165
+ position_id: int,
166
+ take_profit: Optional[float] = None,
167
+ stop_loss: Optional[float] = None,
168
+ ):
169
+ """
170
+ position_id 持仓ID
171
+
172
+ .. code:: json
173
+
174
+ {
175
+ "success": true,
176
+ "code": 0,
177
+ "data": 2280508
178
+ }
179
+ """
180
+ if (take_profit is None) and (stop_loss is None):
181
+ raise ValueError("params err")
182
+
183
+ data = {
184
+ "positionId": position_id,
185
+ "profitTrend": "1",
186
+ "lossTrend": "1",
187
+ "profitLossVolType": "SAME",
188
+ "volType": 2,
189
+ "takeProfitReverse": 2,
190
+ "stopLossReverse": 2,
191
+ "priceProtect": "0",
192
+ }
193
+
194
+ if take_profit is not None:
195
+ data["takeProfitPrice"] = take_profit
196
+ if stop_loss is not None:
197
+ data["stopLossPrice"] = stop_loss
198
+
199
+
200
+ res = await self.client.fetch(
201
+ "POST",
202
+ f"{self.api_url}/api/v1/private/stoporder/place",
203
+ data=data
204
+ )
205
+
206
+ return self.ret_content(res)
207
+
208
+ async def cancel_orders(self, order_ids: list[str]):
209
+ res = await self.client.fetch(
210
+ "POST",
211
+ f"{self.api_url}/api/v1/private/order/cancel",
212
+ data=order_ids,
213
+ )
214
+ return self.ret_content(res)
215
+
216
+ async def query_orders(
217
+ self,
218
+ symbol: str,
219
+ states: list[Literal["filled", "canceled"]], # filled:已成交, canceled:已撤销
220
+ start_time: Optional[int] = None,
221
+ end_time: Optional[int] = None,
222
+ page_size: int = 200,
223
+ page_num: int = 1,
224
+ ):
225
+ """查询历史订单
226
+
227
+ Args:
228
+ symbol: 交易对
229
+ states: 订单状态列表 ["filled":已成交, "canceled":已撤销]
230
+ start_time: 开始时间戳(毫秒), 可选
231
+ end_time: 结束时间戳(毫秒), 可选
232
+ page_size: 每页数量, 默认200
233
+ page_num: 页码, 默认1
234
+ """
235
+ state_map = {"filled": 3, "canceled": 4}
236
+
237
+ params = {
238
+ "symbol": symbol,
239
+ "states": ",".join(str(state_map[state]) for state in states),
240
+ "page_size": page_size,
241
+ "page_num": page_num,
242
+ "category": 1,
243
+ }
244
+
245
+ if start_time:
246
+ params["start_time"] = start_time
247
+ if end_time:
248
+ params["end_time"] = end_time
249
+
250
+ res = await self.client.fetch(
251
+ "GET",
252
+ f"{self.api_url}/api/v1/private/order/list/history_orders",
253
+ params=params,
254
+ )
255
+
256
+ return self.ret_content(res)
257
+
258
+ async def query_order(self, order_id: str):
259
+ """查询单个订单的详细信息
260
+
261
+ Args:
262
+ order_id: 订单ID
263
+
264
+ Returns:
265
+ ..code:python
266
+
267
+ 订单详情数据,例如:
268
+ [
269
+ {
270
+ "id": "38600506", # 成交ID
271
+ "symbol": "SOL_USDT", # 交易对
272
+ "side": 4, # 方向(1:买入, 3:卖出, 4:平仓)
273
+ "vol": 1, # 成交数量
274
+ "price": 204.11, # 成交价格
275
+ "fee": 0.00081644, # 手续费
276
+ "feeCurrency": "USDT", # 手续费币种
277
+ "profit": -0.0034, # 盈亏
278
+ "category": 1, # 品类
279
+ "orderId": "219079365441409152", # 订单ID
280
+ "timestamp": 1756270991000, # 时间戳
281
+ "positionMode": 1, # 持仓模式
282
+ "voucher": false, # 是否使用代金券
283
+ "taker": true # 是否是taker
284
+ }
285
+ ]
286
+ """
287
+ res = await self.client.fetch(
288
+ "GET",
289
+ f"{self.api_url}/api/v1/private/order/deal_details/{order_id}",
290
+ )
291
+ return self.ret_content(res)
@@ -0,0 +1,12 @@
1
+ import asyncio
2
+ import pybotters
3
+
4
+
5
+ class Heartbeat:
6
+ @staticmethod
7
+ async def ourbit(ws: pybotters.ws.ClientWebSocketResponse):
8
+ while not ws.closed:
9
+ await ws.send_str('{"method":"ping"}')
10
+ await asyncio.sleep(10.0)
11
+
12
+ pybotters.ws.HeartbeatHosts.items['futures.ourbit.com'] = Heartbeat.ourbit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 0.26
3
+ Version: 0.32
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
@@ -1,16 +1,21 @@
1
- hyperquant/__init__.py,sha256=gUuAPVpg5k8X_dpda5OpqmMyZ-ZXNQq-xwx-6JR5Jr4,131
1
+ hyperquant/__init__.py,sha256=UpjiX4LS5jmrBc2kE8RiLR02eCfD8JDQrR1q8zkLNcQ,161
2
2
  hyperquant/core.py,sha256=7XrpuHvccWl9lNyVihqaptupqUMsG3xYmQr8eEDrwS4,20610
3
3
  hyperquant/db.py,sha256=i2TjkCbmH4Uxo7UTDvOYBfy973gLcGexdzuT_YcSeIE,6678
4
4
  hyperquant/draw.py,sha256=up_lQ3pHeVLoNOyh9vPjgNwjD0M-6_IetSGviQUgjhY,54624
5
5
  hyperquant/logkit.py,sha256=WALpXpIA3Ywr5DxKKK3k5EKubZ2h-ISGfc5dUReQUBQ,7795
6
6
  hyperquant/notikit.py,sha256=x5yAZ_tAvLQRXcRbcg-VabCaN45LUhvlTZnUqkIqfAA,3596
7
- hyperquant/broker/hyperliquid.py,sha256=mSBVfmjcv6ciI1vWrmHYwBOTHrg-NQrwcyVFUXYEgVw,21998
7
+ hyperquant/broker/auth.py,sha256=hrdVuBTZwD3xSy5GI5JTFJhKiHhvIjZHA_7G5IUFznQ,1580
8
+ hyperquant/broker/hyperliquid.py,sha256=7MxbI9OyIBcImDelPJu-8Nd53WXjxPB5TwE6gsjHbto,23252
9
+ hyperquant/broker/ourbit.py,sha256=sPNdKxRiIICGVOjCIu0-VbqA-qPZ_1UeIpoDRaL9G_Q,9119
10
+ hyperquant/broker/ws.py,sha256=98Djt5n5sHUJKVbQ8Ql1t-G-Wiwu__4MYcUr5P6SDL0,326
8
11
  hyperquant/broker/lib/hpstore.py,sha256=LnLK2zmnwVvhEbLzYI-jz_SfYpO1Dv2u2cJaRAb84D8,8296
9
12
  hyperquant/broker/lib/hyper_types.py,sha256=HqjjzjUekldjEeVn6hxiWA8nevAViC2xHADOzDz9qyw,991
13
+ hyperquant/broker/models/hyperliquid.py,sha256=c4r5739ibZfnk69RxPjQl902AVuUOwT8RNvKsMtwXBY,9459
14
+ hyperquant/broker/models/ourbit.py,sha256=zvjtx6fmOuOPAJ8jxD2RK9_mao2XuE2EXkxR74nmXKM,18525
10
15
  hyperquant/datavison/_util.py,sha256=92qk4vO856RqycO0YqEIHJlEg-W9XKapDVqAMxe6rbw,533
11
16
  hyperquant/datavison/binance.py,sha256=3yNKTqvt_vUQcxzeX4ocMsI5k6Q6gLZrvgXxAEad6Kc,5001
12
17
  hyperquant/datavison/coinglass.py,sha256=PEjdjISP9QUKD_xzXNzhJ9WFDTlkBrRQlVL-5pxD5mo,10482
13
18
  hyperquant/datavison/okx.py,sha256=yg8WrdQ7wgWHNAInIgsWPM47N3Wkfr253169IPAycAY,6898
14
- hyperquant-0.26.dist-info/METADATA,sha256=YAc40ZCIrhQE-5pyXnxPRz3bVSToOyJKiB2XHFspzZs,4317
15
- hyperquant-0.26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- hyperquant-0.26.dist-info/RECORD,,
19
+ hyperquant-0.32.dist-info/METADATA,sha256=n6ti0zu8vGbaBI2cGFAnqI7AlhHpTLnOGd2tRkRPZUU,4317
20
+ hyperquant-0.32.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ hyperquant-0.32.dist-info/RECORD,,