
    mjD                    &   d dl mZ d dlmZ d dlZd dlmZ d dlmZm	Z	m
Z
mZmZmZ d dlmZ d dlmZ d dlmZmZ d d	lmZ d d
lmZmZ d Zd Zd Zd Zd Zd Zd Z d Z!d Z"d Z#d Z$d Z%d Z&d Z'd Z(d Z)d Z*d Z+d Z,d Z-d Z.d  Z/d! Z0d" Z1dS )#    )annotations)DecimalN)ContainerConfirmRequest)annualize_returnattribution_bridge_v1price_fx_attribution_v1stable_input_fingerprintttwror_daily_v1xirr_v1)ensure_canonical_cash_accounts)confirm_account)!set_performance_cashflow_coverage$set_performance_scope_classification)connect_memory)apply_migrationsget_schema_versionc                     t          dt          d          fdt          d          fgg           } | j        t          d          k    sJ d S N
2025-01-01d   
2025-12-31n   0.1r
   r   valueresults    0tests/unit/test_sprint14_performance_contract.py&test_ttwror_without_external_cashflowsr      M    |WS\\:\7SV<<<XY[]^^F<75>>))))))    c                     t          dt          d          fdt          d          fgdt          d          dfg          } | j        t          d          k    sJ d S )Nr   r   
2025-01-02   2   external_depositr   r   r   s    r   #test_ttwror_deposit_at_period_startr'      sd    
	%gcll'CD
%7	89 F <75>>))))))r!   c                     t          dt          d          fdt          d          fgdt          d          dfg          } | j        t          d          k    sJ | j        j        d	k    sJ d S )
Nr   r   r#   r$   z2025-01-01T12:00:00Zr%   r&   r   complete)r
   r   r   qualitystatusr   s    r   Ftest_ttwror_matches_timestamped_cashflow_to_its_calendar_day_valuationr,   '   sz    
	%gcll'CD
 '"++/A	BC F <75>>))))> J......r!   c                     t          dt          d          fdt          d          fdt          d          fgg           } | j        J | j        j        dk    sJ d S )Nz2025-01-01T00:00:00Zr   z2025-01-01T23:00:00Ze   r#   r   )ambiguous_daily_valuationr
   r   r   r*   reasonsr   s    r   8test_ttwror_rejects_ambiguous_duplicate_daily_valuationsr2   0   sr    #WS\\2#WS\\273<<(	

 	 F <>!%CCCCCCCr!   c                     t          dt          d          fdt          d          fgdt          d          dfg          } | j        t          d          k    sJ d S )Nr   r   r#   Z   iexternal_withdrawalr   r   r   s    r   $test_ttwror_withdrawal_at_period_endr6   =   sd    
	%gbkk'BC
&;	<= F <75>>))))))r!   c                     t          dt          d          fdt          d          fdt          d          fgdt          d          dfg          } | j        t          d	          k    sJ d S )
Nr   r   
2025-02-01r   
2025-03-01   r%   r&   0.21r   r   s    r   3test_ttwror_geometrically_links_multiple_subperiodsr<   E   sw    73<<(73<<(73<<(	

 %7	89 F <76??******r!   c                     t          dt          d          fdt          d          fgdt          d          dfg          } | j        J | j        j        dk    sJ d S )	Nr   r   r9   r:   r8   r%   r&   )missing_cashflow_valuationr0   r   s    r   7test_ttwror_requires_stored_cashflow_boundary_valuationr?   Q   sp    
	%gcll'CD
%7	89 F <>!%DDDDDDDr!   c                     t          dt          d          fdt          d          fgdt          d          dfg          } | j        J | j        j        dk    sJ d S )Nr   r   r8   r   r&   )invalid_cashflow_signr0   r   s    r   8test_ttwror_rejects_inconsistent_external_cashflow_signsrC   Z   sp    
	%gcll'CD
&8	9: F <>!%???????r!   c                     t          dt          d          fdt          d          fgdt          d          dfg          } | j        J | j        j        dk    sJ d S )Nr   r   r8   r   
   r&   )!cashflow_outside_return_subperiodr0   r   s    r   9test_ttwror_rejects_cashflow_that_has_no_return_subperiodrG   c   sp    
	%gcll'CD
%7	89 F <>!%KKKKKKKr!   c                     t          dt          d          fdt          d          fgg           } | j        t          d          k    sJ d S r   r   r   s    r   =test_ttwror_never_treats_buy_sell_dividend_or_fee_as_externalrI   l   r    r!   c                     t          t          d          d          t          d          k    sJ t          t          d          d          J d S )Nr;   i  z0.100000000000r   im  )r   r    r!   r   2test_ttwror_annualized_is_separate_from_cumulativerL   q   sN    GFOOS11W=M5N5NNNNNGENNC0088888r!   c            
     D   t          dt          d          fdt          d          fdt          d          fdt          d          fd	t          d          fg          } | j        J t          | j        t          d
          z
            t          d          k     sJ d S )Nz
