httptrading 1.0.0__tar.gz → 1.0.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.
Files changed (23) hide show
  1. {httptrading-1.0.0 → httptrading-1.0.1}/PKG-INFO +34 -29
  2. {httptrading-1.0.0 → httptrading-1.0.1}/README.md +33 -28
  3. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/broker/base.py +7 -1
  4. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/broker/futu.py +3 -3
  5. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/broker/interactive_brokers.py +23 -3
  6. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/broker/tiger.py +3 -3
  7. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/http_server.py +3 -1
  8. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading.egg-info/PKG-INFO +34 -29
  9. {httptrading-1.0.0 → httptrading-1.0.1}/pyproject.toml +1 -1
  10. {httptrading-1.0.0 → httptrading-1.0.1}/LICENSE +0 -0
  11. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/__init__.py +0 -0
  12. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/broker/__init__.py +0 -0
  13. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/broker/longbridge.py +0 -0
  14. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/model.py +0 -0
  15. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/tool/__init__.py +0 -0
  16. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/tool/leaky_bucket.py +0 -0
  17. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/tool/locate.py +0 -0
  18. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading/tool/time.py +0 -0
  19. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading.egg-info/SOURCES.txt +0 -0
  20. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading.egg-info/dependency_links.txt +0 -0
  21. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading.egg-info/requires.txt +0 -0
  22. {httptrading-1.0.0 → httptrading-1.0.1}/httptrading.egg-info/top_level.txt +0 -0
  23. {httptrading-1.0.0 → httptrading-1.0.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: httptrading
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: 统一交易通道的接口服务
5
5
  Author-email: songwei <github@songwei.name>
6
6
  License: MIT
@@ -14,6 +14,10 @@ Dynamic: license-file
14
14
 
15
15
  # httptrading
16
16
 
17
+ ```shell
18
+ pip install httptrading
19
+ ```
20
+
17
21
  项目的用途
18
22
  --------
19
23
 
@@ -221,34 +225,35 @@ GET /httptrading/api/{instanceId}/market/state
221
225
 
222
226
  ```json lines
223
227
  {
224
- "type": "apiResponse",
225
- "instanceId": "ggUqPZbSKuQ7Ewsk",
226
- "broker": "futu",
227
- "brokerDisplay": "富途证券",
228
- "time": "2025-05-28T05:33:42.543109+00:00",
229
- "ex": null,
230
- "marketStatus": {
231
- "securities": { // 证券类市场状态, 以 region 为键的结构
232
- "US": {
233
- "type": "marketStatus",
234
- "region": "US",
235
- "originStatus": "AFTER_HOURS_END", // 交易通道原始市场状态
236
- "unifiedStatus": "CLOSED" // 统一映射的定义
237
- },
238
- "CN": {
239
- "type": "marketStatus",
240
- "region": "CN",
241
- "originStatus": "AFTERNOON",
242
- "unifiedStatus": "RTH"
243
- },
244
- "HK": {
245
- "type": "marketStatus",
246
- "region": "HK",
247
- "originStatus": "AFTERNOON",
248
- "unifiedStatus": "RTH"
249
- }
250
- }
251
- }
228
+ "type": "apiResponse",
229
+ "instanceId": "ggUqPZbSKuQ7Ewsk",
230
+ "broker": "futu",
231
+ "brokerDisplay": "富途证券",
232
+ "time": "2025-05-28T05:33:42.543109+00:00",
233
+ "ex": null,
234
+ "marketStatus": {
235
+ "type": "marketStatusMap",
236
+ "securities": { // 证券类市场状态, 以 region 为键的结构
237
+ "US": {
238
+ "type": "marketStatus",
239
+ "region": "US",
240
+ "originStatus": "AFTER_HOURS_END", // 交易通道原始市场状态
241
+ "unifiedStatus": "CLOSED" // 统一映射的定义
242
+ },
243
+ "CN": {
244
+ "type": "marketStatus",
245
+ "region": "CN",
246
+ "originStatus": "AFTERNOON",
247
+ "unifiedStatus": "RTH"
248
+ },
249
+ "HK": {
250
+ "type": "marketStatus",
251
+ "region": "HK",
252
+ "originStatus": "AFTERNOON",
253
+ "unifiedStatus": "RTH"
254
+ }
255
+ }
256
+ }
252
257
  }
253
258
  ```
254
259
 
@@ -1,5 +1,9 @@
1
1
  # httptrading
2
2
 
3
+ ```shell
4
+ pip install httptrading
5
+ ```
6
+
3
7
  项目的用途
4
8
  --------
5
9
 
@@ -207,34 +211,35 @@ GET /httptrading/api/{instanceId}/market/state
207
211
 
208
212
  ```json lines
209
213
  {
210
- "type": "apiResponse",
211
- "instanceId": "ggUqPZbSKuQ7Ewsk",
212
- "broker": "futu",
213
- "brokerDisplay": "富途证券",
214
- "time": "2025-05-28T05:33:42.543109+00:00",
215
- "ex": null,
216
- "marketStatus": {
217
- "securities": { // 证券类市场状态, 以 region 为键的结构
218
- "US": {
219
- "type": "marketStatus",
220
- "region": "US",
221
- "originStatus": "AFTER_HOURS_END", // 交易通道原始市场状态
222
- "unifiedStatus": "CLOSED" // 统一映射的定义
223
- },
224
- "CN": {
225
- "type": "marketStatus",
226
- "region": "CN",
227
- "originStatus": "AFTERNOON",
228
- "unifiedStatus": "RTH"
229
- },
230
- "HK": {
231
- "type": "marketStatus",
232
- "region": "HK",
233
- "originStatus": "AFTERNOON",
234
- "unifiedStatus": "RTH"
235
- }
236
- }
237
- }
214
+ "type": "apiResponse",
215
+ "instanceId": "ggUqPZbSKuQ7Ewsk",
216
+ "broker": "futu",
217
+ "brokerDisplay": "富途证券",
218
+ "time": "2025-05-28T05:33:42.543109+00:00",
219
+ "ex": null,
220
+ "marketStatus": {
221
+ "type": "marketStatusMap",
222
+ "securities": { // 证券类市场状态, 以 region 为键的结构
223
+ "US": {
224
+ "type": "marketStatus",
225
+ "region": "US",
226
+ "originStatus": "AFTER_HOURS_END", // 交易通道原始市场状态
227
+ "unifiedStatus": "CLOSED" // 统一映射的定义
228
+ },
229
+ "CN": {
230
+ "type": "marketStatus",
231
+ "region": "CN",
232
+ "originStatus": "AFTERNOON",
233
+ "unifiedStatus": "RTH"
234
+ },
235
+ "HK": {
236
+ "type": "marketStatus",
237
+ "region": "HK",
238
+ "originStatus": "AFTERNOON",
239
+ "unifiedStatus": "RTH"
240
+ }
241
+ }
242
+ }
238
243
  }
239
244
  ```
240
245
 
@@ -81,7 +81,13 @@ class BaseBroker(ABC):
81
81
  async def quote(self, contract: Contract) -> Quote:
82
82
  raise NotImplementedError
83
83
 
84
- async def market_status(self) -> dict[str, dict[str, MarketStatus]]:
84
+ async def market_status(self) -> dict[TradeType, dict[str, MarketStatus] | str]:
85
+ """
86
+ 报告交易通道提供的市场状态,
87
+ 返回一个双层字典,
88
+ 外层字典是以交易品种分类的结构, 比如 TradeType.Securities,
89
+ 内层的字典是按国家代码区分的各个市场状态的结构, 比如 "US".
90
+ """
85
91
  raise NotImplementedError
86
92
 
87
93
 
@@ -166,7 +166,7 @@ class Futu(SecuritiesBroker):
166
166
  async def cash(self) -> Cash:
167
167
  return await self.call_sync(lambda : self._cash())
168
168
 
169
- def _market_status(self) -> dict[str, dict[str, MarketStatus]]:
169
+ def _market_status(self) -> dict[TradeType, dict[str, MarketStatus] | str]:
170
170
  # 各个市场的状态定义见:
171
171
  # https://openapi.futunn.com/futu-api-doc/qa/quote.html#2090
172
172
  from futu import RET_OK
@@ -204,10 +204,10 @@ class Futu(SecuritiesBroker):
204
204
  unified_status=unified_status,
205
205
  )
