Skip to content

Commit 4132e32

Browse files
committed
future depth
1 parent 6131bba commit 4132e32

File tree

6 files changed

+187
-168
lines changed

6 files changed

+187
-168
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 3.4.5 (2025-08-22)
2+
### New
3+
- `QuoteClient.get_future_depth` 获取期货深度行情
4+
- QuoteClient 的 `get_depth_quote`, `get_trade_ticks`, `get_timeline`, `get_timeline_history`, `get_bars` 支持 `trade_session` 指定查询夜盘数据
5+
16
## 3.4.4 (2025-07-31)
27
### New
38
选股器支持分页 cursor_id 参数

tests/test_quote_client.py

Lines changed: 91 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
class TestQuoteClient(unittest.TestCase):
3030

3131
def setUp(self):
32-
self.is_mock = False
32+
self.is_mock = True
3333
current_dir = os.path.dirname(__file__)
3434
self.client_config = TigerOpenClientConfig(
3535
props_path=os.path.join(current_dir, ".config/prod_20150899/"))
@@ -344,32 +344,7 @@ def test_get_timeline(self):
344344
"period": "day",
345345
"preClose": 229.09,
346346
"intraday": {
347-
"items": [{
348-
"time": 1754919000000,
349-
"volume": 1656620,
350-
"price": 226.75,
351-
"avgPrice": 227.75438
352-
}, {
353-
"time": 1754919060000,
354-
"volume": 426781,
355-
"price": 226.6,
356-
"avgPrice": 227.51157
357-
}, {
358-
"time": 1754919120000,
359-
"volume": 267382,
360-
"price": 226.31,
361-
"avgPrice": 227.40694
362-
}, {
363-
"time": 1754919180000,
364-
"volume": 322976,
365-
"price": 226.045,
366-
"avgPrice": 227.25978
367-
}, {
368-
"time": 1754919240000,
369-
"volume": 229321,
370-
"price": 226.06,
371-
"avgPrice": 226.0
372-
}],
347+
"items": [{"time": 1754919000000, "volume": 1656620, "price": 226.75, "avgPrice": 227.75438}, {"time": 1754919060000, "volume": 426781, "price": 226.6, "avgPrice": 227.51157}, {"time": 1754919120000, "volume": 267382, "price": 226.31, "avgPrice": 227.40694}, {"time": 1754919180000, "volume": 322976, "price": 226.045, "avgPrice": 227.25978}, {"time": 1754919240000, "volume": 229321, "price": 226.06, "avgPrice": 226.0}],
373348
"beginTime":
374349
-1,
375350
"endTime":
@@ -400,48 +375,20 @@ def test_get_timeline(self):
400375
self.assertEqual(first_row['volume'], 1656620)
401376
self.assertEqual(first_row['trading_session'], 'regular')
402377
else:
403-
result = self.client.get_timeline(symbols=['AAPL'])
378+
result = self.client.get_timeline(symbols=['AAPL'],
379+
# trade_session=TradingSession.OverNight
380+
)
404381
logger.debug(f"Timeline (real):\n {result}")
405382

406383
def test_get_timeline_history(self):
407-
if not self.is_mock:
384+
if self.is_mock:
408385
mock_data = {
409-
"code":
410-
0,
411-
"message":
412-
"success",
413-
"timestamp":
414-
1754990360538,
415-
"data": [{
416-
"symbol":
417-
"AAPL",
418-
"items": [{
419-
"time": 1698845400000,
420-
"volume": 654691,
421-
"price": 171.1,
422-
"avgPrice": 171.0484
423-
}, {
424-
"time": 1698845460000,
425-
"volume": 175598,
426-
"price": 170.595,
427-
"avgPrice": 171.01788
428-
}, {
429-
"time": 1698845520000,
430-
"volume": 186093,
431-
"price": 170.535,
432-
"avgPrice": 170.9575
433-
}, {
434-
"time": 1698845580000,
435-
"volume": 145550,
436-
"price": 170.2719,
437-
"avgPrice": 170.90828
438-
}, {
439-
"time": 1698845640000,
440-
"volume": 221063,
441-
"price": 170.41,
442-
"avgPrice": 170.82759
443-
}]
444-
}]
386+
"code": 0, "message": "success", "timestamp": 1754990360538, "data": [{"symbol": "AAPL", "items": [
387+
{"time": 1698845400000, "volume": 654691, "price": 171.1, "avgPrice": 171.0484},
388+
{"time": 1698845460000, "volume": 175598, "price": 170.595, "avgPrice": 171.01788},
389+
{"time": 1698845520000, "volume": 186093, "price": 170.535, "avgPrice": 170.9575},
390+
{"time": 1698845580000, "volume": 145550, "price": 170.2719, "avgPrice": 170.90828},
391+
{"time": 1698845640000, "volume": 221063, "price": 170.41, "avgPrice": 170.82759}]}]
445392
}
446393

447394
web_utils.do_request = MagicMock(
@@ -465,64 +412,15 @@ def test_get_timeline_history(self):
465412
self.assertEqual(first_row['volume'], 654691)
466413
else:
467414
result = self.client.get_timeline_history(symbols=['AAPL'],
468-
date="2023-11-01")
415+
date="2025-08-21",
416+
# trade_session=TradingSession.OverNight
417+
)
469418
logger.debug(f"Timeline History (real):\n {result}")
470419

471420
def test_get_bars(self):
472421
if self.is_mock:
473422
mock_data = {
474-
"code":
475-
0,
476-
"message":
477-
"success",
478-
"timestamp":
479-
1754990841014,
480-
"data": [{
481-
"symbol":
482-
"AAPL",
483-
"period":
484-
"day",
485-
"items": [{
486-
"time": 1754366400000,
487-
"volume": 44155079,
488-
"open": 203.4,
489-
"close": 202.92,
490-
"high": 205.34,
491-
"low": 202.16,
492-
"amount": 8.987659222543882E9
493-
}, {
494-
"time": 1754452800000,
495-
"volume": 108483103,
496-
"open": 205.63,
497-
"close": 213.25,
498-
"high": 215.38,
499-
"low": 205.59,
500-
"amount": 2.315468887474085E10
501-
}, {
502-
"time": 1754539200000,
503-
"volume": 90224834,
504-
"open": 218.875,
505-
"close": 220.03,
506-
"high": 220.85,
507-
"low": 216.58,
508-
"amount": 1.9798494559887737E10
509-
}, {
510-
"time": 1754625600000,
511-
"volume": 113853967,
512-
"open": 220.83,
513-
"close": 229.35,
514-
"high": 231.0,
515-
"low": 219.25,
516-
"amount": 2.589128470726625E10
517-
}, {
518-
"time": 1754884800000,
519-
"volume": 61806132,
520-
"open": 227.92,
521-
"close": 227.18,
522-
"high": 229.56,
523-
"low": 224.76,
524-
"amount": 1.4164248430829714E10
525-
}]
423+
"code": 0, "message": "success", "timestamp": 1754990841014, "data": [{"symbol": "AAPL", "period": "day", "items": [{"time": 1754366400000, "volume": 44155079, "open": 203.4, "close": 202.92, "high": 205.34, "low": 202.16, "amount": 8.987659222543882E9}, {"time": 1754452800000, "volume": 108483103, "open": 205.63, "close": 213.25, "high": 215.38, "low": 205.59, "amount": 2.315468887474085E10}, {"time": 1754539200000, "volume": 90224834, "open": 218.875, "close": 220.03, "high": 220.85, "low": 216.58, "amount": 1.9798494559887737E10}, {"time": 1754625600000, "volume": 113853967, "open": 220.83, "close": 229.35, "high": 231.0, "low": 219.25, "amount": 2.589128470726625E10}, {"time": 1754884800000, "volume": 61806132, "open": 227.92, "close": 227.18, "high": 229.56, "low": 224.76, "amount": 1.4164248430829714E10}]
526424
}]
527425
}
528426
web_utils.do_request = MagicMock(
@@ -575,7 +473,9 @@ def test_get_bars(self):
575473
result = self.client.get_bars(symbols=['AAPL', 'MSFT'],
576474
period=BarPeriod.DAY,
577475
limit=10,
578-
page_token='')
476+
page_token='',
477+
# trade_session=TradingSession.OverNight
478+
)
579479
logger.debug(f"Bars (real):\n {result}")
580480

581481
def test_get_trade_ticks(self):
@@ -588,33 +488,8 @@ def test_get_trade_ticks(self):
588488
"timestamp":
589489
1754991710630,
590490
"data": [{
591-
"symbol":
592-
"AAPL",
593-
"beginIndex":
594-
482299,
595-
"endIndex":
596-
482499,
597-
"items": [{
598-
"time": 1754942403109,
599-
"volume": 406,
600-
"price": 227.18,
601-
"type": "-"
602-
}, {
603-
"time": 1754942403109,
604-
"volume": 26215,
605-
"price": 227.18,
606-
"type": "-"
607-
}, {
608-
"time": 1754942403109,
609-
"volume": 884,
610-
"price": 227.18,
611-
"type": "-"
612-
}, {
613-
"time": 1754942403109,
614-
"volume": 200,
615-
"price": 227.18,
616-
"type": "-"
617-
}]
491+
"symbol": "AAPL", "beginIndex": 482299, "endIndex": 482499,
492+
"items": [{"time": 1754942403109, "volume": 406, "price": 227.18, "type": "-"}, {"time": 1754942403109, "volume": 26215, "price": 227.18, "type": "-"}, {"time": 1754942403109, "volume": 884, "price": 227.18, "type": "-"}, {"time": 1754942403109, "volume": 200, "price": 227.18, "type": "-"}]
618493
}]
619494
}
620495
web_utils.do_request = MagicMock(
@@ -654,7 +529,9 @@ def test_get_trade_ticks(self):
654529
self.assertEqual(second_row['direction'], '-')
655530
self.assertEqual(second_row['index'], 482300)
656531
else:
657-
result = self.client.get_trade_ticks(symbols=['AAPL'])
532+
result = self.client.get_trade_ticks(symbols=['AAPL'],
533+
# trade_session=TradingSession.OverNight
534+
)
658535
logger.debug(f"Trade Ticks (real): \n {result}")
659536

660537
def test_get_short_interest(self):
@@ -764,7 +641,9 @@ def test_get_depth_quote(self):
764641
else:
765642
# 实际调用API
766643
result = self.client.get_depth_quote(symbols=['AAPL', 'MSFT'],
767-
market=Market.US)
644+
market=Market.US,
645+
# trade_session=TradingSession.OverNight
646+
)
768647
logger.debug(f"Depth Quote (real):\n {result}")
769648

770649
def test_get_option_expirations(self):
@@ -1013,8 +892,12 @@ def test_get_option_bars(self):
1013892
self.assertEqual(second_bar['open_interest'], 9)
1014893

1015894
else:
895+
# result = self.client.get_option_bars(
896+
# identifiers=['AAPL 250815C00200000'], period=BarPeriod.DAY, limit=5)
1016897
result = self.client.get_option_bars(
1017-
identifiers=['AAPL 250815C00200000'], period=BarPeriod.DAY, limit=5)
898+
identifiers=['TCH.HK250828C00590000'], period=BarPeriod.DAY, limit=5,
899+
end_time='2025-08-22', timezone='Asia/Hong_Kong'
900+
)
1018901
logger.debug(f"Option Bars (real):\n {result}")
1019902

1020903
def test_get_option_trade_ticks(self):
@@ -1062,21 +945,15 @@ def test_get_option_trade_ticks(self):
1062945
self.assertEqual(first_tick['price'], 30.0)
1063946
self.assertEqual(first_tick['volume'], 2)
1064947

1065-
# 验证第三条逐笔成交数据
1066-
third_tick = mock_result.iloc[2]
1067-
self.assertEqual(third_tick['time'], 1755005515034)
1068-
self.assertEqual(third_tick['price'], 30.2)
1069-
self.assertEqual(third_tick['volume'], 3)
1070-
1071948
# 验证最后一条逐笔成交数据
1072-
last_tick = mock_result.iloc[5]
949+
last_tick = mock_result.iloc[-1]
1073950
self.assertEqual(last_tick['time'], 1755005863136)
1074951
self.assertEqual(last_tick['price'], 28.6)
1075952
self.assertEqual(last_tick['volume'], 1)
1076953

1077954
else:
1078955
result = self.client.get_option_trade_ticks(
1079-
identifiers=['AAPL 250815C00200000'])
956+
identifiers=['AAPL250829P00200000'])
1080957
logger.debug(f"Option Trade Ticks (real):\n {result}")
1081958

