"""R644:每个虚拟信号保留一个终态,未确认成交不改变账户。""" from decimal import Decimal as D from copy import deepcopy from collections import Counter def process(state,event): out=deepcopy(state) if not isinstance(event['id'],str) or not event['id'].strip() or event['id'] in out['seen']:raise ValueError('invalid or duplicate event') if event['side'] not in {None,'buy','sell'}:raise ValueError('unknown direction') if event['side'] is None:status='no_signal' elif event['security'] not in {'A','B'}:status='outside_basket' elif event['price'] is None:status='quote_unknown' else: price=D(event['price']);fee=D(1);qty=event['qty'] if not price.is_finite() or price<=0 or type(qty) is not int or qty<=0:raise ValueError('invalid executable amount') held=out['shares'].get(event['security'],0);before=out['cash']+held*price if event['side']=='buy' and held:status='already_positioned' elif event['side']=='sell' and held=0 and all(q>=0 for q in out['shares'].values()) return out,status inputs=[(None,'A','100',1,'filled'),('buy','C','100',1,'filled'),('buy','A',None,1,'filled'),('buy','A','100',5,'filled'),('buy','A','100',1,'pending'),('buy','A','100',1,'filled'),('buy','A','100',1,'filled'),('sell','A','105',1,'blocked'),('sell','A','105',1,'filled')] state={'cash':D(250),'shares':{},'seen':set()};rows=[] for i,(side,s,price,qty,confirmation) in enumerate(inputs,1): before=deepcopy(state);event={'id':f'E{i}','side':side,'security':s,'price':price,'qty':qty,'confirmation':confirmation} state,status=process(state,event) if status!='filled':assert state['cash']==before['cash'] and state['shares']==before['shares'] rows.append((event['id'],status,str(state['cash']),dict(state['shares']))) assert [r[1] for r in rows]==['no_signal','outside_basket','quote_unknown','insufficient_cash','fill_unconfirmed','filled','already_positioned','market_rejected','filled'] assert state['cash']==D(253) and state['shares']['A']==0 and len(state['seen'])==9 assert sum(Counter(row[1] for row in rows).values())==9 for bad in [dict(event),dict(event,id='E10',side='buy',price='NaN')]: try:process(state,bad) except ValueError:pass else:raise AssertionError('duplicate or nonfinite event accepted') print('rows='+str(rows)) print('signal_events=9 filled=2 ending_cash=253 ending_A=0 each_event_has_one_outcome=True')