206
206
  return {
207
- TradeType.Securities.name.lower(): sec_result,
207
+ TradeType.Securities: sec_result,
208
208
  }
209
209
 
210
- async def market_status(self) -> dict[str, dict[str, MarketStatus]]:
210
+ async def market_status(self) -> dict[TradeType, dict[str, MarketStatus] | str]:
211
211
  return await self.call_sync(lambda : self._market_status())
212
212
 
213
213
  def _quote(self, contract: Contract):
@@ -5,6 +5,7 @@ https://ib-insync.readthedocs.io/readme.html
5
5
  import re
6
6
  import asyncio
7
7
  from typing import Any
8
+ from collections import defaultdict
8
9
  from httptrading.tool.leaky_bucket import *
9
10
  from httptrading.tool.time import *
10
11
  from httptrading.broker.base import *
@@ -74,7 +75,6 @@ class InteractiveBrokers(SecuritiesBroker):
74
75
  async with self._lock:
75
76
  if contract in self._ib_contracts:
76
77
  return self._ib_contracts[contract]
77
- print(f'{contract}未命中')
78
78
  currency = self.contract_to_currency(contract)
79
79
  ib_contract = ib_insync.Stock(contract.ticker, 'SMART', currency=currency)
80
80
  client = self._client
@@ -82,6 +82,15 @@ class InteractiveBrokers(SecuritiesBroker):
82
82
  self._ib_contracts[contract] = ib_contract
