"""R640:规则版本独立封存、账户独立,不因新成交重写旧分支。""" from decimal import Decimal as D from pathlib import Path from tempfile import TemporaryDirectory import json,hashlib def digest(value):return hashlib.sha256(json.dumps(value,sort_keys=True,separators=(',',':'),allow_nan=False).encode()).hexdigest() def run(rule,initial,events): if set(rule)!={'version','entry','exit','cutoff'} or not isinstance(rule['version'],str) or not rule['version'].strip():raise ValueError('invalid frozen rule') if type(rule['cutoff']) is not int or any(not D(str(rule[k])).is_finite() or D(str(rule[k]))<=0 for k in ['entry','exit']):raise ValueError('invalid rule values') cash=D(initial['cash']);shares=initial['shares'];rows=[] if not cash.is_finite() or cash<0 or type(shares) is not int or shares<0:raise ValueError('invalid initial state') for t,observed,price in events: observed=D(str(observed));price=D(str(price)) if t<=rule['cutoff'] or not observed.is_finite() or not price.is_finite() or observed<=0 or price<=0:raise ValueError('invalid future observation') before=cash+shares*price;side='hold' if shares and observedD(rule['entry']) and cash>=price:cash-=price;shares=1;side='buy' assert cash>=0 and cash+shares*price==before rows.append((t,side,str(cash),shares)) return {'cash':cash,'shares':shares,'rows':rows,'ending':cash+shares*D(112)} old={'version':'v1','entry':'100','exit':'100','cutoff':1} new={'version':'v2','entry':'105','exit':'102','cutoff':3} events=[(4,101,102),(5,107,108),(6,99,100),(7,110,111)] with TemporaryDirectory() as tmp: p=Path(tmp)/'v1.json';p.write_text(json.dumps(old,sort_keys=True),encoding='utf-8');old_bytes=p.read_bytes() with (Path(tmp)/'v2.json').open('x',encoding='utf-8') as f:json.dump(new,f,sort_keys=True) a=run(json.loads(p.read_text(encoding='utf-8')),{'cash':'900','shares':1},events) b=run(new,{'cash':'1000','shares':0},events) common=run(old,{'cash':'1000','shares':0},events) assert p.read_bytes()==old_bytes and digest(old)!=digest(new) try: with (Path(tmp)/'v2.json').open('x',encoding='utf-8'):pass except FileExistsError:pass else:raise AssertionError('frozen version overwritten') assert (a['ending'],b['ending'],common['ending'])==(D(1001),D(993),D(999)) assert a['rows'][0][1]=='hold' and b['rows'][0][1]=='hold' and common['rows'][0][1]=='buy' try:run(new,{'cash':'1000','shares':0},[(3,101,102)]) except ValueError:pass else:raise AssertionError('pre-freeze observation rewritten') print('continuing_v1_ending=1001 fresh_v2_ending=993 fresh_common_v1_ending=999 mark=112 fees=0_synthetic') print('v1_rows='+str(a['rows'])+' v2_rows='+str(b['rows'])) print('old_file_preserved=True exclusive_version=True historical_rewrite=rejected model_calls=0 independent_accounts=True')