hyperquant 0.42__tar.gz → 0.44__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.42 → hyperquant-0.44}/PKG-INFO +1 -1
- {hyperquant-0.42 → hyperquant-0.44}/pyproject.toml +1 -1
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/models/ourbit.py +6 -3
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/ourbit.py +50 -52
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/core.py +9 -6
- hyperquant-0.44/test.py +55 -0
- {hyperquant-0.42 → hyperquant-0.44}/tmp.py +18 -16
- {hyperquant-0.42 → hyperquant-0.44}/uv.lock +1 -1
- hyperquant-0.42/test.py +0 -8
- {hyperquant-0.42 → hyperquant-0.44}/.gitignore +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/.python-version +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/README.md +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/data/logs/notikit.log +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/pub.sh +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/requirements-dev.lock +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/requirements.lock +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/__init__.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/auth.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/db.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/draw.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/logkit.py +0 -0
- {hyperquant-0.42 → hyperquant-0.44}/src/hyperquant/notikit.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hyperquant
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.44
|
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
|
@@ -592,7 +592,11 @@ class SpotBalance(DataStore):
|
|
592
592
|
def _on_message(self, msg: dict[str, Any]) -> None:
|
593
593
|
data = msg.get("d", {})
|
594
594
|
item = self._fmt_ws(data)
|
595
|
-
|
595
|
+
av = float(item.get("available", 0))
|
596
|
+
if av == 0:
|
597
|
+
self._find_and_delete({'currency': item.get("currency")})
|
598
|
+
else:
|
599
|
+
self._update([item])
|
596
600
|
|
597
601
|
|
598
602
|
# SpotOrders: 现货订单数据存储
|
@@ -929,8 +933,7 @@ class OurbitSpotDataStore(DataStoreCollection):
|
|
929
933
|
"""
|
930
934
|
现货账户余额数据流
|
931
935
|
|
932
|
-
|
933
|
-
|
936
|
+
_KEYS = ["currency"]
|
934
937
|
.. code:: python
|
935
938
|
|
936
939
|
[
|
@@ -427,7 +427,7 @@ class OurbitSpot:
|
|
427
427
|
self,
|
428
428
|
symbol: str,
|
429
429
|
side: Literal["buy", "sell"],
|
430
|
-
price: float,
|
430
|
+
price: float = None,
|
431
431
|
quantity: float = None,
|
432
432
|
order_type: Literal["market", "limit"] = "limit",
|
433
433
|
usdt_amount: float = None
|
@@ -437,7 +437,7 @@ class OurbitSpot:
|
|
437
437
|
Args:
|
438
438
|
symbol: 交易对,如 "SOL_USDT"
|
439
439
|
side: 买卖方向 "buy" 或 "sell"
|
440
|
-
price:
|
440
|
+
price: 价格,市价单可为None
|
441
441
|
quantity: 数量
|
442
442
|
order_type: 订单类型 "market" 或 "limit"
|
443
443
|
usdt_amount: USDT金额,如果指定则根据价格计算数量
|
@@ -445,74 +445,72 @@ class OurbitSpot:
|
|
445
445
|
Returns:
|
446
446
|
订单响应数据
|
447
447
|
"""
|
448
|
+
# 参数检查
|
449
|
+
if order_type == "limit" and price is None:
|
450
|
+
raise ValueError("Limit orders require a price")
|
451
|
+
if quantity is None and usdt_amount is None:
|
452
|
+
raise ValueError("Either quantity or usdt_amount must be specified")
|
453
|
+
|
448
454
|
# 解析交易对
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
+
parts = symbol.split("_")
|
456
|
+
if len(parts) != 2:
|
457
|
+
raise ValueError(f"Invalid symbol format: {symbol}")
|
458
|
+
|
459
|
+
currency, market = parts
|
460
|
+
|
461
|
+
# 获取交易对详情
|
462
|
+
detail = self.store.detail.get({"name": currency})
|
455
463
|
if not detail:
|
456
464
|
raise ValueError(f"Unknown currency: {currency}")
|
457
|
-
|
458
|
-
price_scale = detail.get(
|
459
|
-
quantity_scale = detail.get(
|
460
|
-
|
461
|
-
use_quantity = True if quantity is not None else False
|
462
|
-
|
463
|
-
# 如果指定了USDT金额,重新计算数量
|
464
|
-
if usdt_amount is not None:
|
465
|
-
if side == "buy":
|
466
|
-
quantity = usdt_amount / price
|
467
|
-
else:
|
468
|
-
# 卖出时usdt_amount表示要卖出的币种价值
|
469
|
-
quantity = usdt_amount / price
|
470
|
-
|
471
|
-
# 格式化价格和数量
|
472
|
-
if price_scale is not None:
|
473
|
-
price = round(price, price_scale)
|
474
|
-
|
475
|
-
if quantity_scale is not None:
|
476
|
-
quantity = round(quantity, quantity_scale)
|
465
|
+
|
466
|
+
price_scale = detail.get("price_scale")
|
467
|
+
quantity_scale = detail.get("quantity_scale")
|
477
468
|
|
478
469
|
# 构建请求数据
|
479
470
|
data = {
|
480
471
|
"currency": currency,
|
481
472
|
"market": market,
|
482
|
-
"tradeType": side.upper()
|
483
|
-
"quantity": str(quantity),
|
473
|
+
"tradeType": side.upper()
|
484
474
|
}
|
485
475
|
|
476
|
+
# 处理市价单和限价单的不同参数
|
486
477
|
if order_type == "limit":
|
487
478
|
data["orderType"] = "LIMIT_ORDER"
|
488
|
-
data["price"] = str(price)
|
479
|
+
data["price"] = str(round(price, price_scale) if price_scale is not None else price)
|
480
|
+
|
481
|
+
# 计算并设置数量
|
482
|
+
if quantity is None and usdt_amount is not None and price:
|
483
|
+
quantity = usdt_amount / price
|
484
|
+
|
485
|
+
if quantity_scale is not None:
|
486
|
+
quantity = round(quantity, quantity_scale)
|
487
|
+
data["quantity"] = str(quantity)
|
488
|
+
|
489
489
|
elif order_type == "market":
|
490
490
|
data["orderType"] = "MARKET_ORDER"
|
491
|
-
|
492
|
-
#
|
493
|
-
if not
|
494
|
-
del data["quantity"]
|
491
|
+
|
492
|
+
# 市价单可以使用数量或金额,但不能同时使用
|
493
|
+
if usdt_amount is not None:
|
495
494
|
data["amount"] = str(usdt_amount)
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
495
|
+
else:
|
496
|
+
if quantity_scale is not None:
|
497
|
+
quantity = round(quantity, quantity_scale)
|
498
|
+
data["quantity"] = str(quantity)
|
499
|
+
|
500
|
+
if price:
|
501
|
+
data["price"] = str(price)
|
502
502
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
)
|
503
|
+
# 确定API端点
|
504
|
+
url = f'{self.api_url}/api/platform/spot/{"v4/order/place" if order_type == "market" else "order/place"}'
|
505
|
+
|
506
|
+
# 发送请求
|
507
|
+
res = await self.client.fetch("POST", url, json=data)
|
508
508
|
|
509
509
|
# 处理响应
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
raise Exception(f"Failed to place order: {res.data}")
|
515
|
-
|
510
|
+
if res.data.get("msg") == "success":
|
511
|
+
return res.data["data"]
|
512
|
+
raise Exception(f"Failed to place order: {res.data}")
|
513
|
+
|
516
514
|
async def cancel_orders(self, order_ids: list[str]):
|
517
515
|
|
518
516
|
for order_id in order_ids:
|
@@ -269,10 +269,10 @@ class ExchangeBase:
|
|
269
269
|
)
|
270
270
|
|
271
271
|
class Exchange(ExchangeBase):
|
272
|
-
def __init__(self, trade_symbols, fee=0.0002, initial_balance=10000, recorded=False):
|
272
|
+
def __init__(self, trade_symbols:list=[], fee=0.0002, initial_balance=10000, recorded=False):
|
273
273
|
super().__init__(initial_balance=initial_balance, recorded=recorded)
|
274
274
|
self.fee = fee
|
275
|
-
self.trade_symbols = trade_symbols
|
275
|
+
self.trade_symbols:list = trade_symbols
|
276
276
|
self.id_gen = 0
|
277
277
|
self.account['USDT'].update({
|
278
278
|
'hold': 0,
|
@@ -280,8 +280,12 @@ class Exchange(ExchangeBase):
|
|
280
280
|
'short': 0
|
281
281
|
})
|
282
282
|
for symbol in trade_symbols:
|
283
|
-
self.account[symbol] =
|
284
|
-
|
283
|
+
self.account[symbol] = self._act_template
|
284
|
+
|
285
|
+
@property
|
286
|
+
def _act_template(self):
|
287
|
+
return {'amount': 0, 'hold_price': 0, 'value': 0, 'price': 0,
|
288
|
+
'realised_profit': 0, 'unrealised_profit': 0, 'fee': 0}.copy()
|
285
289
|
|
286
290
|
def Trade(self, symbol, direction, price, amount, **kwargs):
|
287
291
|
if self.recorded and 'time' not in kwargs:
|
@@ -307,8 +311,7 @@ class Exchange(ExchangeBase):
|
|
307
311
|
|
308
312
|
if symbol not in self.trade_symbols:
|
309
313
|
self.trade_symbols.append(symbol)
|
310
|
-
self.account[symbol] =
|
311
|
-
'realised_profit': 0, 'unrealised_profit': 0, 'fee': 0}
|
314
|
+
self.account[symbol] = self._act_template
|
312
315
|
|
313
316
|
cover_amount = 0 if direction * self.account[symbol]['amount'] >= 0 else min(abs(self.account[symbol]['amount']), amount)
|
314
317
|
open_amount = amount - cover_amount
|
hyperquant-0.44/test.py
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
import asyncio
|
2
|
+
import random
|
3
|
+
import time
|
4
|
+
import pybotters
|
5
|
+
import hyperquant
|
6
|
+
from hyperquant.broker.models.ourbit import OurbitSpotDataStore
|
7
|
+
from hyperquant.broker.ourbit import OurbitSpot
|
8
|
+
from hyperquant.core import Exchange
|
9
|
+
|
10
|
+
|
11
|
+
async def main():
|
12
|
+
store = OurbitSpotDataStore()
|
13
|
+
|
14
|
+
async with pybotters.Client(
|
15
|
+
apis={
|
16
|
+
"ourbit": [
|
17
|
+
"WEB3bf088f8b2f2fae07592fe1a6240e2d798100a9cb2a91f8fda1056b6865ab3ee"
|
18
|
+
]
|
19
|
+
}
|
20
|
+
) as client:
|
21
|
+
res = await client.fetch("POST", "https://www.ourbit.com/ucenter/api/user_info")
|
22
|
+
|
23
|
+
ob_spot = OurbitSpot(client)
|
24
|
+
await ob_spot.__aenter__()
|
25
|
+
await ob_spot.update('balance')
|
26
|
+
|
27
|
+
e = Exchange([], fee=0, initial_balance=0)
|
28
|
+
|
29
|
+
print(ob_spot.store.balance.find())
|
30
|
+
|
31
|
+
for info in ob_spot.store.balance.find():
|
32
|
+
coin = info['currency']
|
33
|
+
if info['currency'] == 'USDT':
|
34
|
+
e.account['USDT']['total'] = float(info['available'])
|
35
|
+
else:
|
36
|
+
if coin not in e.account:
|
37
|
+
e.account[coin] = e._act_template
|
38
|
+
e.account[coin]['amount'] = float(info['available'])
|
39
|
+
e.account[coin]['hold_price'] = float(info['avg_price'])
|
40
|
+
e.account[coin]['price'] = float(info['avg_price'])
|
41
|
+
e.account[coin]['value'] = float(info['avg_price']) * float(info['available'])
|
42
|
+
|
43
|
+
# "amount": 1,
|
44
|
+
# "hold_price": 1000.0,
|
45
|
+
# "value": 1000.0,
|
46
|
+
# "price": 1000.0,
|
47
|
+
print(e.account)
|
48
|
+
|
49
|
+
|
50
|
+
asyncio.run(main())
|
51
|
+
# e = Exchange([], fee=0)
|
52
|
+
# e.Buy('btc', 1000, 1)
|
53
|
+
# e.Update({'btc':1500})
|
54
|
+
# print(e.account)
|
55
|
+
# print(e.available_margin)
|
@@ -21,29 +21,31 @@ async def main():
|
|
21
21
|
|
22
22
|
ob_spot = OurbitSpot(client)
|
23
23
|
await ob_spot.__aenter__()
|
24
|
-
|
24
|
+
await ob_spot.sub_personal()
|
25
25
|
# await ob_spot.update('ticker')
|
26
26
|
# print(ob_spot.store.ticker.find())
|
27
27
|
|
28
28
|
# await ob_spot.sub_orderbook('DOLO_USDT')
|
29
29
|
# print(ob_spot.store.book.find())
|
30
|
-
await ob_spot.update('
|
31
|
-
symbols = [d['symbol'] for d in ob_spot.store.ticker.find()][:5]
|
30
|
+
await ob_spot.update('balance')
|
31
|
+
# symbols = [d['symbol'] for d in ob_spot.store.ticker.find()][:5]
|
32
32
|
|
33
|
-
await ob_spot.sub_orderbook(symbols)
|
33
|
+
# await ob_spot.sub_orderbook(symbols)
|
34
34
|
|
35
|
-
# print(len(ob_spot.store.book.find()))
|
36
|
-
import pandas as pd
|
37
|
-
print(pd.DataFrame(ob_spot.store.book.find({'S': 'a'})))
|
35
|
+
# # print(len(ob_spot.store.book.find()))
|
36
|
+
# import pandas as pd
|
37
|
+
# print(pd.DataFrame(ob_spot.store.book.find({'S': 'a'})))
|
38
38
|
|
39
39
|
|
40
|
-
while True:
|
41
|
-
await ob_spot.store.book.wait()
|
42
|
-
print(len(ob_spot.store.book.find()))
|
43
|
-
|
44
40
|
# while True:
|
45
|
-
# await ob_spot.store.
|
46
|
-
# print(ob_spot.store.
|
41
|
+
# await ob_spot.store.book.wait()
|
42
|
+
# print(len(ob_spot.store.book.find()))
|
43
|
+
|
44
|
+
|
45
|
+
while True:
|
46
|
+
await ob_spot.store.balance.wait()
|
47
|
+
print(ob_spot.store.balance.find())
|
48
|
+
|
47
49
|
return
|
48
50
|
|
49
51
|
await store.initialize(
|
@@ -131,9 +133,9 @@ async def test_cancel_order():
|
|
131
133
|
# await ob_spot.sub_personal()
|
132
134
|
# await ob_spot.update('all')
|
133
135
|
|
134
|
-
oid = await ob_spot.place_order('SOL_USDT', '
|
136
|
+
oid = await ob_spot.place_order('SOL_USDT', 'sell', order_type='market', quantity=0.03 )
|
135
137
|
print(oid)
|
136
|
-
await ob_spot.cancel_orders([oid])
|
138
|
+
# await ob_spot.cancel_orders([oid])
|
137
139
|
|
138
140
|
if __name__ == "__main__":
|
139
|
-
asyncio.run(
|
141
|
+
asyncio.run(test_cancel_order())
|
hyperquant-0.42/test.py
DELETED
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
|
File without changes
|
File without changes
|
File without changes
|