83
83
  return ib_contract
84
84
 
85
+ def _when_create_client(self, client):
86
+ import ib_insync
87
+ client: ib_insync.IB = client
88
+
89
+ def _order_status_changed(trade: ib_insync.Trade):
90
+ pass
91
+
92
+ client.orderStatusEvent += _order_status_changed
93
+
85
94
  async def _try_create_client(self):
86
95
  import ib_insync
87
96
  async with self._lock:
@@ -101,6 +110,7 @@ class InteractiveBrokers(SecuritiesBroker):
101
110
  ib = ib_socket
102
111
  else:
103
112
  ib = ib_insync.IB()
113
+ self._when_create_client(ib)
104
114
  host = self.broker_args.get('host', '127.0.0.1')
105
115
  port = self.broker_args.get('port', 4000)
106
116
  client_id = self.broker_args.get('client_id', self._client_id)
@@ -222,13 +232,23 @@ class InteractiveBrokers(SecuritiesBroker):
222
232
  case _:
223
233
  raise Exception(f'不支持的订单类型: {order_type}')
224
234
 
235
+ evt = asyncio.Event()
236
+ def _status_evnet(_trade: ib_insync.Trade):
237
+ evt.set()
238
+
225
239
  ib_order = _map_order()
226
240
  ib_order.tif = _map_time_in_force()
227
241
  ib_order.outsideRth = _map_lifecycle()
228
242
  trade: ib_insync.Trade = client.placeOrder(ib_contract, ib_order)
229
- await asyncio.sleep(2.0)
230
- order_id = str(trade.order.permId)
243
+ trade.statusEvent += _status_evnet
244
+ try:
245
+ await asyncio.wait_for(evt.wait(), timeout=2.0)
246
+ except asyncio.TimeoutError:
247
+ pass
248
+ finally:
249
+ order_id = str(trade.order.permId)
231
250
  assert order_id
251
+ order_id = str(trade.order.permId)
232
252
  return order_id
233
253
 
234
254
  async def place_order(
@@ -142,7 +142,7 @@ class Tiger(SecuritiesBroker):
142
142
  async def cash(self) -> Cash:
143
143
  return await self.call_sync(lambda: self._cash())
144
144
 
145
- def _market_status(self) -> dict[str, dict[str, MarketStatus]]:
145
+ def _market_status(self) -> dict[TradeType, dict[str, MarketStatus] | str]:
146
146
  from tigeropen.common.consts import Market
147
147
  from tigeropen.quote.domain.market_status import MarketStatus as TigerMarketStatus
148
148
  client = self._quote_client
@@ -172,10 +172,10 @@ class Tiger(SecuritiesBroker):
172
172
  unified_status=unified_status,
173
173
  )
174
174
  return {
175
- TradeType.Securities.name.lower(): sec_result,
175
+ TradeType.Securities: sec_result,
176
176
  }
177
177
 
