hyperquant 1.0__tar.gz → 1.1__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-1.0 → hyperquant-1.1}/PKG-INFO +1 -1
- {hyperquant-1.0 → hyperquant-1.1}/apis.json +2 -2
- {hyperquant-1.0 → hyperquant-1.1}/pyproject.toml +1 -1
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/bitmart.py +4 -4
- hyperquant-1.1/test.py +48 -0
- hyperquant-1.1/tests/test_bitmart.py +189 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_coinup.py +5 -5
- {hyperquant-1.0 → hyperquant-1.1}/uv.lock +1 -1
- hyperquant-1.0/tests/test_bitmart.py +0 -156
- {hyperquant-1.0 → hyperquant-1.1}/.gitignore +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/.python-version +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/README.md +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/data/alpine_smoke.log +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/data/logs/notikit.log +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/data/logs/test_order_sync.log +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/data/records_swap.csv +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/data/records_swapc.csv +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/doc/bitmart.md +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/doc/coinup.md +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/doc/lbank.md +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/pub.sh +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/requirements-dev.lock +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/requirements.lock +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/__init__.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/auth.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/bitget.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/coinup.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/coinw.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/edgex.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/lbank.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/lib/util.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/lighter.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/bitget.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/bitmart.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/coinup.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/coinw.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/edgex.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/lbank.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/lighter.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/models/ourbit.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/ourbit.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/core.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/db.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/draw.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/logkit.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/src/hyperquant/notikit.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_bitget.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_coinw.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_draw.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_edgex.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_lbank.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_lighter.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/test_ourbit.py +0 -0
- {hyperquant-1.0 → hyperquant-1.1}/tests/tmp.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperquant
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1
|
|
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
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"41933ec0f5dace5eee47628ea2e2cb40a491f7fd20f983b385f8bdc5eea3d3ad4a47bd745f70fc2a"
|
|
25
25
|
],
|
|
26
26
|
"bitmart":[
|
|
27
|
-
"
|
|
28
|
-
"
|
|
27
|
+
"eyJoZWFkZXIiOnsidHlwIjoiQml0TWFydCIsImFsZyI6IkJNQVBJU0lYIn0sInBheWxvYWQiOnsiamlkIjoiMjk4Mjk1NWMzZTBlNDA5YmFjODYyZGNlOWY0MTFhNDkiLCJ2ZXJzaW9uIjoiMjAyNTExMDEiLCJleHBpcmVzQXQiOjE3NjIwNTE3MzI0NTMsImJtIjoiRVhFbjk2UzV2dGRISmtKZ0hYejlPcTA0a0ZTdFdHTWtRWG5yVHpDZktYdW1OOHFIVTh0WExUeXlTaEMzVFJDMlZHOVhuMWxkcVkzdVdlOHluWlBrTGhqQWNIT0VjK2VoeEhlc3VJVUh3U3ArRVFnMEFtY0JIWW53OVRuelRkb2Fob25jakxBNTdtRDVEK2tPSHNWUXdDdElXRGNsTWdpd1d3V3NlY0d5Y1hNdjJJd1pvUVAwZzRlZUtudTgwNDlpZ1RPdERkN0JLenlFN0tONy85ZWpBL1hHQmg4SzBROW9hTmN6aWwwMmxVUnBGNm9wUUJHVTY1eHhBTkMwR0Zyb2RZMmFNalRtQVN2Si9vdmJMNEQxZGtUcUp6azIxdVlhamllRm9OTmREdlZqMXlzOGdTTGJBc3FWMDlsd2x0M3lTWER2L093a0hyclhJMlAyZVZqU2JuK1hPODhKRTlFYWRpTkcwRU9EOWhLRy9YRFhoU0dVT1N5YnJrNWdLdjNoIn0sInNpZ25hdHVyZSI6InQ3SmRFZ3VhTzNrUU9RSlNBcVRqMDVQRCsrcGR6UjlLVDVZTGhNdmNQRDlvc0tWcjRYMEM0VjkwaStYTm0rMFBxNlhuZGo5SjFSeXFtZVBuVG5QUmRBPT0ifQ==",
|
|
28
|
+
"4OamVUwuSznwyStlmRIfc9o0y5tCYdMOh",
|
|
29
29
|
"1c0886dd192a1dd0f23f71f7ab577a45"
|
|
30
30
|
]
|
|
31
31
|
}
|
|
@@ -363,7 +363,7 @@ class Bitmart:
|
|
|
363
363
|
if detail is None:
|
|
364
364
|
raise ValueError(f"Market metadata unavailable for symbol: {symbol}")
|
|
365
365
|
|
|
366
|
-
if
|
|
366
|
+
if qty is not None:
|
|
367
367
|
|
|
368
368
|
contract_size_str = detail.get("contract_size") or detail.get("vol_unit") or "1"
|
|
369
369
|
try:
|
|
@@ -380,10 +380,10 @@ class Bitmart:
|
|
|
380
380
|
f"Volume too small for contract size ({contract_size_val}): volume={qty}"
|
|
381
381
|
)
|
|
382
382
|
|
|
383
|
-
|
|
384
|
-
contracts_int = int(
|
|
383
|
+
if qty_contract is not None:
|
|
384
|
+
contracts_int = int(qty_contract)
|
|
385
385
|
if contracts_int <= 0:
|
|
386
|
-
raise ValueError(f"Volume must be positive integer contracts: volume={
|
|
386
|
+
raise ValueError(f"Volume must be positive integer contracts: volume={qty_contract}")
|
|
387
387
|
|
|
388
388
|
price_unit = detail.get("price_unit") or 1
|
|
389
389
|
try:
|
hyperquant-1.1/test.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import aiohttp
|
|
3
|
+
import time
|
|
4
|
+
from rnet import Client # rnet 需要 pip install rnet
|
|
5
|
+
|
|
6
|
+
URL = "https://futures.ourbit.com/api/v1/contract/systemSetting"
|
|
7
|
+
|
|
8
|
+
HEADERS = {
|
|
9
|
+
"accept": "*/*",
|
|
10
|
+
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
11
|
+
"cache-control": "no-cache",
|
|
12
|
+
"language": "Chinese",
|
|
13
|
+
"pragma": "no-cache",
|
|
14
|
+
"priority": "u=1, i",
|
|
15
|
+
"referer": "https://futures.ourbit.com/zh-CN/exchange/LAYER_USDT",
|
|
16
|
+
"sec-ch-ua": '"Microsoft Edge";v="141", "Not?A_Brand";v="8", "Chromium";v="141"',
|
|
17
|
+
"sec-ch-ua-mobile": "?0",
|
|
18
|
+
"sec-ch-ua-platform": '"macOS"',
|
|
19
|
+
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async def test_aiohttp(n=10):
|
|
23
|
+
async with aiohttp.ClientSession() as session:
|
|
24
|
+
start = time.perf_counter()
|
|
25
|
+
for _ in range(n):
|
|
26
|
+
async with session.get(URL, headers=HEADERS) as resp:
|
|
27
|
+
await resp.text()
|
|
28
|
+
end = time.perf_counter()
|
|
29
|
+
return (end - start) / n
|
|
30
|
+
|
|
31
|
+
async def test_rnet(n=10):
|
|
32
|
+
client = Client()
|
|
33
|
+
start = time.perf_counter()
|
|
34
|
+
for _ in range(n):
|
|
35
|
+
await client.get(URL, headers=HEADERS)
|
|
36
|
+
end = time.perf_counter()
|
|
37
|
+
return (end - start) / n
|
|
38
|
+
|
|
39
|
+
async def main():
|
|
40
|
+
aio_t = await test_aiohttp()
|
|
41
|
+
rnet_t = await test_rnet()
|
|
42
|
+
print(f"Aiohttp 平均响应时间: {aio_t:.3f}s")
|
|
43
|
+
print(f"Rnet 平均响应时间: {rnet_t:.3f}s")
|
|
44
|
+
faster = "aiohttp" if aio_t < rnet_t else "rnet"
|
|
45
|
+
print(f"🏁 更快的是: {faster}")
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import pybotters
|
|
2
|
+
|
|
3
|
+
from hyperquant.broker.lighter import Lighter
|
|
4
|
+
import asyncio
|
|
5
|
+
from logging import Logger
|
|
6
|
+
import time
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Awaitable, Literal
|
|
9
|
+
from hyperquant.logkit import get_logger
|
|
10
|
+
from hyperquant.broker.bitmart import Bitmart
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def test_update():
|
|
15
|
+
async with pybotters.Client(apis='./apis.json') as client:
|
|
16
|
+
async with Bitmart(client=client) as broker:
|
|
17
|
+
# print(broker.store.detail.find()[0])
|
|
18
|
+
# await broker.update('balances')
|
|
19
|
+
# print(broker.store.balances.find())
|
|
20
|
+
# await broker.update('ticker')
|
|
21
|
+
# print(broker.store.ticker.find())
|
|
22
|
+
|
|
23
|
+
# await broker.update('history_orders')
|
|
24
|
+
# print(broker.store.orders.find())
|
|
25
|
+
# print(broker.store.orders.get({'order_id':3000237069605967}))
|
|
26
|
+
await broker.update('positions')
|
|
27
|
+
print(broker.store.positions.find())
|
|
28
|
+
|
|
29
|
+
# print(broker.store.detail.find({'name':'PUMPUSDT'}))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def test_place():
|
|
33
|
+
async with pybotters.Client(apis='./apis.json') as client:
|
|
34
|
+
async with Bitmart(client=client) as broker:
|
|
35
|
+
# [2025-10-31 17:34:15] 🔵 准备建仓: VIRTUAL_USDT entry=short qty=20.498804 lag_px=1.4635 lead=binance
|
|
36
|
+
# [2025-11-01 01:33:22] 🔵 准备建仓: PUMP_USDT entry=long qty=18872.375560 lag_px=0.004239 lead=binance
|
|
37
|
+
# [2025-11-01 01:33:22] 🔔 Order placement failed: Bitmart submitOrder error: {'errno': 'ORDER_LEVERAGE_INFO_NOT_SYNCHRONIZED', 'message': 'Leverage info not synchronized, please place your order later or re
|
|
38
|
+
for i in range(2):
|
|
39
|
+
start = time.time()
|
|
40
|
+
oid = await broker.place_order(
|
|
41
|
+
symbol='PUMPUSDT',
|
|
42
|
+
side='sell',
|
|
43
|
+
category='limit',
|
|
44
|
+
price=0.004239,
|
|
45
|
+
qty=18872.375560,
|
|
46
|
+
mode='ioc',
|
|
47
|
+
leverage=40
|
|
48
|
+
# qty_contract=1
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
print(f"Placed order ID: {oid}, 下单毫秒数: {(time.time() - start)*1000:.2f} ms")
|
|
52
|
+
|
|
53
|
+
# resp = await broker.cancel_order(
|
|
54
|
+
# symbol='VIRTUALUSDT',
|
|
55
|
+
# order_ids=[oid],
|
|
56
|
+
# )
|
|
57
|
+
|
|
58
|
+
# print(f"Cancel response: {resp}")
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class OrderSyncResult:
|
|
62
|
+
position: dict[str, Any]
|
|
63
|
+
order: dict[str, Any]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def order_sync_polling(
|
|
67
|
+
broker: Bitmart,
|
|
68
|
+
*,
|
|
69
|
+
symbol: str,
|
|
70
|
+
place_task: Any,
|
|
71
|
+
is_ioc: bool = True,
|
|
72
|
+
cancel_retry: int = 3,
|
|
73
|
+
logger: Logger | None = None,
|
|
74
|
+
) -> OrderSyncResult:
|
|
75
|
+
"""Bitmart order sync flow based on doc/bitmart.md.
|
|
76
|
+
|
|
77
|
+
- Awaits the placement task to obtain order id
|
|
78
|
+
- Updates history orders and positions to fetch snapshots
|
|
79
|
+
- If IOC, returns immediately after one sync
|
|
80
|
+
- Otherwise polls and attempts cancel on timeout
|
|
81
|
+
"""
|
|
82
|
+
oid = None
|
|
83
|
+
try:
|
|
84
|
+
started = int(time.time() * 1000)
|
|
85
|
+
oid = await place_task
|
|
86
|
+
latency = int(time.time() * 1000) - started
|
|
87
|
+
if logger:
|
|
88
|
+
logger.info(f"Order placed, id={oid}, latency={latency} ms")
|
|
89
|
+
except Exception as e: # pragma: no cover - defensive logging
|
|
90
|
+
if logger:
|
|
91
|
+
logger.warning(f"Order placement failed: {e}")
|
|
92
|
+
return OrderSyncResult(position={}, order={})
|
|
93
|
+
|
|
94
|
+
async def sync() -> OrderSyncResult:
|
|
95
|
+
await broker.update("history_orders")
|
|
96
|
+
await broker.update("positions")
|
|
97
|
+
order = broker.store.orders.get({"order_id": oid}) if oid else None
|
|
98
|
+
position: dict[str, Any] = {}
|
|
99
|
+
if order:
|
|
100
|
+
contract_id = order.get("contract_id")
|
|
101
|
+
if contract_id is not None:
|
|
102
|
+
positions = broker.store.positions.find({"contract_id": contract_id})
|
|
103
|
+
if positions:
|
|
104
|
+
position = positions[0]
|
|
105
|
+
return OrderSyncResult(position=position or {}, order=order or {})
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
for attempt in range(cancel_retry):
|
|
109
|
+
await asyncio.sleep(1)
|
|
110
|
+
await broker.update("history_orders")
|
|
111
|
+
order = broker.store.orders.get({"order_id": oid}) if oid else None
|
|
112
|
+
# 说明订单已经置入历史委托
|
|
113
|
+
if order:
|
|
114
|
+
return await sync()
|
|
115
|
+
else:
|
|
116
|
+
try:
|
|
117
|
+
# 撤单需要 symbol + order_ids
|
|
118
|
+
await broker.cancel_order(symbol, order_ids=[oid])
|
|
119
|
+
except Exception as e: # pragma: no cover - defensive logging
|
|
120
|
+
if logger:
|
|
121
|
+
logger.warning(
|
|
122
|
+
f"Order cancellation attempt {attempt + 1} failed: {e}"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return OrderSyncResult(position={}, order={})
|
|
126
|
+
|
|
127
|
+
async def test_sub_book():
|
|
128
|
+
async with pybotters.Client() as client:
|
|
129
|
+
async with Bitmart(client=client) as broker:
|
|
130
|
+
broker.store.book.limit = 1
|
|
131
|
+
await broker.sub_orderbook(["TRXUSDT", 'XRPUSDT', 'DOTUSDT'])
|
|
132
|
+
while True:
|
|
133
|
+
# await broker.store.book.wait()
|
|
134
|
+
# print(broker.store.book.find())
|
|
135
|
+
asks = broker.store.book.find({"S": "a", 's': 'TRXUSDT'})
|
|
136
|
+
bids = broker.store.book.find({"S": "b", 's': 'TRXUSDT'})
|
|
137
|
+
# 订单薄format化输出
|
|
138
|
+
print("Asks:")
|
|
139
|
+
for ask in asks:
|
|
140
|
+
print(f"Price: {ask['p']}, Quantity: {ask['q']}")
|
|
141
|
+
print("Bids:")
|
|
142
|
+
for bid in bids:
|
|
143
|
+
print(f"Price: {bid['p']}, Quantity: {bid['q']}")
|
|
144
|
+
print("-" * 30)
|
|
145
|
+
await asyncio.sleep(1)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# with broker.store.book.watch() as stream:
|
|
149
|
+
# async for change in stream:
|
|
150
|
+
# print(change)
|
|
151
|
+
|
|
152
|
+
async def test_auto_refresh():
|
|
153
|
+
async with pybotters.Client(apis='./apis.json') as client:
|
|
154
|
+
async with Bitmart(client=client, apis='./apis.json') as broker:
|
|
155
|
+
await broker.auto_refresh(0, test=True)
|
|
156
|
+
await asyncio.sleep(2)
|
|
157
|
+
|
|
158
|
+
async def test_sync_place():
|
|
159
|
+
logger = get_logger("test_sync_place")
|
|
160
|
+
async with pybotters.Client(apis='./apis.json') as client:
|
|
161
|
+
async with Bitmart(client=client) as broker:
|
|
162
|
+
# place_task = broker.place_order(
|
|
163
|
+
# symbol='VIRTUALUSDT',
|
|
164
|
+
# side='sell',
|
|
165
|
+
# category='limit',
|
|
166
|
+
# mode='ioc',
|
|
167
|
+
# price=1.38,
|
|
168
|
+
# qty=0.1,
|
|
169
|
+
# )
|
|
170
|
+
|
|
171
|
+
place_task = broker.place_order( # type: ignore[attr-defined]
|
|
172
|
+
symbol="VELODROMEUSDT",
|
|
173
|
+
side='sell',
|
|
174
|
+
qty_contract=int(round(1.000000)),
|
|
175
|
+
price=0.040645,
|
|
176
|
+
category='market'
|
|
177
|
+
)
|
|
178
|
+
result = await order_sync_polling(
|
|
179
|
+
broker,
|
|
180
|
+
place_task=place_task,
|
|
181
|
+
is_ioc=False,
|
|
182
|
+
cancel_retry=2,
|
|
183
|
+
symbol="VELODROMEUSDT",
|
|
184
|
+
logger=logger,
|
|
185
|
+
)
|
|
186
|
+
logger.info(f"Order Sync Result: {result}")
|
|
187
|
+
|
|
188
|
+
if __name__ == "__main__":
|
|
189
|
+
asyncio.run(test_sync_place())
|
|
@@ -16,10 +16,10 @@ async def test_update():
|
|
|
16
16
|
async with Coinup(client=client) as broker:
|
|
17
17
|
# await broker.update("detail")
|
|
18
18
|
# print(broker.store.detail.get({"symbol": "WLFI-USDT"}))
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
await broker.update("history_orders")
|
|
22
|
-
print(broker.store.history_orders.find())
|
|
19
|
+
await broker.update("position")
|
|
20
|
+
print(broker.store.position.find())
|
|
21
|
+
# await broker.update("history_orders")
|
|
22
|
+
# print(broker.store.history_orders.find())
|
|
23
23
|
|
|
24
24
|
# await broker.update('orders')
|
|
25
25
|
# print(broker.store.orders.find())
|
|
@@ -282,4 +282,4 @@ async def test_order_sync_polling():
|
|
|
282
282
|
if __name__ == "__main__":
|
|
283
283
|
import asyncio
|
|
284
284
|
|
|
285
|
-
asyncio.run(
|
|
285
|
+
asyncio.run(test_update())
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import pybotters
|
|
2
|
-
|
|
3
|
-
from hyperquant.broker.lighter import Lighter
|
|
4
|
-
import asyncio
|
|
5
|
-
from logging import Logger
|
|
6
|
-
import time
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from typing import Any, Awaitable, Literal
|
|
9
|
-
from hyperquant.logkit import get_logger
|
|
10
|
-
from hyperquant.broker.bitmart import Bitmart
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
async def test_update():
|
|
15
|
-
async with pybotters.Client(apis='./apis.json') as client:
|
|
16
|
-
async with Bitmart(client=client) as broker:
|
|
17
|
-
# print(broker.store.detail.find()[0])
|
|
18
|
-
# await broker.update('balances')
|
|
19
|
-
# print(broker.store.balances.find())
|
|
20
|
-
# await broker.update('ticker')
|
|
21
|
-
# print(broker.store.ticker.find())
|
|
22
|
-
|
|
23
|
-
# await broker.update('history_orders')
|
|
24
|
-
# print(broker.store.orders.find())
|
|
25
|
-
# print(broker.store.orders.get({'order_id':3000237069605967}))
|
|
26
|
-
await broker.update('positions')
|
|
27
|
-
print(broker.store.positions.find())
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
async def test_place():
|
|
31
|
-
async with pybotters.Client(apis='./apis.json') as client:
|
|
32
|
-
async with Bitmart(client=client) as broker:
|
|
33
|
-
# [2025-10-31 17:34:15] 🔵 准备建仓: VIRTUAL_USDT entry=short qty=20.498804 lag_px=1.4635 lead=binance
|
|
34
|
-
oid = await broker.place_order(
|
|
35
|
-
symbol='VIRTUALUSDT',
|
|
36
|
-
side='sell',
|
|
37
|
-
category='limit',
|
|
38
|
-
price=1.4635,
|
|
39
|
-
qty=20.49880423641954,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
print(f"Placed order ID: {oid}")
|
|
43
|
-
|
|
44
|
-
# resp = await broker.cancel_order(
|
|
45
|
-
# symbol='ONDOUSDT',
|
|
46
|
-
# order_ids=[oid],
|
|
47
|
-
# )
|
|
48
|
-
|
|
49
|
-
# print(f"Cancel response: {resp}")
|
|
50
|
-
|
|
51
|
-
@dataclass
|
|
52
|
-
class OrderSyncResult:
|
|
53
|
-
position: dict[str, Any]
|
|
54
|
-
order: dict[str, Any]
|
|
55
|
-
|
|
56
|
-
async def order_sync_polling(
|
|
57
|
-
broker: Bitmart,
|
|
58
|
-
place_task: Awaitable,
|
|
59
|
-
is_ioc: bool = True,
|
|
60
|
-
cancel_retry: int = 3,
|
|
61
|
-
logger: Logger | None = None,
|
|
62
|
-
) -> OrderSyncResult:
|
|
63
|
-
try:
|
|
64
|
-
oid = await place_task
|
|
65
|
-
except Exception as e:
|
|
66
|
-
if logger:
|
|
67
|
-
logger.warning(f"Order placement failed: {e}")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
async def sync():
|
|
71
|
-
await broker.update('history_orders')
|
|
72
|
-
await broker.update('positions')
|
|
73
|
-
order = broker.store.orders.get({'order_id': oid})
|
|
74
|
-
if order:
|
|
75
|
-
contract_id = order.get('contract_id')
|
|
76
|
-
positions = broker.store.positions.find({'contract_id': contract_id})
|
|
77
|
-
position = positions[0] if positions else {}
|
|
78
|
-
return OrderSyncResult(position=position, order=order)
|
|
79
|
-
|
|
80
|
-
if is_ioc:
|
|
81
|
-
return await sync()
|
|
82
|
-
else:
|
|
83
|
-
for attempt in range(cancel_retry):
|
|
84
|
-
await asyncio.sleep(1)
|
|
85
|
-
await broker.update('history_orders')
|
|
86
|
-
order = broker.store.orders.get({'order_id': oid})
|
|
87
|
-
# 说明订单已经置入历史委托
|
|
88
|
-
if order:
|
|
89
|
-
return await sync()
|
|
90
|
-
else:
|
|
91
|
-
try:
|
|
92
|
-
await broker.cancel_order(
|
|
93
|
-
symbol=order.get('symbol'),
|
|
94
|
-
order_ids=[oid],
|
|
95
|
-
)
|
|
96
|
-
except Exception as e:
|
|
97
|
-
if logger:
|
|
98
|
-
logger.warning(f"Order cancellation attempt {attempt + 1} failed: {e}")
|
|
99
|
-
|
|
100
|
-
return OrderSyncResult(position={}, order={})
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
async def test_sub_book():
|
|
104
|
-
async with pybotters.Client() as client:
|
|
105
|
-
async with Bitmart(client=client) as broker:
|
|
106
|
-
broker.store.book.limit = 1
|
|
107
|
-
await broker.sub_orderbook(["TRXUSDT", 'XRPUSDT', 'DOTUSDT'])
|
|
108
|
-
while True:
|
|
109
|
-
# await broker.store.book.wait()
|
|
110
|
-
# print(broker.store.book.find())
|
|
111
|
-
asks = broker.store.book.find({"S": "a", 's': 'TRXUSDT'})
|
|
112
|
-
bids = broker.store.book.find({"S": "b", 's': 'TRXUSDT'})
|
|
113
|
-
# 订单薄format化输出
|
|
114
|
-
print("Asks:")
|
|
115
|
-
for ask in asks:
|
|
116
|
-
print(f"Price: {ask['p']}, Quantity: {ask['q']}")
|
|
117
|
-
print("Bids:")
|
|
118
|
-
for bid in bids:
|
|
119
|
-
print(f"Price: {bid['p']}, Quantity: {bid['q']}")
|
|
120
|
-
print("-" * 30)
|
|
121
|
-
await asyncio.sleep(1)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# with broker.store.book.watch() as stream:
|
|
125
|
-
# async for change in stream:
|
|
126
|
-
# print(change)
|
|
127
|
-
|
|
128
|
-
async def test_auto_refresh():
|
|
129
|
-
async with pybotters.Client(apis='./apis.json') as client:
|
|
130
|
-
async with Bitmart(client=client, apis='./apis.json') as broker:
|
|
131
|
-
await broker.auto_refresh(0, test=True)
|
|
132
|
-
await asyncio.sleep(2)
|
|
133
|
-
|
|
134
|
-
async def test_sync_place():
|
|
135
|
-
logger = get_logger("test_sync_place")
|
|
136
|
-
async with pybotters.Client(apis='./apis.json') as client:
|
|
137
|
-
async with Bitmart(client=client) as broker:
|
|
138
|
-
place_task = broker.place_order(
|
|
139
|
-
symbol='ONDOUSDT',
|
|
140
|
-
side='sell',
|
|
141
|
-
category='market',
|
|
142
|
-
mode='ioc',
|
|
143
|
-
price=0.71,
|
|
144
|
-
qty=0.2,
|
|
145
|
-
)
|
|
146
|
-
result = await order_sync_polling(
|
|
147
|
-
broker,
|
|
148
|
-
place_task,
|
|
149
|
-
is_ioc=False,
|
|
150
|
-
cancel_retry=2,
|
|
151
|
-
logger=logger,
|
|
152
|
-
)
|
|
153
|
-
logger.info(f"Order Sync Result: {result}")
|
|
154
|
-
|
|
155
|
-
if __name__ == "__main__":
|
|
156
|
-
asyncio.run(test_place())
|
|
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
|
|
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
|
|
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
|