"""R641:事件驱动虚拟现金/股数账,未成交不入账。""" from decimal import Decimal as D from copy import deepcopy def apply(state,event): out=deepcopy(state) if not isinstance(event['id'],str) or not event['id'].strip():raise ValueError('invalid execution identity') if event['id'] in out['seen']:raise ValueError('duplicate execution identity') if event['side'] not in {'buy','sell'} or type(event['qty']) is not int or event['qty']<=0:raise ValueError('invalid order') price=D(event['price']);fee=D(event['fee']) if not price.is_finite() or price<=0 or not fee.is_finite() or fee<0:raise ValueError('invalid money') if event['status'] not in {'filled','unfilled'}:raise ValueError('unknown confirmation') before=out['cash']+out['shares']*price status='unfilled' if event['status']=='filled': qty=event['qty'];signed=qty if event['side']=='buy' else -qty cash=out['cash']-signed*price-fee;shares=out['shares']+signed status='insufficient_cash' if cash<0 else 'insufficient_shares' if shares<0 else 'filled' if status=='filled':out['cash']=cash;out['shares']=shares out['seen'].add(event['id']) nav=out['cash']+out['shares']*price assert nav==before-(fee if status=='filled' else 0) assert out['cash']>=0 and out['shares']>=0 return out,{'id':event['id'],'status':status,'cash':out['cash'],'shares':out['shares'],'NAV':nav} events=[('E1','buy',6,'100','1','filled'),('E2','buy',1,'105','1','unfilled'),('E3','buy',5,'90','1','filled'),('E4','sell',2,'110','1','filled')] state={'cash':D(1000),'shares':0,'seen':set()};rows=[] for values in events: event=dict(zip(['id','side','qty','price','fee','status'],values));state,row=apply(state,event);rows.append(row) assert [(r['status'],r['cash'],r['shares']) for r in rows]==[('filled',D(399),6),('unfilled',D(399),6),('insufficient_cash',D(399),6),('filled',D(618),4)] assert state['cash']+state['shares']*D(105)==D(1038) saved=deepcopy(state) state,rejected=apply(state,{'id':'E5','side':'sell','qty':5,'price':'105','fee':'1','status':'filled'}) assert rejected['status']=='insufficient_shares' and state['cash']==saved['cash'] and state['shares']==saved['shares'] try:apply(state,{'id':'E1','side':'buy','qty':1,'price':'100','fee':'1','status':'filled'}) except ValueError:pass else:raise AssertionError('duplicate applied twice') try:apply(state,{'id':'E6','side':'buy','qty':1,'price':'NaN','fee':'1','status':'filled'}) except ValueError:pass else:raise AssertionError('NaN quote used') for ident in ['',None,' ']: try:apply(state,{'id':ident,'side':'buy','qty':1,'price':'100','fee':'1','status':'filled'}) except ValueError:pass else:raise AssertionError('unknown execution identity') print('rows='+str(rows)) print('ending_cash=618 ending_shares=4 terminal_mark=105 ending_NAV=1038 unfilled_changes_position=False') print('oversell=rejected duplicate_execution=rejected nonfinite_money=rejected account_is_virtual=True')