hyperquant 0.55__tar.gz → 0.57__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.
Files changed (41) hide show
  1. {hyperquant-0.55 → hyperquant-0.57}/PKG-INFO +1 -1
  2. {hyperquant-0.55 → hyperquant-0.57}/pyproject.toml +1 -1
  3. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/models/ourbit.py +4 -2
  4. {hyperquant-0.55 → hyperquant-0.57}/tests/test_ourbit.py +29 -2
  5. {hyperquant-0.55 → hyperquant-0.57}/uv.lock +1 -1
  6. hyperquant-0.55/tests/test.py +0 -183
  7. hyperquant-0.55/tests/test_order_sync.py +0 -82
  8. hyperquant-0.55/tests/test_store.py +0 -16
  9. {hyperquant-0.55 → hyperquant-0.57}/.gitignore +0 -0
  10. {hyperquant-0.55 → hyperquant-0.57}/.python-version +0 -0
  11. {hyperquant-0.55 → hyperquant-0.57}/README.md +0 -0
  12. {hyperquant-0.55 → hyperquant-0.57}/apis.json +0 -0
  13. {hyperquant-0.55 → hyperquant-0.57}/backtest_tmp.py +0 -0
  14. {hyperquant-0.55 → hyperquant-0.57}/data/alpine_smoke.log +0 -0
  15. {hyperquant-0.55 → hyperquant-0.57}/data/logs/notikit.log +0 -0
  16. {hyperquant-0.55 → hyperquant-0.57}/data/logs/test_order_sync.log +0 -0
  17. {hyperquant-0.55 → hyperquant-0.57}/deals.json +0 -0
  18. {hyperquant-0.55 → hyperquant-0.57}/pnl_chart.html +0 -0
  19. {hyperquant-0.55 → hyperquant-0.57}/pub.sh +0 -0
  20. {hyperquant-0.55 → hyperquant-0.57}/requirements-dev.lock +0 -0
  21. {hyperquant-0.55 → hyperquant-0.57}/requirements.lock +0 -0
  22. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/__init__.py +0 -0
  23. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/auth.py +0 -0
  24. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/hyperliquid.py +0 -0
  25. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/lib/hpstore.py +0 -0
  26. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  27. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  28. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/ourbit.py +0 -0
  29. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/broker/ws.py +0 -0
  30. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/core.py +0 -0
  31. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/datavison/_util.py +0 -0
  32. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/datavison/binance.py +0 -0
  33. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/datavison/coinglass.py +0 -0
  34. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/datavison/okx.py +0 -0
  35. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/db.py +0 -0
  36. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/draw.py +0 -0
  37. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/logkit.py +0 -0
  38. {hyperquant-0.55 → hyperquant-0.57}/src/hyperquant/notikit.py +0 -0
  39. {hyperquant-0.55 → hyperquant-0.57}/tests/test_draw.py +0 -0
  40. {hyperquant-0.55 → hyperquant-0.57}/tests/test_lbank.py +0 -0
  41. {hyperquant-0.55 → hyperquant-0.57}/tests/tmp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 0.55
3
+ Version: 0.57
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperquant"
3
- version = "0.55"
3
+ version = "0.57"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -134,6 +134,7 @@ class Orders(DataStore):
134
134
  def _fmt(self, order:dict):
135
135
  return {
136
136
  "order_id": order.get("orderId"),
137
+ "position_id": order.get("positionId"),
137
138
  "symbol": order.get("symbol"),
138
139
  "price": order.get("price"),
139
140
  "vol": order.get("vol"),
@@ -213,6 +214,7 @@ class Detail(DataStore):
213
214
  "contract_sz": detail.get("cs"),
214
215
  "minv": detail.get("minV"),
215
216
  "maxv": detail.get("maxV")
217
+ "online_time": detail.get("tcd")
216
218
  }
217
219
  )
218
220
  self._update(data_to_insert)
@@ -403,8 +405,8 @@ class OurbitSwapDataStore(DataStoreCollection):
403
405
  "io": ["binance", "mexc"], # 交易所列表
404
406
  "contract_sz": 1,
405
407
  "minv": 1,
406
- "maxv": 10000
407
-
408
+ "maxv": 10000,
409
+ "online_time": 1625247600000 # 上线时间
408
410
  }
409
411
  ]
