"""道积君 C03/C07 构造算例:仅依赖 Python 标准库,执行后重建 JSON。""" import json import math from datetime import date from decimal import Decimal, getcontext, ROUND_HALF_UP from pathlib import Path getcontext().prec = 50 D = Decimal def money(x): return float(D(str(x)).quantize(D('.01'), rounding=ROUND_HALF_UP)) def calculate(): out = {} exact = D('12345.67') monthly = exact rows = [] for m in range(1, 13): exact *= D('1.0037') monthly = (monthly * D('1.0037')).quantize(D('.01'), rounding=ROUND_HALF_UP) rows.append([m, str(exact), str(monthly), money(exact), money(monthly-exact)]) out['DJ021'] = {'inputs': {'principal':12345.67,'monthly_rate':.0037,'months':12}, 'rows':rows, 'monthly':float(monthly),'final_round':money(exact),'difference':money(monthly-exact)} rows=[] for r in [0,-.01,-.1,-1]: b=10000.; hist=[] for _ in range(3): b=b*(1+r)+1000 hist.append(money(b)) rows.append([r,*hist,money(b-13000)]) out['DJ022']={'inputs':{'principal':10000,'yearend_contribution':1000,'years':3},'rows':rows,'near_zero':10000*(1+1e-8)**3+1000*sum((1+1e-8)**k for k in range(3))} rows=[] for seq in [[.1,.1,.1],[.3,-.2,.1],[.5,-.5,0]]: prod=math.prod(1+r for r in seq); a=sum(seq)/len(seq); g=prod**(1/len(seq))-1 rows.append({'returns':seq,'arithmetic':a,'geometric':g,'balances':[money(10000*math.prod(1+r for r in seq[:k])) for k in range(1,4)],'arithmetic_end':money(10000*(1+a)**3),'end':money(10000*prod)}) out['DJ023']={'principal':10000,'rows':rows} r=.06; deposits=[(0,100000),(.25,20000),(.75,-15000)] b=100000; last=0;rows=[] for t,cf in deposits[1:]+[(1,0)]: before=b*(1+r)**(t-last); interest=before-b;after=before+cf rows.append([last,t,money(b),money(interest),cf,money(after)]) b=after;last=t out['DJ024']={'rate':r,'rows':rows,'end':b,'cashflow_check':sum(v*(1+r)**(1-t) for t,v in deposits),'late_deposit':sum(v*(1+r)**(1-(.75 if t==.25 else t)) for t,v in deposits)} rows=[] for mode in ['比例期初','比例期末','固定期初','固定期末']: b=100000.;hist=[] for year in range(1,4): start=b if mode=='比例期初':fee=b*.01;b=(b-fee)*1.06 elif mode=='比例期末':gross=b*1.06;fee=gross*.01;b=gross-fee elif mode=='固定期初':fee=1000;b=(b-fee)*1.06 else:fee=1000;b=b*1.06-fee hist.append([year,money(start),money(fee),money(b)]) rows.append({'mode':mode,'rows':hist,'end':money(b)}) out['DJ025']={'rows':rows,'fixed_difference':rows[3]['end']-rows[2]['end']} b=100000;index=1;rows=[] for year,p in enumerate([.02,.05,-.01],1): b*=1.06;index*=1+p;rows.append([year,p,index,money(b),money(b/index)]) avg=sum([.02,.05,-.01])/3 out['DJ026']={'rows':rows,'average_inflation':avg,'arithmetic_index':(1+avg)**3,'arithmetic_real':b/(1+avg)**3,'flat_nominal_real':100000/index} rn=.06;p=.025;rr=(1+rn)/(1+p)-1;rows=[] for year in [1,5,10]: nominal=100000*(1+rn)**year;real=nominal/(1+p)**year;wrong=100000*(1+rr)**year/(1+p)**year rows.append([year,money(nominal),money(real),money(100000*(1+rr)**year),money(wrong),money(real-wrong)]) out['DJ027']={'nominal_rate':rn,'inflation':p,'real_rate':rr,'rows':rows} rows=[] for r in [.005,.01,.02,.04,.06,.08,.1,.12,.2,.3,.5]: exact=math.log(2)/math.log1p(r);approx=.72/r rows.append([r,exact,approx,approx-exact,(approx-exact)/exact,math.ceil(exact)]) out['DJ028']={'rows':rows} start=date(2026,1,15);end=date(2027,9,20);days=(end-start).days out['DJ029']={'start':str(start),'end':str(end),'days':days,'rows':[[name,t,100000*1.06**t] for name,t in [('整月20个月',20/12),('小数年另计5/30月',(20+5/30)/12),('实际天数除365',days/365),('按所在日历年分段',(date(2027,1,1)-start).days/365+(end-date(2027,1,1)).days/365)]],'leap_days':(date(2028,9,20)-date(2027,1,15)).days} b=10000.;cum=10000.;earned=0;rows=[] for k,cf in enumerate([1000,0,-500,2000],1): start=b;i=b*.02;b+=i+cf;cum+=cf;earned+=i rows.append([k,money(start),cf,money(i),money(b),money(cum),money(earned),money(b-cum-earned)]) out['DJ030']={'rate':.02,'rows':rows,'bad_last_balance':money(b+10),'bad_residual':10} flows=[100,110,120,130,140];r=.1;rows=[] for y,cf in enumerate(flows,1):rows.append([y,cf,y,y-.5,cf/(1+r)**y,cf/(1+r)**(y-.5)]) yearend=sum(x[4] for x in rows);mid=sum(x[5] for x in rows) out['DJ061']={'unit':'万元','rate':r,'rows':rows,'yearend':yearend,'midyear':mid,'difference':mid-yearend,'december_point_nine':sum(cf/(1+r)**(y-.1) for y,cf in enumerate(flows,1))} ev=1800;debt=500;cash=260;restricted=60;operating=80;nci=90 excess=cash-restricted-operating out['DJ062']={'enterprise':ev,'debt':debt,'cash':cash,'restricted':restricted,'operating_cash':operating,'excess_cash':excess,'minority_value':nci,'equity':ev-debt+excess-nci,'wrong_all_cash':ev-debt+cash-nci,'wrong_no_minority':ev-debt+excess} fcff=100;rd=.05;tax=.25;debt=400;re=17/120;wacc=.1;interest=rd*debt;fcfe=fcff-interest*(1-tax);ev=fcff/wacc;equity=ev-debt out['DJ063']={'fcff':fcff,'rd':rd,'tax':tax,'debt':debt,'re':re,'interest':interest,'tax_shield':interest*tax,'fcfe':fcfe,'wacc':wacc,'enterprise':ev,'equity_fcff':equity,'equity_fcfe':fcfe/re,'wrong_fcfe_wacc':fcfe/wacc,'wrong_fcff_re_then_debt':fcff/re-debt,'wacc_check':re*equity/ev+rd*(1-tax)*debt/ev} rows=[] for r in [.08,.1,.12]: rows.append({'r':r,'cells':[None if g>=r else 100*(1+g)/(r-g) for g in [.02,.06,.079,.08,.099,.1,.12]]}) out['DJ064']={'yearN_cashflow':100,'g':[.02,.06,.079,.08,.099,.1,.12],'rows':rows} flows=[-80,-60,-20,70,100];r=.12;cash=150;rows=[] for y,cf in enumerate(flows,1):cash+=cf;rows.append([y,cf,cf/(1+r)**y,cash]) tv=100*1.02/(r-.02);negative=sum(cf/(1+r)**y for y,cf in enumerate(flows,1) if cf<0);pv=sum(x[2] for x in rows) out['DJ065']={'rate':r,'rows':rows,'explicit_pv':pv,'negative_pv':negative,'terminal':tv,'terminal_pv':tv/1.12**5,'total':pv+tv/1.12**5,'wrong_drop_negative':pv-negative+tv/1.12**5,'funding_gap':max(0,-min(x[3] for x in rows)),'no_recovery_flows':sum(cf/(1+r)**y for y,cf in enumerate([-80,-60,-20,0,0],1))} p=.025;rr=.07;rn=(1+rr)*(1+p)-1;rows=[] for y,cf in enumerate([100,110,120],1): nominal=cf*(1+p)**y;rows.append([y,cf,nominal,cf/(1+rr)**y,nominal/(1+rn)**y,cf/(1+rn)**y]) out['DJ066']={'inflation':p,'real_discount':rr,'nominal_discount':rn,'rows':rows,'real_pv':sum(x[3] for x in rows),'nominal_pv':sum(x[4] for x in rows),'wrong_pv':sum(x[5] for x in rows)} start=date(2026,1,1);issue=date(2026,7,1);end=date(2027,1,1);n=(end-start).days;after=(end-issue).days;weighted=1000+400*after/n out['DJ067']={'profit_wanyuan':1200,'opening_shares_wan':1000,'new_shares_wan':400,'days':n,'old_only_days':(issue-start).days,'new_days':after,'weighted_shares':weighted,'eps':1200/weighted,'ending_eps':1200/1400,'monthly_approx_eps':1200/1200,'october_weighted':1000+400*(end-date(2026,10,1)).days/n} quarters=[20,25,30,45,26,28,36];last_year=sum(quarters[:4]);prior=sum(quarters[:3]);current=sum(quarters[4:]) out['DJ068']={'quarters':quarters,'last_year':last_year,'prior_9m':prior,'current_9m':current,'ttm':last_year-prior+current,'direct_last_four':sum(quarters[3:]),'wrong_annualize':current*4/3,'wrong_add':last_year+current,'rolling':[sum(quarters[k:k+4]) for k in range(4)]} cfo=500;ppe=150;intangible=40;construction=30;capex=ppe+intangible+construction out['DJ069']={'cfo':cfo,'capex':capex,'capex_split':[ppe,intangible,construction],'fcf':cfo-capex,'financial_investment':200,'acquisition_net':80,'asset_sale':15,'wrong_all_investing_outflows':cfo-(capex+200+80),'net_capex_view':cfo-capex+15,'noncash_asset':60,'unpaid_asset_purchase':25} opening=1000;profit=120;buyback=200;closing=opening+profit-buyback;avg=opening+profit/2-buyback*.5 out['DJ070']={'opening_equity':opening,'profit':profit,'buyback':buyback,'closing_no_buyback':opening+profit,'closing_buyback':closing,'weighted_no_buyback':opening+profit/2,'weighted_buyback':avg,'roe_no_buyback':profit/(opening+profit/2),'roe_buyback':profit/avg,'wrong_end_roe':profit/closing,'january_roe':profit/(opening+profit/2-buyback),'december_roe':profit/(opening+profit/2),'profit_down_roe':100/(opening+100/2-buyback*.5)} return out if __name__ == '__main__': results=calculate() assert math.isclose(results['DJ024']['end'],results['DJ024']['cashflow_check'],rel_tol=1e-12) assert math.isclose(results['DJ063']['equity_fcff'],results['DJ063']['equity_fcfe'],rel_tol=1e-12) assert math.isclose(results['DJ063']['wacc'],results['DJ063']['wacc_check'],rel_tol=1e-12) assert math.isclose(results['DJ066']['real_pv'],results['DJ066']['nominal_pv'],rel_tol=1e-12) assert results['DJ068']['ttm']==results['DJ068']['direct_last_four'] assert all(row[-1]==0 for row in results['DJ030']['rows']) target=Path(__file__).with_suffix('.json') target.write_text(json.dumps({'as_of':'2026-10-23','sample_type':'全部为构造教学示例','articles':results},ensure_ascii=False,indent=2),encoding='utf-8') print(f'已复算 {len(results)} 篇,六组独立勾稽通过;结果:{target}')