optz
This commit is contained in:
113
py-client/tests/test_python314_performance.py
Normal file
113
py-client/tests/test_python314_performance.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""Offline regression checks for the Python 3.14 performance changes."""
|
||||
|
||||
import importlib
|
||||
import inspect
|
||||
import unittest
|
||||
from annotationlib import Format, get_annotations
|
||||
from concurrent.futures import Future
|
||||
from datetime import datetime, time, timedelta
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from libs.calc import trading_time
|
||||
from libs.signal import SignalItem
|
||||
from sdk.models import OrderItem, _parse_datetime
|
||||
from strategy.trend import boot
|
||||
from strategy.trend.open import _parse_minutes, check_timezone
|
||||
|
||||
|
||||
class PerformanceRegressionTests(unittest.TestCase):
|
||||
def test_trading_time_matches_original_for_week_and_boundaries(self):
|
||||
start = datetime(2026, 9, 7)
|
||||
for minute in range(7 * 24 * 60):
|
||||
now = start + timedelta(minutes=minute)
|
||||
expected = now.weekday() < 5 and (
|
||||
time(9, 30) <= now.time() <= time(11, 30)
|
||||
or time(13) <= now.time() <= time(15)
|
||||
)
|
||||
self.assertEqual(trading_time(now), expected, now)
|
||||
for clock in ((11, 30), (15, 0)):
|
||||
self.assertFalse(trading_time(start.replace(hour=clock[0], minute=clock[1], microsecond=1)))
|
||||
|
||||
def test_date_parser_preserves_strptime_acceptance(self):
|
||||
for date in ('20260907', '2026-09-07', '', '20260229', '20240229', '202691'):
|
||||
for clock in ('100000', '10:00:00', '93000', '', '240000', 'bad', '1'):
|
||||
try:
|
||||
expected = datetime.strptime(date.replace('-', '') + clock.replace(':', '').zfill(6), '%Y%m%d%H%M%S')
|
||||
except ValueError:
|
||||
expected = None
|
||||
self.assertEqual(_parse_datetime(date, clock), expected, (date, clock))
|
||||
|
||||
def test_date_cache_tracks_mutable_order_fields(self):
|
||||
order = OrderItem(insert_date='20260907', insert_time='100000', remark='first|trend')
|
||||
self.assertEqual(order.created_at, datetime(2026, 9, 7, 10))
|
||||
order.insert_time = '110000'
|
||||
order.remark = 'second|trend'
|
||||
self.assertEqual(order.created_at, datetime(2026, 9, 7, 11))
|
||||
self.assertEqual(order.local_order_id, 'second')
|
||||
self.assertEqual(order.get_local_order_id, 'second')
|
||||
|
||||
def test_caches_are_bounded(self):
|
||||
_parse_minutes.cache_clear()
|
||||
_parse_datetime.cache_clear()
|
||||
for i in range(4200):
|
||||
_parse_datetime('invalid', str(i))
|
||||
_parse_minutes(str(i))
|
||||
self.assertLessEqual(_parse_datetime.cache_info().currsize, 4096)
|
||||
self.assertLessEqual(_parse_minutes.cache_info().currsize, 256)
|
||||
|
||||
def test_timezone_boundaries_and_current_time_not_cached(self):
|
||||
for hour in range(24):
|
||||
for minute in range(60):
|
||||
now = datetime(2026, 9, 7, hour, minute)
|
||||
m = hour * 60 + minute
|
||||
self.assertEqual(check_timezone('9:30-10:30,invalid,23:00-1:00', now),
|
||||
570 <= m <= 630 or m >= 1380 or m <= 60)
|
||||
self.assertTrue(check_timezone('*'))
|
||||
self.assertFalse(check_timezone('24:00-25:00'))
|
||||
self.assertFalse(check_timezone(''))
|
||||
|
||||
def test_signal_order_duplicates_and_request_order_preserved(self):
|
||||
future = Future()
|
||||
future.set_result(None)
|
||||
assets = SimpleNamespace(available=100, total=100)
|
||||
portfolio = SimpleNamespace(assets=assets, positions={'held': object()}, orders=[])
|
||||
run = SimpleNamespace(client=Mock(), orders=Mock(), executor=Mock(),
|
||||
account_cfg=SimpleNamespace(account_id='test', min_cash_ratio=0.1))
|
||||
run.client.portfolio.return_value = portfolio
|
||||
run.client.full_tick.return_value = {}
|
||||
run.executor.submit.return_value = future
|
||||
signals = [SignalItem(code=c) for c in ('new-b', 'held', 'new-a', 'new-b')]
|
||||
with patch.object(boot, 'trading_time', return_value=True), \
|
||||
patch.object(boot, 'market_allow_open', return_value=True), \
|
||||
patch.object(boot, '_cache_portfolio'), patch('builtins.print'):
|
||||
boot.RunOnce(run, signals)
|
||||
run.client.full_tick.assert_called_once_with(['held', 'new-b', 'new-a'])
|
||||
self.assertEqual(run.executor.submit.call_args_list[1].args,
|
||||
(boot.open_signal, run, {}, [signals[0], signals[2], signals[3]]))
|
||||
|
||||
def test_native_annotations_resolve_for_all_application_modules(self):
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
paths = [p for name in ('config', 'sdk', 'libs', 'strategy')
|
||||
for p in (root / name).rglob('*.py')]
|
||||
for path in paths:
|
||||
parts = list(path.relative_to(root).with_suffix('').parts)
|
||||
if parts[-1] == '__init__':
|
||||
parts.pop()
|
||||
module = importlib.import_module('.'.join(parts))
|
||||
for obj in vars(module).values():
|
||||
if (inspect.isclass(obj) or inspect.isfunction(obj)) and obj.__module__ == module.__name__:
|
||||
get_annotations(obj, format=Format.VALUE)
|
||||
if inspect.isclass(obj):
|
||||
for member in vars(obj).values():
|
||||
if isinstance(member, (classmethod, staticmethod)):
|
||||
member = member.__func__
|
||||
elif isinstance(member, property):
|
||||
member = member.fget
|
||||
if inspect.isfunction(member):
|
||||
get_annotations(member, format=Format.VALUE)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user