"""五交易期输家十股、流动性过滤、五期开盘批次与退出优先现金账。""" from copy import deepcopy from decimal import Decimal as D import json from math import isfinite def make_data(): return {f'S{i:02}': {'close': [10 - i*j/100 for j in range(12)], 'volume': [500 if i == 12 else 2000] * 12, 'open': [10.0] * 11 + [10.2]} for i in range(1, 13)} def select(data, index): scores = {} if index < 5: return [] for symbol, bars in data.items(): closes = bars['close'][index-5:index+1] volumes = bars['volume'][index-4:index+1] if (len(closes) != 6 or any(x is None or not isfinite(x) or x <= 0 for x in closes) or len(volumes) != 5 or any(v is None or not isfinite(v) or v <= 0 for v in volumes) or sum(volumes)/5 < 1000): continue scores[symbol] = closes[-1]/closes[0] - 1 return sorted(scores, key=lambda s: (scores[s], s))[:10] def run(data): cash, holdings, fees, ledger = D(10000), {}, D(0), [] def price_at(symbol, session): raw = data[symbol]['open'][session] if raw is None or not isfinite(raw) or raw <= 0: raise ValueError('invalid_open') return D(str(raw)) for session in range(len(next(iter(data.values()))['open'])): orders, skipped, exited = [], [], set() for symbol, lot in sorted(list(holdings.items())): if session >= lot['due']: price = price_at(symbol, session) cash += lot['quantity'] * price - 1 fees += 1 orders.append([symbol, 'sell', lot['quantity'], str(price), 1, 'fixed_due']) exited.add(symbol) del holdings[symbol] if session - 1 in (5, 10): for symbol in sorted(select(data, session-1)): if symbol in exited or symbol in holdings: skipped.append([symbol, 'exit_priority' if symbol in exited else 'existing_batch']) continue price = price_at(symbol, session) cost = 40 * price + 1 if cost > cash: skipped.append([symbol, 'cash']) continue cash -= cost fees += 1 holdings[symbol] = {'quantity': 40, 'entry': session, 'due': session + 5} orders.append([symbol, 'buy', 40, str(price), 1, 'prior_close_selection']) equity = cash + sum(lot['quantity'] * price_at(s, session) for s, lot in holdings.items()) ledger.append({'session': session, 'orders': orders, 'skipped': skipped, 'holdings': deepcopy(holdings), 'cash': str(cash), 'fees': str(fees), 'equity': str(equity)}) return ledger data = make_data() assert select(data, 5) == [f'S{i:02}' for i in range(11, 1, -1)] assert 'S12' not in select(data, 5) rows = run(data) expected_buys = [[f'S{i:02}', 'buy', 40, '10.0', 1, 'prior_close_selection'] for i in range(2, 12)] expected_sells = [[f'S{i:02}', 'sell', 40, '10.2', 1, 'fixed_due'] for i in range(2, 12)] assert rows[6]['orders'] == expected_buys and rows[6]['cash'] == '5990.0' assert {lot['due'] for lot in rows[6]['holdings'].values()} == {11} assert rows[11]['orders'] == expected_sells assert rows[11]['cash'] == rows[11]['equity'] == '10060.0' assert rows[11]['fees'] == '20' and rows[11]['holdings'] == {} assert len(rows[11]['skipped']) == 10 and all(x[1] == 'exit_priority' for x in rows[11]['skipped']) assert rows[11]['skipped'] == [[f'S{i:02}', 'exit_priority'] for i in range(2, 12)] prefix = {s: {field: values[:7] for field, values in bars.items()} for s, bars in data.items()} assert run(prefix) == rows[:7] changed = deepcopy(data) for bars in changed.values(): bars['close'][6:] = [10000] * 6 assert select(changed, 5) == select(data, 5) invalid = deepcopy(data) invalid['S02']['open'][6] = float('nan') try: run(invalid) except ValueError as error: assert str(error) == 'invalid_open' else: raise AssertionError('非有限执行价格必须拒绝') print(json.dumps({'selected': select(data, 5), 'entry': rows[6], 'exit': rows[11], 'prefix_equal': True, 'future_close_mutation_first_selection_equal': True, 'scope': 'synthetic open fills; no historical return claim'}))