2008-01-01iz
2008-03-01i
  z
2008-10-30i  z
2009-02-15i  z
2009-04-01z0.373362535z
0.00000001)r   r   r   absr   s    r   4test_xirr_matches_documented_excel_reference_examplerO   v   s    76??+74==)74==)74==)74==)	
 F <###v|gm444558M8MMMMMMMr!   c                     t          dt          d          fdt          d          fdt          d          fg          } | j        J | j        j        dk    sJ d S )Nz
2024-01-01iz
2024-02-17iz
2025-04-03i  r)   )r   r   r   r*   r+   )	irregulars    r   &test_xirr_uses_irregular_calendar_daysrR      sm    75>>*74==)74==)	
 I ?&&&#z111111r!   c                     t          dt          d          fdt          d          fg          } | j        J | j        j        dk    sJ d S )Nr   
2026-01-01rA   )insufficient_cashflowsr   r   r   r*   r1   r   s    r   ,test_xirr_without_sign_change_is_unavailablerX      sU    |WT]]3lGCLL5QRSSF<>!%@@@@@@@r!   c                     t          dt          d          fdt          d          fdt          d          fg          } | j        J | j        j        dk    sJ d S )Nr   rT   z
2025-06-01   rU   i`)mwr_multiple_solutionsrW   r   s    r   4test_xirr_with_ambiguous_sign_pattern_is_unavailabler\      sm    
	&ws||(D|U\]aUbUbFcd F <>!%@@@@@@@r!   c                     t          dt          d          t          d          fidt          d          t          d          fi          } | t          d          t          d          fk    sJ d S )Npositionr   z0.9r   z1.0z9.0z11.0)r   r   r   s    r   =test_price_fx_interaction_is_deterministically_assigned_to_fxr_      sm    $	gcllGENN34	gcllGENN34 F genngfoo6666666r!   c                    t          t          d          t          d          t          d          t          d          t          d          t          d          t          d          t          d                    } | d	         t          d
          k    sJ | d         t          d          k    sJ | d         t          d          k    sJ | d         t          d          k    sJ | d         dk    sJ d S )Nr   x   r               opening_valueclosing_valuenet_external_cashflowsmarket_pricefxdividends_and_interestfeestaxesinvestment_result   unattributed_residualrm   rn   r+   r)   r   r   bridges    r   8test_attribution_value_bridge_reconciles_within_one_centrv      s    "cllcll&qzzR[[1::&qzzQZZajj	 	 	F %&'"++5555)*gajj8888&>WR[[(((('?gbkk))))(z))))))r!   c                    t                      } t          |            |                     d           |                     d           |                     d                                          d         dk    sJ |                     d                                          d         }|                     d                                          d         }t          |            t	          |           dk    sJ |                     d                                          d         |k    sJ |                     d                                          d         |k    sJ d S )NzINSERT INTO platforms(platform_id,name,platform_type,default_currency,created_at) VALUES ('p','Bank','bank','CHF','2026-01-01T00:00:00Z')zINSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,created_at) VALUES ('a','p','New household','bank','CHF','2026-01-01T00:00:00Z')>SELECT performance_included FROM accounts WHERE account_id='a'r   zSELECT COUNT(*) FROM audit_logz6SELECT COUNT(*) FROM performance_scope_classifications1   )r   r   executefetchoner   )connaudit_beforeclassification_befores      r   Ktest_schema_46_defaults_new_accounts_out_of_performance_and_repeats_as_noopr      sw   DTLL 	T   	LL 	j   	UVV__aabcd	 	 	 	 << @AAJJLLQOL LL@ hjj Td##r))))<<899BBDDQG<WWWWMNNWWYYZ[\ 	! 	! 	! 	! 	! 	!r!   c                 "   t          t          d          t          d          t          d          d d t          d          t          d          t          d                    } | d         t          d          k    sJ | d	         d
k    sJ d S )Nr   ra   r      re   rf   rq      r+   partialrs   rt   s    r   Btest_attribution_unexplained_remainder_remains_visible_and_partialr      s    "cllcll&qzz&qzzQZZajj	 	 	F )*gbkk9999(y((((((r!   c                 .   t          t          d          t          d          t          d          d d t          d          t          d          t          d                    } | d         J | d         J | d         t          d          k    sJ d S )	Nr   r   r   rf   rj   rk   rq   rE   rs   rt   s    r   ?test_total_value_account_does_not_invent_price_or_fx_componentsr      s    "cllcll&qzz&qzzQZZajj	 	 	F .!)))$<)*gbkk999999r!   c                 X    dddgd} t          |           t          |           k    sJ d S )Nz2026-07-27T12:00:00Z100110)cutoffvalues)r	   )payloads    r   7test_identical_cutoff_payload_has_identical_fingerprintr      s;    /E5>JJG#G,,0H0Q0QQQQQQQr!   c                 <   t                      } t          |            |                     d           |                     d           |                     d                                          d         dk    sJ t	          |                     d                    }t          |            t	          |                     d                    |k    sJ t          |           dk    sJ |                     d                                          d         dk    sJ d S )	NzhINSERT INTO platforms(platform_id,name,platform_type,created_at) VALUES ('p','Bank','bank','2026-01-01')zINSERT INTO accounts(account_id,platform_id,account_name,account_type,created_at) VALUES ('a','p','Household','cash','2026-01-01')rx   r   z;SELECT version,name FROM schema_migrations ORDER BY versionry   zPRAGMA integrity_checkok)r   r   rz   r{   listr   )r|   befores     r   Vtest_performance_scope_migration_defaults_new_accounts_to_excluded_and_repeats_as_noopr      s,   DTLLr   	LL 	M   	UVV__aabcd	 	 	 	 $,,\]]^^FTT\\WXXYY]cccccd##r))))<<011::<<Q?4GGGGGGr!   c                    t                      } t          |            t          | t          dddddd                    }|                     d|j        f                                          d	         d	k    sJ |                     d
|j        f                                          d	         d	k    sJ t          | d          }|d         dk    sJ |                     d                                          d	         d	k    sJ |                     d                                          d	         }t          | d           |                     d                                          d	         }||k    sJ t          j	        t          d          5  |                     d|                     d                                          d	         f           d d d            d S # 1 swxY w Y   d S )NzOrdinary household accountbankzSynthetic BankCHFtestT)account_nameaccount_typeplatform_namecurrencynoteconfirmz<SELECT performance_included FROM accounts WHERE account_id=?r   zISELECT COUNT(*) FROM performance_scope_classifications WHERE account_id=?)
created_bycreated   zSSELECT COUNT(*) FROM accounts WHERE account_type='cash' AND performance_included<>0zeSELECT group_concat(account_id || ':' || performance_included, ',') FROM accounts ORDER BY account_idz.new accounts default outside performance scopematchzINSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,
                 performance_included,created_at)
               VALUES('bypass',?,'Bypass','brokerage','CHF',1,'2026-01-01')z)SELECT platform_id FROM platforms LIMIT 1)r   r   r   r   rz   	entity_idr{   r   pytestraises	Exception)r|   manualensuredr   afters        r   Etest_new_account_paths_are_excluded_until_audited_role_classificationr     sy   DT5*	
 	
 	

 
F <<F		  hjj     <<S		  hjj    
 -TfEEEG9""""<<] hjj    \\o hjjF #4F;;;;LLo hjjE F????	y(X	Y	Y	Y 
 
O \\EFFOOQQRSTV		
 	
 	

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
s   AG%%G),G)c                 	   t                      } t          |            |                     d           dD ]}|                     d||f           t          j        t
          d          5  t          | dddd	d
d           d d d            n# 1 swxY w Y   t          | dddd	dd          sJ |                     d                                          d         }t          | dddd	dd          rJ |                     d                                          d         |k    sJ t          | dddd	dd          sJ t          | dddd	dd          sJ |                     d                                          d         |dz   k    sJ dD ])\  }}t          | |d|d	dd|dk    rdnd  d!          sJ *t          |                     d"                    d#d#d#d$k    sJ t          j        t
          d%          5  t          | dddd	d
&           d d d            n# 1 swxY w Y   |                     d'           |                     d(           t          j        t          d)          5  |                     d*           d d d            n# 1 swxY w Y   t          j        t          d+          5  |                     d,           d d d            n# 1 swxY w Y   t          j        t          d-          5  |                     d.           d d d            n# 1 swxY w Y   t          j        t          d/          5  |                     d0           d d d            n# 1 swxY w Y   |                     d1                                          d         d#k    sJ t          | dd2d3d4d5d6d78          sJ |                     d9                                          d         }t          | dd2d3d4d5d:d;8          rJ |                     d9                                          d         |k    sJ t          j        t          d-          5  |                     d<           d d d            d S # 1 swxY w Y   d S )=NznINSERT INTO platforms(platform_id,name,platform_type,created_at) VALUES('p','Synthetic','broker','2026-01-01'))pftwcryptozINSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,
                 performance_included,created_at) VALUES(?, 'p', ?, 'brokerage', 'CHF', 0, '2026-01-01')zapproved investment roler   r   Tordinary_bank_accountr   z	must failz2026-01-01T00:00:00Z)
account_idincludedclassification_rolesourcer   classified_atpostfinance_etrading_depotapprovedz2026-01-01T00:00:01ZzfSELECT COUNT(*) FROM audit_log WHERE entity_type='performance_scope_classification' AND entity_id='pf'r   repeatz2026-01-01T00:00:02ZF#not_in_investment_performance_scopeexcludedz2026-01-01T00:00:03Zzincluded againz2026-01-01T00:00:04Zr   ))r    canonical_truewealth_total_value)r   crypto_portfoliozapproved rolez2026-01-01T00:00:0r      r   ZzHSELECT account_id,performance_included FROM accounts ORDER BY account_idre   )r   r   r   zapproved non-investment role)r   r   r   r   r   zINSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,
             performance_included,created_at) VALUES('bogus','p','Bogus','brokerage','CHF',0,'2026-01-01')a  INSERT INTO audit_log(audit_id,timestamp,source,action,entity_type,entity_id,
             old_values_json,new_values_json,user_text_note,created_by,created_at)
           VALUES('bogus-audit','2026-01-01','test','other','other','bogus','{}',
             '{"performance_included":1,"classification_role":"postfinance_etrading_depot"}',
             'invalid audit','test','2026-01-01')zinvalid or unauditeda"  INSERT INTO performance_scope_classifications(
                 account_id,included,classification_role,decision_version,audit_id,classified_at)
               VALUES('bogus',1,'postfinance_etrading_depot','investment_performance_scope_v1',
                      'bogus-audit','2026-01-01')znew matching auditz]UPDATE performance_scope_classifications SET classified_at='2026-02-01' WHERE account_id='pf'zcannot be deletedzCDELETE FROM performance_scope_classifications WHERE account_id='pf'z&flag must match audited classificationz@UPDATE accounts SET performance_included=0 WHERE account_id='pf'z?SELECT performance_included FROM accounts WHERE account_id='pf'r   r   r)   synthetic_ledger_reconciliationz(Complete source-to-ledger reconciliationz2026-01-02T00:00:00Z)r   coverage_fromcoverage_tor+   r   r   recorded_atzPSELECT COUNT(*) FROM audit_log WHERE entity_type='performance_cashflow_coverage'zIdempotent repeatz2026-01-03T00:00:00Zz?DELETE FROM performance_cashflow_coverage WHERE account_id='pf')r   r   rz   r   r   
ValueErrorr   r{   dictr   r   )r|   r   audit_countrolecoverage_auditss        r   Ntest_scope_classification_is_role_gated_idempotent_and_every_change_is_auditedr   ;  s   DTLLx   - 
 

l$	
 	
 	
 	

 
z)C	D	D	D 	
 	
, 70	
 	
 	
 	
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 08,      ,,p hjjK 48,      <<p hjj"# # # # 0A,      08,      <<p hjj"Q' ' ' ' 
 

D 4! $ PJ$4F4FqqAPPP
 
 
 	
 	
 
 	
 _`` Qa	(	() ) ) ) 
z)G	H	H	H 
 
, 7	
 	
 	
 	

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	LL	n   	LL	5   
y(>	?	?	? 
 
5	
 	
 	

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
y(<	=	=	= 
 
k	
 	
 	

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
y(;	<	<	< \ \Z[[[\ \ \ \ \ \ \ \ \ \ \ \ \ \ \	y(P	Q	Q	Q Y YWXXXY Y Y Y Y Y Y Y Y Y Y Y Y Y Y<<I hjj    -" 07*	 	 	 	 	 	 llZ hjjO 1" 0 *	 	 	 	 	 	 <<Z hjj&' ' ' ' 
y(;	<	<	< X XVWWWX X X X X X X X X X X X X X X X X Xs~   +BBBH$$H(+H(4JJJ<KK"%K"L&&L*-L*M..M25M2Q<<R R )2
__future__r   decimalr   r   $jarvis_finance.api.schemas.positionsr   !jarvis_finance.ledger.performancer   r   r   r	   r
   r   $jarvis_finance.services.cash_servicer   ,jarvis_finance.services.manual_entry_servicer   )jarvis_finance.services.performance_scoper   r   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r   r'   r,   r2   r6   r<   r?   rC   rG   rI   rL   rO   rR   rX   r\   r_   rv   r   r   r   r   r   r   r   rK   r!   r   <module>r      s   " " " " " "        H H H H H H                P O O O O O H H H H H H        ; : : : : : R R R R R R R R* * *
* * */ / /
D 
D 
D* * *	+ 	+ 	+E E E@ @ @L L L* * *
9 9 9
N N N	2 	2 	2A A AA A A7 7 7* * *$  4) ) ): : : R R R
H H H,+
 +
 +
\YX YX YX YX YXr!   