410
412
  """
@@ -173,11 +173,38 @@ async def test_order_sync_swap():
173
173
  # await ob.sub_orderbook(["SOL_USDT"]) # 订单簿频道
174
174
  # # 示例:市价
175
175
  # now= time.time()
176
- result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="limit_IOC", usdt_amount=8, price=200, window_sec=3)
176
+ result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="market", usdt_amount=8, price=200, window_sec=3)
177
177
  print(result)
178
+
179
+
180
+ async def test_order_close_swap():
181
+ async with pybotters.Client(
182
+ apis={
183
+ "ourbit": [
184
+ "WEBb401428e69af1815808e470be0a4f4e8a70a5c5cc0b0df0a33220f689167c629"
185
+ ]
186
+ }
187
+ ) as client:
188
+ ob = OurbitSwap(client)
189
+ await ob.__aenter__()
190
+ # await ob.sub_personal() # 私有频道
191
+ # result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="market", usdt_amount=8, price=200, window_sec=3)
192
+ # print(result)
193
+ # oid = await ob.place_order(
194
+ # "SOL_USDT",
195
+ # "buy",
196
+ # order_type="market",
197
+ # size=1
198
+ # )
199
+
200
+ # print(oid)
201
+ # await ob.update('position')
202
+ # print(ob.store.position.find())
203
+
204
+ await ob.place_order('SOL_USDT', 'close_sell', 1, position_id=6062178)
178
205
 
179
206
 
180
207
 
181
208
  if __name__ == "__main__":
182
209
  import asyncio
183
- asyncio.run(test_order_sync_swap())
210
+ asyncio.run(test_order_close_swap())
@@ -530,7 +530,7 @@ wheels = [
530
530
 
531
531
  [[package]]
532
532
  name = "hyperquant"
533
- version = "0.54"
533
+ version = "0.56"
534
534
  source = { editable = "." }
535
535
  dependencies = [
536
536
  { name = "aiohttp" },
@@ -1,183 +0,0 @@
1
- import asyncio
2
- import time
3
- from typing import Optional, TypedDict, Literal
4
-
5
- import pybotters
6
- from hyperquant.broker.ourbit import OurbitSpot
7
- from hyperquant.logkit import get_logger
8
-
9
- logger = get_logger("alpine_test", "./data/alpine_smoke.log", True)
10
-
11
-
12
- class Hold(TypedDict, total=False):
13
- symbol: str
14
- status: Literal["pending", "filled", "closing"]
15
- oid: str
16
- ts: float
17
- p: float
18
- q: float
19
- filled_ts: float
20
-
21
-
22
- class AlpineSmokeTest:
23
- """
24
- 仅针对 ALPINE_USDT:市价开仓 -> 持有 5 秒 -> 市价平仓。
25
- - 目的:测试 WS 事件链路与下单路径稳定性(不依赖价差/策略信号)。
26
- - 仅订阅 Ourbit 订单簿(可选) 和私有频道,验证参数与心跳。
27
- """
28
-
29
- SYMBOL = "SOL_USDT"
30
-
31
- def __init__(self, hold_seconds: float = 5.0, open_timeout_ms: int = 2000, close_timeout_ms: int = 3000):
32
- self.hold_seconds = hold_seconds
33
- self.open_timeout_ms = open_timeout_ms
34
- self.close_timeout_ms = close_timeout_ms
35
- self.client: Optional[pybotters.Client] = None
36
- self.ourbit: Optional[OurbitSpot] = None
37
- self.ob_store = None
38
-
39
- def _msg_debug(self,msg: dict, ws):
40
-
41
- if 'spot@private.orders' in msg.get("c", ""):
42
- logger.info(f"orders消息: {msg}")
43
-
44
- async def _msg_debug2(self):
45
- with self.ob_store.orders.watch() as stream:
46
- async for change in stream:
47
- print(change)
48
-
49
- async def __aenter__(self):
50
- self.client = pybotters.Client(apis="./apis.json")
51
- await self.client.__aenter__()
52
-
53
- self.ourbit = OurbitSpot(self.client)
54
- await self.ourbit.__aenter__()
55
- self.ob_store = self.ourbit.store
56
-
57
- # 仅订阅 ALPINE 订单簿与私有频道(用于测试 WS 参数与心跳)
58
- await self.ourbit.sub_orderbook([self.SYMBOL])
59
- await self.ourbit.sub_personal()
60
- logger.info("已就绪:仅订阅 Ourbit ALPINE 订单簿/私有频道")
61
- asyncio.create_task(self._msg_debug2())
62
- return self
63
-
64
- async def __aexit__(self, et, ev, tb):
65
- if self.client:
66
- await self.client.__aexit__(et, ev, tb)
67
-
68
- # ============ helpers ============
69
- async def _open_market_and_wait(self, symbol: str, usdt_amount: float, timeout_ms: int) -> Optional[Hold]:
70
- """以市价开仓并在本地流中等待终态。成功返回 Hold;失败/超时返回 None。"""
71
- start = time.time() * 1000
72
- with self.ob_store.orders.watch() as stream:
73
- try:
74
- oid = await self.ourbit.place_order(symbol, "buy", order_type="market", usdt_amount=usdt_amount)
75
- logger.info(f"准备开仓: {symbol} oid={oid}")
76
- except Exception as e:
77
- logger.error(f"开仓下单失败 {symbol}: {e}")
78
- return None
79
-
80
- while True:
81
- remain = timeout_ms - (time.time() * 1000 - start)
82
- if remain <= 0:
83
- logger.warning(f"开仓超时: {symbol} oid={oid}")
84
- return None
85
- try:
86
- change = await asyncio.wait_for(stream.__anext__(), timeout=remain / 1000)
87
- except asyncio.TimeoutError:
88
- logger.warning(f"开仓超时: {symbol} oid={oid}")
89
- return None
90
-
91
- if change.operation != "delete":
92
- continue
93
- data = change.data
94
- if data.get("order_id") != oid:
95
- continue
96
-
97
- state = data.get("state")
98
- if state == "filled":
99
- p = float(data["avg_price"]) ; q = float(data["deal_quantity"]) ; ts = time.time() * 1000
100
- logger.ok(f"建仓成交: {symbol} p={p} q={q}")
101
- return {"symbol": symbol, "status": "filled", "oid": oid, "p": p, "q": q, "filled_ts": ts}
102
- if state in ("canceled", "expired", "rejected", "failed"):
103
- logger.warning(f"开仓失败: {symbol} state={state}")
104
- return None
105
-
106
- async def _close_market_and_wait(self, symbol: str, qty: float, timeout_ms: int) -> bool:
107
- """以市价平仓并等待终态。成功返回 True。"""
108
- start = time.time() * 1000
109
- with self.ob_store.orders.watch() as stream:
110
- try:
111
- oid = await self.ourbit.place_order(symbol, "sell", order_type="market", quantity=qty)
112
- logger.info(f"触发平仓: {symbol} qty={qty} oid={oid}")
113
- except Exception as e:
114
- logger.error(f"平仓下单失败 {symbol}: {e}")
115
- return False
116
-
117
-
118
- while True:
119
- remain = timeout_ms - (time.time() * 1000 - start)
120
- if remain <= 0:
121
- logger.error(f"平仓超时: {symbol} oid={oid}")
122
- return False
123
- try:
124
- change = await asyncio.wait_for(stream.__anext__(), timeout=remain / 1000)
125
- except asyncio.TimeoutError:
126
- logger.error(f"平仓超时: {symbol} oid={oid}")
127
- return False
128
-
129
- if change.operation != "delete":
130
- continue
131
- data = change.data
132
- if data.get("order_id") != oid:
133
- continue
134
-
135
- state = data.get("state")
136
- if state == "filled":
137
- logger.ok(f"平仓完成: {symbol} close_p={data.get('avg_price')}")
138
- return True
139
- if state in ("canceled", "expired", "rejected", "failed"):
140
- logger.error(f"平仓失败: {symbol} state={state}")
141
- return False
142
-
143
- # ============ one-shot test flow ============
144
- async def run_once(self, usdt_amount: float = 10.0):
145
- """一次性流程:开仓 -> 持有 N 秒 -> 平仓。"""
146
- # 1) 开仓
147
- hold = await self._open_market_and_wait(self.SYMBOL, usdt_amount, timeout_ms=self.open_timeout_ms)
148
- if not hold:
149
- logger.warning("开仓未成功,终止本次测试")
150
- return
151
-
152
- # 2) 持有 N 秒
153
- logger.info(f"开始持有 {self.hold_seconds}s: {self.SYMBOL} q={hold['q']} p={hold['p']}")
154
- await asyncio.sleep(self.hold_seconds)
155
-
156
- # 3) 平仓
157
- ok = await self._close_market_and_wait(self.SYMBOL, float(hold["q"]), timeout_ms=self.close_timeout_ms)
158
- if ok:
159
- logger.info("本次测试完成 ✅")
160
- else:
161
- logger.error("本次测试失败 ❌")
162
-
163
- async def run_loop(self, usdt_amount: float = 10.0, interval_seconds: float = 60.0):
164
- """循环执行 run_once,间隔 N 秒。"""
165
- while True:
166
- await self.run_once(usdt_amount)
167
- logger.info(f"等待 {interval_seconds}s 后进行下一轮测试")
168
- await asyncio.sleep(interval_seconds)
169
-
170
-
171
- async def main():
172
- async with AlpineSmokeTest(hold_seconds=5.0) as test:
173
- # await test.run_once(usdt_amount=10.0)
174
- await test.run_loop(usdt_amount=10.0, interval_seconds=5)
175
-
176
-
177
- if __name__ == "__main__":
178
- try:
179
- asyncio.run(main())
180
- except KeyboardInterrupt:
181
- print("\n👋 程序已退出")
182
- except Exception as e:
183
- print(f"❌ 程序异常: {e}")
@@ -1,82 +0,0 @@
1
- import asyncio
2
- import time
3
- from hyperquant.broker.ourbit import OurbitSpot
4
- import pybotters
5
- from hyperquant.logkit import get_logger
6
-
7
- logger = get_logger('test_order_sync', './data/logs/test_order_sync.log', show_time=True)
8
-
9
-
10
- # 等待指定 oid 的最终 delete,超时抛 TimeoutError
11
- async def wait_delete(stream: pybotters.StoreStream, oid: str, seconds: float):
12
- async with asyncio.timeout(seconds):
13
- while True:
14
- change = await stream.__anext__()
15
- if change.operation == "delete" and change.data.get("order_id") == oid:
16
- return change.data # 内含 state / avg_price / deal_quantity 等累计字段
17
-
18
-
19
- async def order_sync(
20
- ob: OurbitSpot,
21
- symbol: str = "SOL_USDT",
22
- side: str = "buy",
23
- order_type: str = "market", # "market" / "limit"
24
- usdt_amount: float | None = None, # 市价可填
25
- quantity: float | None = None, # 市价可填
26
- price: float | None = None, # 限价必填
27
- window_sec: float = 2.0, # 主等待窗口(限价可设为 5.0)
28
- grace_sec: float = 2, # 撤单后宽限
29
- ):
30
- with ob.store.orders.watch() as stream:
31
- # 下单(只保留最简两种入参形态)
32
- try:
33
- oid = await ob.place_order(
34
- symbol,
35
- side,
36
- order_type=order_type,
37
- usdt_amount=usdt_amount,
38
- quantity=quantity,
39
- price=price,
40
- )
41
- except Exception as e:
42
- return {"symbol": symbol, "state": "error", "error": str(e)}
43
-
44
- # 步骤1:主窗口内等待这单的最终 delete
45
- try:
46
- return await wait_delete(stream, oid, window_sec)
47
- except TimeoutError:
48
- # 步骤2:到点撤单(市价通常用不到;限价才有意义)
49
- for i in range(3):
50
- try:
51
- await ob.cancel_order(oid)
52
- break
53
- except Exception:
54
- pass
55
- await asyncio.sleep(0.1)
56
- # 固定宽限内再等“迟到”的最终 delete
57
- try:
58
- return await wait_delete(stream, oid, grace_sec)
59
- except TimeoutError:
60
- return {"order_id": oid, "symbol": symbol, "state": "timeout"}
61
-
62
-
63
- async def test_order_sync():
64
- async with pybotters.Client(
65
- apis={
66
- "ourbit": [
67
- "WEB3bf088f8b2f2fae07592fe1a6240e2d798100a9cb2a91f8fda1056b6865ab3ee"
68
- ]
69
- }
70
- ) as client:
71
- ob = OurbitSpot(client)
72
- await ob.__aenter__()
73
- await ob.sub_personal() # 私有频道
74
- ob.store.book.limit = 3
75
- await ob.sub_orderbook(["SOL_USDT"]) # 订单簿频道
76
- # # 示例:市价
77
- # now= time.time()
78
- result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="market", usdt_amount=8, price=200, window_sec=2)
79
- print(result)
80
-
81
-
82
-
@@ -1,16 +0,0 @@
1
- import asyncio
2
- import pybotters
3
-
4
-
5
- async def main():
6
- ds = pybotters.store.DataStore(keys=["id"])
7
- loop = asyncio.get_running_loop()
8
-
9
- wait_task = loop.create_task(ds.wait())
10
- loop.call_soon(
11
- ds._insert, [{"id": 1, "val": 1}, {"id": 2, "val": 2}, {"id": 3, "val": 3}]
12
- )
13
- await asyncio.wait_for(wait_task, timeout=5.0)
14
-
15
- ds._insert, [{"id": 1, "val": 1}, {"id": 2, "val": 2}, {"id": 3, "val": 3}]
16
-
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