1082959
def test_get_option_symbols(self):
@@ -1827,6 +1704,63 @@ def test_get_future_brief(self):
18271704
result = self.client.get_future_brief(identifiers=['ES2509'])
18281705
logger.debug(f"Future Brief: \n {result}")
18291706

1707+
def test_get_future_depth(self):
1708+
if self.is_mock:
1709+
mock_data = {"code": 0, "message": "success", "timestamp": 1755847046112, "data": [
1710+
{"contractId": "d2cf66f761354eda8ddbecc242f6d245", "contractCode": "ES2509",
1711+
"ask": [{"price": 6385.00, "volume": 1}, {"price": 6385.25, "volume": 23},
1712+
{"price": 6385.50, "volume": 30}, {"price": 6385.75, "volume": 26}],
1713+
"bid": [{"price": 6384.75, "volume": 22}, {"price": 6384.50, "volume": 28},
1714+
{"price": 6384.25, "volume": 36}, {"price": 6384.00, "volume": 33}]},
1715+
{"contractId": "2971d762658f43c7bb26d353cc314b4d", "contractCode": "ES2512",
1716+
"ask": [{"price": 6440.75, "volume": 2}, {"price": 6441.00, "volume": 3}],
1717+
"bid": [{"price": 6439.75, "volume": 1}, {"price": 6439.50, "volume": 2}]}]}
1718+
web_utils.do_request = MagicMock(
1719+
return_value=json.dumps(mock_data).encode())
1720+
1721+
# {'identifier': 'ES2509', 'asks': [(6385.0, 1), (6385.25, 23), (6385.5, 30), (6385.75, 26), (6386.0, 43), (6386.25, 37), (6386.5, 31), (6386.75, 39), (6387.0, 39), (6387.25, 30)], 'bids': [(6384.75, 22), (6384.5, 28), (6384.25, 36), (6384.0, 33), (6383.75, 44), (6383.5, 32), (6383.25, 35), (6383.0, 38), (6382.75, 33), (6382.5, 42)]}
1722+
result_single = self.client.get_future_depth(identifiers=['ES2509'])
1723+
1724+
logger.debug(f"Future Depth (single): \n {result_single}")
1725+
# Verify the result
1726+
self.assertIsNotNone(result_single)
1727+
self.assertIsInstance(result_single, dict)
1728+
self.assertIn('ES2509', result_single)
1729+
self.assertIn('asks', result_single['ES2509'])
1730+
self.assertIn('bids', result_single['ES2509'])
1731+
self.assertEqual(len(result_single['ES2509']['asks']), 4)
1732+
self.assertEqual(len(result_single['ES2509']['bids']), 4)
1733+
self.assertEqual(result_single['ES2509']['identifier'], 'ES2509')
1734+
self.assertEqual(result_single['ES2509']['asks'][0], (6385.00, 1))
1735+
self.assertEqual(result_single['ES2509']['bids'][0], (6384.75, 22))
1736+
1737+
# {'ES2509': {'identifier': 'ES2509', 'asks': [(6385.0, 1), (6385.25, 23), (6385.5, 30), (6385.75, 26), (6386.0, 43), (6386.25, 37), (6386.5, 31), (6386.75, 39), (6387.0, 39), (6387.25, 30)], 'bids': [(6384.75, 22), (6384.5, 28), (6384.25, 36), (6384.0, 33), (6383.75, 44), (6383.5, 32), (6383.25, 35), (6383.0, 38), (6382.75, 33), (6382.5, 42)]}, 'ES2512': {'identifier': 'ES2512', 'asks': [(6440.75, 2), (6441.0, 3), (6441.25, 2), (6441.5, 2), (6441.75, 2), (6442.0, 4), (6442.25, 2), (6442.5, 1), (6442.75, 3), (6443.0, 4)], 'bids': [(6439.75, 1), (6439.5, 2), (6439.25, 1), (6439.0, 4), (6438.75, 2), (6438.5, 2), (6438.25, 2), (6438.0, 4), (6437.75, 2), (6437.5, 2)]}}
1738+
result_multi = self.client.get_future_depth(identifiers=['ES2509', 'ES2512'])
1739+
1740+
# Verify the result
1741+
self.assertIsNotNone(result_multi)
1742+
self.assertIsInstance(result_multi, dict)
1743+
self.assertIn('ES2509', result_multi)
1744+
self.assertIn('ES2512', result_multi)
1745+
self.assertIn('asks', result_multi['ES2509'])
1746+
self.assertIn('bids', result_multi['ES2509'])
1747+
self.assertIn('asks', result_multi['ES2512'])
1748+
self.assertIn('bids', result_multi['ES2512'])
1749+
self.assertEqual(len(result_multi['ES2509']['asks']), 4)
1750+
self.assertEqual(len(result_multi['ES2509']['bids']), 4)
1751+
self.assertEqual(len(result_multi['ES2512']['asks']), 2)
1752+
self.assertEqual(len(result_multi['ES2512']['bids']), 2)
1753+
self.assertEqual(result_multi['ES2509']['identifier'], 'ES2509')
1754+
self.assertEqual(result_multi['ES2512']['identifier'], 'ES2512')
1755+
self.assertEqual(result_multi['ES2509']['asks'][0], (6385.00, 1))
1756+
self.assertEqual(result_multi['ES2509']['bids'][0], (6384.75, 22))
1757+
self.assertEqual(result_multi['ES2512']['asks'][0], (6440.75, 2))
1758+
self.assertEqual(result_multi['ES2512']['bids'][0], (6439.75, 1))
1759+
1760+
else:
1761+
result = self.client.get_future_depth(identifiers=['ES2509', 'ES2512'])
1762+
logger.debug(f"Future Depth: \n {result}")
1763+
18301764
def test_get_trading_calendar(self):
18311765
if self.is_mock:
18321766
mock_data = {"code": 0, "message": "success", "timestamp": 1755068224574,
@@ -1983,7 +1917,6 @@ def test_get_broker_hold(self):
19831917
result = self.client.get_broker_hold()
19841918
logger.debug(f"Broker Hold:\n {result}")
19851919

1986-
19871920
def test_market_scanner(self):
19881921
"""测试市场扫描器功能"""
19891922
if self.is_mock:

tigeropen/common/consts/service_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
FUTURE_REAL_TIME_QUOTE = "future_real_time_quote"
101101
FUTURE_TICK = "future_tick"
102102
FUTURE_TRADING_DATE = "future_trading_date"
103+
FUTURE_DEPTH = "future_depth"
103104

104105
# 基金行情
105106
FUND_ALL_SYMBOLS = "fund_all_symbols"

0 commit comments

Comments
 (0)