178
- async def market_status(self) -> dict[str, dict[str, MarketStatus]]:
178
+ async def market_status(self) -> dict[TradeType, dict[str, MarketStatus] | str]:
179
179
  return await self.call_sync(lambda: self._market_status())
180
180
 
181
181
  def _quote(self, contract: Contract):
@@ -19,7 +19,7 @@ class HttpTradingView(web.View):
19
19
  def instance_id(self) -> str:
20
20
  return self.request.match_info.get('instance_id', '')
21
21
 
22
- def current_broker(self):
22
+ def current_broker(self) -> BaseBroker:
23
23
  broker = getattr(self.request, '__current_broker__', None)
24
24
  if broker is None:
25
25
  raise web.HTTPNotFound()
@@ -212,6 +212,8 @@ class MarketStatusView(HttpTradingView):
212
212
  async def get(self):
213
213
  broker = self.current_broker()
214
214
  ms_dict = await broker.market_status()
215
+ ms_dict = {t.name.lower(): d for t, d in ms_dict.items()}
216
+ ms_dict['type'] = 'marketStatusMap'
215
217
  return self.response_api(broker, {
216
218
  'marketStatus': ms_dict,
217
219
  })
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: httptrading
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: 统一交易通道的接口服务
5
5
  Author-email: songwei <github@songwei.name>
6
6
  License: MIT
@@ -14,6 +14,10 @@ Dynamic: license-file
14
14
 
15
15
  # httptrading
16
16
 
17
+ ```shell
18
+ pip install httptrading
19
+ ```
20
+
17
21
  项目的用途
18
22
  --------
19
23
 
@@ -221,34 +225,35 @@ GET /httptrading/api/{instanceId}/market/state
221
225
 
222
226
  ```json lines
223
227
  {
224
- "type": "apiResponse",
225
- "instanceId": "ggUqPZbSKuQ7Ewsk",
226
- "broker": "futu",
227
- "brokerDisplay": "富途证券",
228
- "time": "2025-05-28T05:33:42.543109+00:00",
229
- "ex": null,
230
- "marketStatus": {
231
- "securities": { // 证券类市场状态, 以 region 为键的结构
232
- "US": {
233
- "type": "marketStatus",
234
- "region": "US",
235
- "originStatus": "AFTER_HOURS_END", // 交易通道原始市场状态
236
- "unifiedStatus": "CLOSED" // 统一映射的定义
237
- },
238
- "CN": {
239
- "type": "marketStatus",
240
- "region": "CN",
241
- "originStatus": "AFTERNOON",
242
- "unifiedStatus": "RTH"
243
- },
244
- "HK": {
245
- "type": "marketStatus",
246
- "region": "HK",
247
- "originStatus": "AFTERNOON",
248
- "unifiedStatus": "RTH"
249
- }
250
- }
251
- }
228
+ "type": "apiResponse",
229
+ "instanceId": "ggUqPZbSKuQ7Ewsk",
230
+ "broker": "futu",
231
+ "brokerDisplay": "富途证券",
232
+ "time": "2025-05-28T05:33:42.543109+00:00",
233
+ "ex": null,
234
+ "marketStatus": {
235
+ "type": "marketStatusMap",
236
+ "securities": { // 证券类市场状态, 以 region 为键的结构
237
+ "US": {
238
+ "type": "marketStatus",
239
+ "region": "US",
240
+ "originStatus": "AFTER_HOURS_END", // 交易通道原始市场状态
241
+ "unifiedStatus": "CLOSED" // 统一映射的定义
242
+ },
243
+ "CN": {
244
+ "type": "marketStatus",
245
+ "region": "CN",
246
+ "originStatus": "AFTERNOON",
247
+ "unifiedStatus": "RTH"
248
+ },
249
+ "HK": {
250
+ "type": "marketStatus",
251
+ "region": "HK",
252
+ "originStatus": "AFTERNOON",
253
+ "unifiedStatus": "RTH"
254
+ }
255
+ }
256
+ }
252
257
  }
253
258
  ```
254
259
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "httptrading"
3
- version = "1.0.0"
3
+ version = "1.0.1"
4
4
  description = "统一交易通道的接口服务"
5
5
  authors = [
6
6
  {name = "songwei", email = "github@songwei.name"},
File without changes
File without changes