"""23只合成股票,12减1窗口、20名与两次月度换仓。""" import json from math import isclose prices = {f'S{i:02}': [10] + [10 + i * m / 1000 for m in range(1, 14)] for i in range(1, 24)} prices['S23'][12] = None def select(month): scores = {} for symbol, xs in prices.items(): start, end = xs[month - 12], xs[month - 1] if start is not None and end is not None and start > 0: scores[symbol] = end / start - 1 return sorted(scores, key=lambda s: (-scores[s], s))[:20], scores first, score1 = select(12) second, score2 = select(13) assert first == [f'S{i:02}' for i in range(23, 3, -1)] assert second == [f'S{i:02}' for i in range(22, 2, -1)] assert isclose(score1['S23'], .0253) original = first[:] prices['S01'][12] = 99999 assert select(12)[0] == original # 最近月价格不参与本月形成期 cash, holdings, fees = 10000, set(), 0 for target in [set(first), set(second)]: for symbol in sorted(holdings - target): cash += 40 * 10 - 1 fees += 1 for symbol in sorted(target - holdings): cash -= 40 * 10 + 1 fees += 1 holdings = target equity = cash + len(holdings) * 40 * 10 assert fees == 22 and isclose(cash, 1978) and isclose(equity, 9978) print(json.dumps({'first': first, 'second': second, 'first_S23_score': score1['S23'], 'exit': 'S23', 'entry': 'S03', 'fees': fees, 'cash': cash, 'marked_equity': equity, 'latest_month_mutation_ignored': True}))