"""分数缺失、交易日年龄与到期后持仓退出,不以缺失代替零。""" import json from math import isfinite observations = [{'A': 9, 'B': 8}, {'A': None, 'B': 8}, {'A': None, 'B': 8}, {'A': 11, 'B': 8}] opens = [{'A': 10, 'B': 10}, {'A': 10.2, 'B': 10}, {'A': 11, 'B': 10}, {'A': 12, 'B': 10.5}] def run(max_age): known, cash, holding, ledger = {}, 1000, None, [] for session, current in enumerate(observations): for symbol, score in current.items(): if score is not None and isfinite(score): known[symbol] = (score, session) statuses = {} for symbol in current: if symbol not in known: statuses[symbol] = 'never_observed' else: age = session - known[symbol][1] statuses[symbol] = ('fresh' if age == 0 else 'reusable_old' if age <= max_age else 'expired') eligible = {s: score for s, (score, at) in known.items() if session - at <= max_age} target = sorted(eligible, key=lambda s: (-eligible[s], s))[0] if eligible else None orders = [] if target != holding: if holding: cash += 40 * opens[session][holding] - 1 orders.append([holding, 'sell']) if target: cash -= 40 * opens[session][target] + 1 orders.append([target, 'buy']) holding = target a_age = session - known['A'][1] if 'A' in known else None ledger.append({'session': session, 'A_age': a_age, 'score_status': statuses, 'eligible': eligible, 'target': target, 'orders': orders, 'cash': round(cash, 6), 'equity': round(cash + (40 * opens[session][holding] if holding else 0), 6)}) return ledger bounded, skip, forever = run(1), run(0), run(99) assert [r['target'] for r in bounded] == ['A', 'A', 'B', 'A'] assert [r['target'] for r in skip] == ['A', 'B', 'B', 'A'] assert [r['target'] for r in forever] == ['A', 'A', 'A', 'A'] assert 'A' not in bounded[2]['eligible'] assert bounded[2]['orders'] == [['A', 'sell'], ['B', 'buy']] assert bounded[-1]['cash'] == 575 and bounded[-1]['equity'] == 1055 assert skip[-1]['equity'] == 1023 and forever[-1]['equity'] == 1079 saved_observations = observations boundary_results, boundary_failures = {}, [] boundary_cases = { 'nan_gap': [dict(row, A=float('nan')) if i in (1, 2) else dict(row) for i, row in enumerate(saved_observations)], 'positive_infinity_gap': [dict(row, A=float('inf')) if i in (1, 2) else dict(row) for i, row in enumerate(saved_observations)], 'negative_infinity_gap': [dict(row, A=float('-inf')) if i in (1, 2) else dict(row) for i, row in enumerate(saved_observations)], 'never_observed_A': [dict(row, A=None) if i == 0 else dict(row) for i, row in enumerate(saved_observations)], 'all_missing_at_start': [dict(A=None, B=None) if i == 0 else dict(row) for i, row in enumerate(saved_observations)], } try: for label, fixture in boundary_cases.items(): observations = fixture try: result = run(1) assert all(isfinite(score) for row in result for score in row['eligible'].values()) if label.endswith('_gap'): assert [row['target'] for row in result] == ['A', 'A', 'B', 'A'] assert [row['A_age'] for row in result] == [0, 1, 2, 0] assert result[1]['score_status']['A'] == 'reusable_old' assert result[2]['score_status']['A'] == 'expired' assert result[-1]['cash'] == 575 and result[-1]['equity'] == 1055 elif label == 'never_observed_A': assert result[0]['target'] == 'B' and result[0]['A_age'] is None assert result[0]['score_status']['A'] == 'never_observed' assert result[0]['eligible'] == {'B': 8} else: assert result[0]['target'] is None and result[0]['A_age'] is None assert result[0]['eligible'] == {} and result[0]['orders'] == [] assert result[0]['cash'] == result[0]['equity'] == 1000 assert set(result[0]['score_status'].values()) == {'never_observed'} boundary_results[label] = result except (AssertionError, KeyError) as error: boundary_failures.append([label, type(error).__name__, str(error)]) finally: observations = saved_observations assert not boundary_failures, boundary_failures print(json.dumps({'bounded_age_one': bounded, 'skip_final': skip[-1], 'unbounded_final': forever[-1], 'boundary_cases': boundary_results, 'warning': 'unbounded is counterexample'}))