hyperquant 0.25__tar.gz → 0.26__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.25 → hyperquant-0.26}/PKG-INFO +2 -2
- {hyperquant-0.25 → hyperquant-0.26}/pyproject.toml +2 -2
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/core.py +77 -26
- {hyperquant-0.25 → hyperquant-0.26}/uv.lock +5 -5
- {hyperquant-0.25 → hyperquant-0.26}/.gitignore +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/.python-version +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/README.md +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/pub.sh +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/requirements-dev.lock +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/requirements.lock +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/__init__.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/db.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/draw.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/logkit.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/src/hyperquant/notikit.py +0 -0
- {hyperquant-0.25 → hyperquant-0.26}/test.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hyperquant
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.26
|
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.9.
|
22
|
+
Requires-Dist: pybotters>=1.9.1
|
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.26"
|
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.9.
|
16
|
+
"pybotters>=1.9.1",
|
17
17
|
]
|
18
18
|
readme = "README.md"
|
19
19
|
requires-python = ">=3.9"
|
@@ -350,7 +350,7 @@ class Exchange(ExchangeBase):
|
|
350
350
|
self.record_history(time)
|
351
351
|
|
352
352
|
# 自动更新账户状态
|
353
|
-
self.Update({symbol: price},
|
353
|
+
self.Update({symbol: price}, time=time)
|
354
354
|
|
355
355
|
return trade
|
356
356
|
|
@@ -377,41 +377,92 @@ class Exchange(ExchangeBase):
|
|
377
377
|
trades.append(trade)
|
378
378
|
return trades
|
379
379
|
|
380
|
-
def
|
380
|
+
def _recalc_aggregates(self):
|
381
|
+
"""基于 self.account 中已保存的各 symbol 状态,重算聚合字段。"""
|
382
|
+
usdt = self.account['USDT']
|
383
|
+
usdt['unrealised_profit'] = 0
|
384
|
+
usdt['hold'] = 0
|
385
|
+
usdt['long'] = 0
|
386
|
+
usdt['short'] = 0
|
387
|
+
|
388
|
+
for symbol in self.trade_symbols:
|
389
|
+
if symbol not in self.account:
|
390
|
+
continue
|
391
|
+
sym = self.account[symbol]
|
392
|
+
px = sym.get('price', 0)
|
393
|
+
amt = sym.get('amount', 0)
|
394
|
+
hp = sym.get('hold_price', 0)
|
395
|
+
|
396
|
+
# 仅当价格有效时计入聚合
|
397
|
+
if px is not None and not np.isnan(px) and px != 0:
|
398
|
+
sym['unrealised_profit'] = (px - hp) * amt
|
399
|
+
sym['value'] = amt * px
|
400
|
+
|
401
|
+
if amt > 0:
|
402
|
+
usdt['long'] += sym['value']
|
403
|
+
elif amt < 0:
|
404
|
+
usdt['short'] += sym['value']
|
405
|
+
|
406
|
+
usdt['hold'] += abs(sym['value'])
|
407
|
+
usdt['unrealised_profit'] += sym['unrealised_profit']
|
408
|
+
|
409
|
+
usdt['total'] = round(self.account['USDT']['realised_profit'] + self.initial_balance + usdt['unrealised_profit'], 6)
|
410
|
+
usdt['leverage'] = round(usdt['hold'] / usdt['total'] if usdt['total'] != 0 else 0.0, 3)
|
411
|
+
|
412
|
+
def Update(self, close_price=None, symbols=None, partial=True, **kwargs):
|
413
|
+
"""
|
414
|
+
更新账户状态。
|
415
|
+
- partial=True:只更新给定 symbols 的逐符号状态,然后对所有符号做一次聚合重算(推荐)。
|
416
|
+
- partial=False:与原逻辑兼容;当提供一部分 symbol 时,也会聚合重算,不会清空未提供符号的信息。
|
417
|
+
|
418
|
+
支持三种入参形式:
|
419
|
+
1) close_price 为 dict/Series:symbols 自动取其键/索引
|
420
|
+
2) close_price 为标量 + symbols 为单个字符串
|
421
|
+
3) 显式传 symbols=list[...],close_price 为 dict/Series(从中取价)
|
422
|
+
如果既不传 close_price 也不传 symbols,则只做一次聚合重算(例如你先前已经手动修改了某些 symbol 的 price)。
|
423
|
+
"""
|
381
424
|
if self.recorded and 'time' not in kwargs:
|
382
425
|
raise ValueError("Time parameter is required in recorded mode.")
|
383
426
|
|
384
427
|
time = kwargs.get('time', pd.Timestamp.now())
|
385
|
-
|
386
|
-
|
387
|
-
self.account['USDT']['long'] = 0
|
388
|
-
self.account['USDT']['short'] = 0
|
428
|
+
|
429
|
+
# 解析 symbols & 价格获取器
|
389
430
|
if symbols is None:
|
390
|
-
# symbols = self.trade_symbols
|
391
|
-
# 如果symbols是dict类型, 则取出所有的key, 如果是Series类型, 则取出所有的index
|
392
431
|
if isinstance(close_price, dict):
|
393
432
|
symbols = list(close_price.keys())
|
394
433
|
elif isinstance(close_price, pd.Series):
|
395
|
-
symbols = close_price.index
|
434
|
+
symbols = list(close_price.index)
|
396
435
|
else:
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
436
|
+
symbols = []
|
437
|
+
elif isinstance(symbols, str):
|
438
|
+
symbols = [symbols]
|
439
|
+
|
440
|
+
def get_px(sym):
|
441
|
+
if isinstance(close_price, (int, float, np.floating)) and len(symbols) == 1:
|
442
|
+
return float(close_price)
|
443
|
+
if isinstance(close_price, dict):
|
444
|
+
return close_price.get(sym, np.nan)
|
445
|
+
if isinstance(close_price, pd.Series):
|
446
|
+
return close_price.get(sym, np.nan)
|
447
|
+
return np.nan
|
448
|
+
|
449
|
+
# 仅更新传入的 symbols(部分更新,不动其它符号已保存信息)
|
450
|
+
for sym in symbols:
|
451
|
+
if sym not in self.trade_symbols or sym not in self.account:
|
452
|
+
# 未登记的交易对直接跳过(或可选择自动登记,但此处保持严格)
|
453
|
+
continue
|
454
|
+
px = get_px(sym)
|
455
|
+
if px is None or np.isnan(px):
|
456
|
+
# 价格无效则不覆盖旧价格
|
401
457
|
continue
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
self.account['USDT']['hold'] += abs(self.account[symbol]['value'])
|
411
|
-
self.account['USDT']['unrealised_profit'] += self.account[symbol]['unrealised_profit']
|
412
|
-
|
413
|
-
self.account['USDT']['total'] = round(self.account['USDT']['realised_profit'] + self.initial_balance + self.account['USDT']['unrealised_profit'], 6)
|
414
|
-
self.account['USDT']['leverage'] = round(self.account['USDT']['hold'] / self.account['USDT']['total'], 3)
|
458
|
+
|
459
|
+
self.account[sym]['price'] = float(px)
|
460
|
+
amt = self.account[sym]['amount']
|
461
|
+
self.account[sym]['value'] = amt * float(px)
|
462
|
+
# 不在这里算 unrealised_profit,聚合阶段统一算
|
463
|
+
|
464
|
+
# 无论 partial 与否,最后都用“账户中保存的所有 symbol 当前状态”做一次聚合重算
|
465
|
+
self._recalc_aggregates()
|
415
466
|
|
416
467
|
# 记录账户总资产到 history
|
417
468
|
if self.recorded:
|
@@ -530,7 +530,7 @@ wheels = [
|
|
530
530
|
|
531
531
|
[[package]]
|
532
532
|
name = "hyperquant"
|
533
|
-
version = "0.
|
533
|
+
version = "0.25"
|
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.9.
|
560
|
+
{ name = "pybotters", specifier = ">=1.9.1" },
|
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.9.
|
1224
|
+
version = "1.9.1"
|
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/0c/c0/182e118d42a818565a5ee807cbce82f435d651ee6e3ab2344a378a285776/pybotters-1.9.1.tar.gz", hash = "sha256:60ebaa3cecd446efd52ed4515038cdacfde298a02137e51aad2185ccf1e5a4d6", size = 559033 }
|
1231
1231
|
wheels = [
|
1232
|
-
{ url = "https://files.pythonhosted.org/packages/
|
1232
|
+
{ url = "https://files.pythonhosted.org/packages/7f/7f/224b3fd8a1e991edc4d5b2d8aeeefb1b75f6c0f1a27eefa8e4ab82bc524d/pybotters-1.9.1-py3-none-any.whl", hash = "sha256:4d75c5b5a103e2fad4b320d8920c1e918c007ce9acb91a7c4e86672986225ebb", size = 519566 },
|
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
|
File without changes
|
File without changes
|