
    "ojd                       d dl mZ d dlZd dlmc mZ d dlm	Z	 d dl
Z
d dlZd dlmZ d dlmZ d dlmZ d dlmZ d dlmZmZmZmZ d d	lmZ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ddddde$dddd
dRd&Z&dddd'd'ddd(ddd)
dSd4Z'd5 Z(d6 Z)d7 Z*d8 Z+d9 Z,d: Z-d; Z.d< Z/d= Z0d> Z1d? Z2d@ Z3dA Z4dB Z5dC Z6dD Z7dE Z8dF Z9dG Z:dH Z;dI Z<dJ Z=dK Z>dL Z?dM Z@dN ZAdO ZBdP ZCdQ ZDdS )T    )annotationsN)Decimal)
TestClient)get_db)
create_app)fifo_v1)Activityeffective_activitiestwr_v1xirr_v1)build_performance_coveragebuild_portfolio_performance)!set_performance_cashflow_coverage$set_performance_scope_classification)connect_memory)MIGRATION_VERSIONapply_migrationsz2026-07-23T12:00:00+00:00c            
     V   t                      } t          |            |                     dt          f           |                     dt          f           t	          | dddddt                     t          | dd	d
dddt                     |                     dt          f           | S )NzdINSERT INTO platforms(platform_id,name,platform_type,created_at) VALUES ('p','Synthetic','broker',?)zINSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,created_at) VALUES ('a','p','Control','brokerage','CHF',?)aTcrypto_portfoliosynthetic_test_fixturezControl portfolio)
account_idincludedclassification_rolesourcenoteclassified_atz
1900-01-01z
2100-12-31completez#Complete synthetic cashflow historyr   coverage_fromcoverage_tostatusr   r   recorded_atzyINSERT INTO instruments(instrument_id,asset_class,name,currency,created_at) VALUES ('i','equity','Synthetic ETF','CHF',?))r   r   executeNOWr   r   conns    \/home/agent/.hermes/repos/FinanceManager/tests/unit/test_portfolio_performance_foundation.pydbr)      s    DTLLn	   	LL 	T	   ).'     &" '2	 	 	 	 	LL 	D	   K    CHFaccountr      r   )
currencyfx
scope_kindscope_idr   versioncaptured_at
supersedesquality_statusreasonssnapshot_idstratvaluer.   r/   
str | Noner0   r1   r   r2   intr3   r4   r5   r6   list[str] | Nonec       
            |                      d||||||d|d|d|
|	|d| |t          j        |pg           f           d S )Nay  
        INSERT INTO portfolio_valuation_snapshots(
          snapshot_id,scope_kind,scope_id,account_id,value_original,currency,base_currency,
          fx_rate_to_base,fx_direction,valuation_at,source,captured_at,snapshot_version,
          supersedes_snapshot_id,source_reference,quality_status,reason_codes_json
        ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        r+   original_to_basesynthetic_fixturezref-)r$   jsondumps)r'   r7   r9   r:   r.   r/   r0   r1   r   r2   r3   r4   r5   r6   s                 r(   add_valuationrC   @   sy    " 	LL	  ;  Jw}"%%#	
    r*   01)
instrument_idquantitygrossfeetaxnetr.   r/   reversal_oftransfer_groupactivity_idkindrF   rG   rH   rI   rJ   rK   rL   rM   c       
            |                      d|||d||d d         |d d         ||||||	|
||dndddd| d	| d
|dndt          ||d| f           d S )Na4  
        INSERT INTO transactions(
          transaction_id,transaction_type,activity_kind,account_id,instrument_id,trade_date,
          booking_date,event_timestamp,quantity,gross_amount_original,fee_original,tax_original,
          net_amount_original,currency_original,fx_rate_to_chf,fx_status,source_type,source_id,
          external_transaction_id,row_hash,is_confirmed,quality_status,created_at,
          reversal_of_transaction_id,internal_transfer_group_id,source_reference
        ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        r   
   okmissingsynthetic_importzfixture.csvz	external-zhash-r-   
incompletezline-)r$   r%   )r'   rN   rO   r9   rF   rG   rH   rI   rJ   rK   r.   r/   rL   rM   s                 r(   add_activityrV   o   s    " 	LL	 ssGssGNDD	%%%!K!!NDD!K!!5	
& & & & &r*   c                L   t          | ddd           t          | ddd           t          | ddd	           t          | d
dddd           t          | dddd           t          | ddddddd           t          | dddddddd	  	         t          | d d!d"dd#$           d S )%Nv1z2025-01-01T00:00:0010000v22025-02-01T00:00:0011000v3z2025-03-01T00:00:0017600vi720
instrumentir0   r1   depositexternal_deposit5000rK   buyz2025-01-05T00:00:001010005)rF   rG   rH   rI   sellz2025-02-10T00:00:00448023)rF   rG   rH   rI   rJ   transferinternal_transferz2025-02-15T00:00:00zpair-1)rK   rM   )rC   rV   r&   s    r(   add_control_portfoliors      s    $3W===$3W===$3W===$3U|^abbbby"46KQWXXXXue%:#X\djpsttttvv'<CZ]ejpsy|}}}}z#68MSYjrssssssr*   c                    t          dt          d          fdt          d          fdt          d          fgdt          d          fg          } | j        }d}t          |          }||k    }|st          j        d	|fd
||f          dt          j                    v st          j        |           rt          j        |           ndt          j        |          dt          j                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j
        |                    d x}x}x}}| j        }|j        }d}||k    }|st          j        d	|fd||f          dt          j                    v st          j        |           rt          j        |           ndt          j        |          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j
        |	                    d x}x}x}}d S )N
2025-01-01rY   
2025-02-01r\   
2025-03-01r^   rf   0.21==zK%(py2)s
{%(py2)s = %(py0)s.value
} == %(py8)s
{%(py8)s = %(py4)s(%(py6)s)
}resultr   py0py2py4py6py8assert %(py10)spy10r   )zK%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.quality
}.status
} == %(py7)sr~   r   r   py7assert %(py9)spy9)r   r   r:   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationqualityr"   )
r|   @py_assert1@py_assert5@py_assert7@py_assert3@py_format9@py_format11@py_assert6@py_format8@py_format10s
             r(   .test_twr_control_is_exactly_twenty_one_percentr      s   
((	)L'':J:J+Kl\cdk\l\lMmn
	() F <*6*76??*<?**********<?************6*****6******<***********7*****7******6****?*****************************>.> .J. J.......... J............6.....6......>.... ....J...............................r*   c                    t          dt          d          fdt          d          fgdt          d          fg          } | j        }d }||u }|st          j        d|fd||f          d	t          j                    v st          j        |           rt          j        |           nd	t          j        |          t          j        |          d
z  }dd|iz  }t          t          j
        |                    d x}x}}| j        }|j        }d}||k    }|st          j        d|fd||f          d	t          j                    v st          j        |           rt          j        |           nd	t          j        |          t          j        |          t          j        |          dz  }dd|iz  }t          t          j
        |                    d x}x}x}}d S )Nru   rY   rw   r^   rv   rf   isz-%(py2)s
{%(py2)s = %(py0)s.value
} is %(py5)sr|   r~   r   py5assert %(py7)sr   )missing_cashflow_valuationry   zL%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.quality
}.reasons
} == %(py7)sr   r   r   )r   r   r:   r   r   r   r   r   r   r   r   r   r6   	r|   r   @py_assert4r   @py_format6r   r   r   r   s	            r(   4test_twr_refuses_cashflow_without_boundary_valuationr      s)   
((	)L'':J:J+KL
	() F <4<4<466<4>D>!D%DD!%DDDDDDDDDDD!%DDDDDDDDDDDDD6DDDDD6DDDDDD>DDDD!DDDD%DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDr*   c                    t          dt          d          fdt          d          fgdt          d          fg          } | j        }d}t          |          }||k    }|st          j        d|fd	||f          d
t          j                    v st          j        |           rt          j        |           nd
t          j        |          dt          j                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j
        |                    d x}x}x}}d S )Nz2025-01-01T10:00:00+02:00100z2025-02-01T08:00:00Z165z2025-01-01T08:00:00Z500.1ry   r{   r|   r   r}   r   r   )r   r   r:   r   r   r   r   r   r   r   r   )r|   r   r   r   r   r   r   s          r(   ?test_twr_normalizes_timezone_offsets_before_matching_boundariesr      s   
%wu~~	69OQXY^Q_Q_8`a
 '$--	01 F <)5)75>>)<>))))))))))<>))))))))))))6)))))6))))))<)))))))))))7)))))7))))))5))))>)))))))))))))))))))))))))))))))r*   c                    t                      } t          | dddd           t          | dddd           t          | d	d
dd           t          | ddt                    }|d         d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}d |d         D             }ddg}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}d S )Nopenz2026-01-01T09:00:00+01:00r   r-   )r2   zclose-earlyz2026-01-02T10:00:00+01:00105zclose-canonicalz2026-01-02T23:30:00+01:00110   
2026-01-01z
2026-01-02	from_dateto_datedata_cutoffsummaryclosing_valuery   z%(py1)s == %(py4)spy1r   assert %(py6)sr   ttwror_cumulativer   c                .    g | ]}|d          dd         S )r9   NrQ    .0points     r(   
<listcomp>z\test_performance_reader_canonicalizes_multiple_times_on_one_business_day.<locals>.<listcomp>   s%    @@@E$K@@@r*   time_series	r)   rC   r   r%   r   r   r   r   r   r'   r|   @py_assert0r   @py_assert2@py_format5@py_format7s          r(   Htest_performance_reader_canonicalizes_multiple_times_on_one_business_dayr      s   44D$ ;UANNNN$'BESTUUUU$)+FWXYYYY(	  F )_-66-6666666666-66666-6666666666666666666666666666666)01:U:1U::::::::::1U:::::1::::U:::::::::::::::::::::::::::@@&*?@@@`\S_D``@D```````````@D``````@````D``````````````````````````````r*   c            	     $   t          dt          d          fdt          d          fg          } | j        }d}t          |          }||k    }|st          j        d|fd||f          dt          j                    v st          j        |           rt          j        |           ndt          j        |          d	t          j                    v st          j        t                    rt          j        t                    nd	t          j        |          t          j        |          d
z  }dd|iz  }t          t          j
        |                    d x}x}x}}dt          d          fg}t          |          }|j        }|j        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        t                     rt          j        t                     ndt          j        |          t          j        |          t          j        |          t          j        |          t          j        |          dz  }	dd|	iz  }
t          t          j
        |
                    d x}x}x}x}x}}d S )Nru   z-1000r   1100z0.100000000000ry   r{   r|   r   r}   r   r   )insufficient_cashflows)zk%(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}.quality
}.reasons
} == %(py11)sr   )r~   r   r   r   r   py11zassert %(py13)spy13r   r   r:   r   r   r   r   r   r   r   r   r   r6   )r|   r   r   r   r   r   r   @py_assert10@py_assert9@py_format12@py_format14s              r(   5test_xirr_one_year_control_and_insufficient_structurer      s   |WW%5%56wv8WXYYF<4#347#3444<44444444444<44444444444446444446444444<444444444447444447444444#34444444444444444444444444444444444!77#3#345e7566e6>e>FeJeeFJeeeeeeeeeeeFJeeeeeeeeeeeee7eeeee7eeeeee5eeee6eeee>eeeeFeeeeJeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer*   c                 J
   t          ddt          d          t          d          t          d          t          d          dddt          d          t          d	          t          d
          t          d          dg          } | j        }d}t          |          }||k    }|st          j        d|fd||f          dt          j                    v st          j        |           rt          j        |           ndt          j        |          dt          j                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j
        |                    d x}x}x}}| j        }d}t          |          }||k    }|st          j        d|fd||f          dt          j                    v st          j        |           rt          j        |           ndt          j        |          dt          j                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j
        |                    d x}x}x}}| j        }d}t          |          }||k    }|st          j        d|fd||f          dt          j                    v st          j        |           rt          j        |           ndt          j        |          dt          j                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j
        |                    d x}x}x}}| j        d         }|j        }d}	t          |	          }
||
k    }|st          j        d|fd||
f          t          j        |          t          j        |          dt          j                    v st          j        t                    rt          j        t                    ndt          j        |	          t          j        |
          dz  }dd|iz  }t          t          j
        |                    d x}x}x}x}	}
d S )Nrh   ri   rj   rk   rD   rN   rO   rG   rH   rI   rJ   rl   rm   rn   ro   rp   6ry   zX%(py2)s
{%(py2)s = %(py0)s.remaining_quantity
} == %(py8)s
{%(py8)s = %(py4)s(%(py6)s)
}r|   r   r}   r   r   603zZ%(py2)s
{%(py2)s = %(py0)s.remaining_cost_basis
} == %(py8)s
{%(py8)s = %(py4)s(%(py6)s)
}73zR%(py2)s
{%(py2)s = %(py0)s.realized_pnl
} == %(py8)s
{%(py8)s = %(py4)s(%(py6)s)
}r   402)zZ%(py3)s
{%(py3)s = %(py1)s.allocated_cost_basis
} == %(py9)s
{%(py9)s = %(py5)s(%(py7)s)
})r   py3r   r   r   zassert %(py11)sr   )r   r   remaining_quantityr   r   r   r   r   r   r   r   remaining_cost_basisrealized_pnl	disposalsallocated_cost_basis)r|   r   r   r   r   r   r   r   r   r   @py_assert8r   r   r   s                 r(   %test_fifo_v1_independent_control_caser      s   !5gdmmV]^dVeVenuvynznz  DK  LO  DP  DP  Q  Q"FW^_dWeWenuvynznz  DK  LO  DP  DP  Q  Q	
 F $444$4444444444$4444444444446444446444444$4444444444444444444444444444444444444444444444444444444&8%8'%..8&.8888888888&.8888888888886888886888888&88888888888'88888'888888%8888.88888888888888888888888888888/$/'$--/-//////////-////////////6/////6/////////////////'/////'//////$////-/////////////////////////////AE3EuEwu~~E3~EEEEEEEEEE3~EEEEEEEEE3EEEEEEEEEEEwEEEEEwEEEEEEuEEEE~EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEr*   c                 	   t          ddt          d          t          d          t          d          t          d          dddt          d          t          d	          t          d          t          d          dd
dt          d          t          d          t          d          t          d          dg          } d | j        D             }dt          d          fdt          d          fg}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}| j        }d}t          |          }||k    }|st          j        d|fd||f          dt          j
                    v st          j        |           rt          j        |           ndt          j        |          dt          j
                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }	dd|	iz  }
t          t          j        |
                    d x}x}x}}| j        }d}t          |          }||k    }|st          j        d|fd||f          dt          j
                    v st          j        |           rt          j        |           ndt          j        |          dt          j
                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }	dd|	iz  }
t          t          j        |
                    d x}x}x}}| j        }d}t          |          }||k    }|st          j        d|fd||f          dt          j
                    v st          j        |           rt          j        |           ndt          j        |          dt          j
                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }	dd|	iz  }
t          t          j        |
                    d x}x}x}}d S )Nb1rh   ro   200rD   r   b2rp   360srl   rm   600c                *    g | ]}|j         |j        fS r   )lot_activity_idrG   )r   items     r(   r   zBtest_fifo_sale_consumes_multiple_lots_in_order.<locals>.<listcomp>   s"    OOOdT!4=1OOOr*   ry   r   r   r   r   rE   r   r|   r   r}   r   r   120r   156r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r|   r   r   r   r   r   r   r   r   r   r   s              r(   .test_fifo_sale_consumes_multiple_lots_in_orderr      s   ePWX]P^P^gnorgsgs  }D  EH  }I  }I  	J  	JePWX]P^P^gnorgsgs  }D  EH  }I  }I  	J  	JVPWX]P^P^gnorgsgs  }D  EH  }I  }I  	J  	J  F
 POf>NOOOUY[bcf[g[gThkoqxy|q}q}j~SOSOSOS$444$4444444444$4444444444446444446444444$4444444444444444444444444444444444444444444444444444444&8%8'%..8&.8888888888&.8888888888886888886888888&88888888888'88888'888888%8888.888888888888888888888888888880%0'%..0.0000000000.000000000000600000600000000000000000'00000'000000%0000.0000000000000000000000000000000r*   c                 P   t          ddddddt          d          t          d                    } t          d	d
ddddd          }t          | |g          \  }}g }||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          dz  }dd|iz  }t          t          j
        |                    d x}}d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          dz  }dd|iz  }t          t          j
        |                    d x}}d S )Nxrh   r   ru   r+   rE   r   )rG   rH   rreversalz
2025-01-02)rL   ry   z%(py0)s == %(py3)s	effectiver~   r   assert %(py5)sr   r   r6   r	   r   r
   r   r   r   r   r   r   r   r   )originalr   r   r6   r   r   @py_format4r   s           r(   4test_reversal_neutralizes_original_without_overwriter     s   UC|UU\]`UaUaipqviwiwxxxHZlL%]`aaaH-x.BCCIw9?9997b=7b77br*   c                    t          ddddddt          d          d          } t          | g          \  }}g }||k    }|st          j        d	|fd
||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          dz  }dd|iz  }t          t          j
        |                    d x}}d}||k    }|st          j        d	|fd
||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          dz  }dd|iz  }t          t          j
        |                    d x}}d S )Nr   unsupportedr   ru   r+   r   F)rH   	supportedry   r   r   r   r   r   )unsupported_activityr6   r   )r  r   r6   r   r   r  r   s          r(   3test_unsupported_activity_is_excluded_and_explainedr    s   3sL,PU]dej]k]kw|}}}K-{m<<Iw9?999//7///////////7/////////////7/////7//////////////////////////////////r*   c                 	   t                      } t          |            t          | ddt                    }t          | ddt                    }||k    }|st	          j        d|fd||f          dt          j                    v st	          j        |          rt	          j	        |          nddt          j                    v st	          j        |          rt	          j	        |          nddz  }d	d
|iz  }t          t	          j        |                    d }|d         }ddddddd |d         d         dd|d         d         dddd}||k    }|slt	          j        d|fd||f          t	          j	        |          t	          j	        |          dz  }dd|iz  }	t          t	          j        |	                    d x}x}}|d         d         }d }||u}|slt	          j        d|fd||f          t	          j	        |          t	          j	        |          dz  }dd|iz  }	t          t	          j        |	                    d x}x}}|d         d          d!         }d"}||k    }|slt	          j        d|fd||f          t	          j	        |          t	          j	        |          dz  }dd|iz  }	t          t	          j        |	                    d x}x}}|d         d#         d!         }d"}||k    }|slt	          j        d|fd||f          t	          j	        |          t	          j	        |          dz  }dd|iz  }	t          t	          j        |	                    d x}x}}|d$         }d%d&dd'g}||k    }|slt	          j        d|fd||f          t	          j	        |          t	          j	        |          dz  }dd|iz  }	t          t	          j        |	                    d x}x}}|d(         }t          |          }d)}
||
k    }|st	          j        d|fd*||
f          d+t          j                    v st	          j        t                    rt	          j	        t                    nd+t	          j	        |          t	          j	        |          t	          j	        |
          d,z  }d-d.|iz  }t          t	          j        |                    d x}x}x}}
d S )/Nru   rw   r   ry   )z%(py0)s == %(py2)sfirstsecond)r~   r   zassert %(py4)sr   r   rY   r^   rf   7rp   rx   xirr_annualized2600mwrr   117r   )opening_valuer   net_external_cashflowsfeestaxesr   ttwror_annualizedr  investment_resulttwrr  r   unrealized_pnlr   r   r   r   r   )is not)z%(py1)s is not %(py4)sr   r  r"   r   
cost_basisexternal_cashflowsr[   re   )r9   rO   amountinput_fingerprint@   z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slenr   r   r   )r)   rs   r   r%   r   r   r   r   r   r   r   r   r  )r'   r	  r
  r   @py_format3r   r   r   r   r   r   r   r   r   s                 r(   8test_full_control_portfolio_contract_and_reproducibilityr!    sn   44D$'	  E )	  F F?5F55FF   "(#! +,=>#Y& %                                                    E".$."$.........."$....."....$...........................E"8,:
:,
::::::::::,
:::::,::::
:::::::::::::::::::::::::::L)(3AzA3zAAAAAAAAAA3zAAAAA3AAAAzAAAAAAAAAAAAAAAAAAAAAAAAAAA%&w2GQcou+v+v*ww&*wwwwwwwwwww&*wwwwww&wwww*wwwwwwwwwwwwwwwwwwwwwwwwwwww()03)**0b0*b0000000000*b0000000000003000003000000)0000*0000b0000000000000000000000000000000r*   c                    t                      } t          | ddd           t          | ddd           t          | ddt                    }|d         d	         }d
}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         }d }||u }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}d S )NrX   ru   rY   rZ   
2025-12-31r\   r   r   r  r   ry   r   r   r   r   r   r   z%(py1)s is %(py4)sr   r  r"   unavailabler   r   s          r(   >test_total_value_account_has_performance_without_invented_lotsr&  5  s`   44D$lG444$lG444(|ilmmmF)U#,u,#u,,,,,,,,,,#u,,,,,#,,,,u,,,,,,,,,,,,,,,,,,,,,,,,,,,)34<<4<<<<<<<<<<4<<<<<4<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<)\*84EE4EEEEEEEEEE4EEEEE4EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEr*   c                    t                      } t          | ddd           t          | ddd           t          | ddd	d
           t          | dddt                    }|d         d         }d }||u }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}|d         d         }d }||u }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}d}|d         d         d         }||v }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}d}|d         d         d         }||v }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}d S )NrX   ru   rY   rZ   r#  r\   zunknown-flowunclassified_cash_movement
2025-06-01500rg   bothr   r   methodr   r   r   r   r$  r   r   r   r  "cashflow_classification_incompleter   ttwrorreason_codesinz%(py1)s in %(py4)sxirr
r)   rC   rV   r   r%   r   r   r   r   r   r   s          r(   Ftest_unclassified_period_activity_blocks_both_ttwror_and_personal_xirrr6  ?  sC   44D$lG444$lG444~'C\W\]]]](  F )019T91T99999999991T9999919999T999999999999999999999999999)./747/47777777777/477777/77774777777777777777777777777777/^6)3DX3N~3^^/3^^^^^^^^^^^/3^^^^^^/^^^^3^^^^^^^^^^^^^^^^^^^^^^^^^^^^/\6)3DV3L^3\\/3\\\\\\\\\\\/3\\\\\\/\\\\3\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\r*   c            
     (   t                      } t          | ddddddd           t          | d	dd
           t          | ddd           t          | dddt                    }|d         d         }d }||u }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}|d         d         }d }||u }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}|d         d         d         }dg}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}d}|d         d         d         }||v }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}d S ) Nr   ru   r#  r%  synthetic_missing_historyz%No complete external cashflow historyz2026-07-23T12:01:00+00:00r   rX   rY   rZ   r\   r+  r,  r   r   r   r$  r   r   r   r  r   r/  r0  !external_cashflow_history_missingry   r   r4  r1  r3  )
r)   r   rC   r   r%   r   r   r   r   r   r   s          r(   Qtest_missing_cashflow_history_evidence_blocks_returns_even_without_recorded_flowsr:  S  s   44D%" *4/	 	 	 	 $lG444$lG444(  F )019T91T99999999991T9999919999T999999999999999999999999999)./747/47777777777/477777/77774777777777777777777777777777)X&~6 +; 6 ;        6 ;     7   ;                          /[&2CF2KN2[[.2[[[[[[[[[[[.2[[[[[[.[[[[2[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[r*   c                 B   t                      } t          | ddd           t          | ddd           t          | ddd	d
           t          | ddddd           t          | dddt                    }|d         d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}|d         d         d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}|d         d         d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}d S ) NrX   ru   rY   rZ   r#  r\   zold-unknownr(  
2024-06-01r*  rg   zperiod-dividenddividendr)  r   )rH   rK   r+  r,  r   r   r   ry   r   r   r   r   r   r/  r"   r   r4  r5  r   s          r(   Ktest_historical_unsupported_activity_does_not_poison_later_period_cashflowsr>  r  s   44D$lG444$lG444}&BLV[\\\\(*l%UZ[[[[(  F )01:U:1U::::::::::1U:::::1::::U:::::::::::::::::::::::::::)X&x0>J>0J>>>>>>>>>>0J>>>>>0>>>>J>>>>>>>>>>>>>>>>>>>>>>>>>>>)V$X.<*<.*<<<<<<<<<<.*<<<<<.<<<<*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<r*   c                    t                      } |                     dddg           t          | ddt                    }|d         d         }d	}||k    }|slt	          j        d
|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         }dg}||k    }|slt	          j        d
|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}d S )Nz
        INSERT INTO account_value_snapshots(
          snapshot_id,account_id,valuation_date,total_value_chf,currency,source_type,
          quality_status,created_at,updated_at
        ) VALUES (?,?,?,?,?,?,?,?,NULL)
        )zlegacy-openr   ru   rY   r+   manual_total_valuerR   z2025-01-02T00:00:00Z)zlegacy-closer   r#  r\   r+   r@  rR   z2026-01-01T00:00:00Zru   r#  r   r   r  r   ry   r   r   r   r   sourceszlegacy:manual_total_value)	r)   executemanyr   r%   r   r   r   r   r   r   s          r(   Atest_existing_immutable_legacy_account_values_are_bound_read_onlyrC    s   44D	 ss	
   )|ilmmmF)U#,u,#u,,,,,,,,,,#u,,,,,#,,,,u,,,,,,,,,,,,,,,,,,,,,,,,,,,)=!< == =========== ========== ==============================r*   c                    t                      } t          | ddddd            t          | ddddd            t          | ddt          	          }|d
         d         }d }||u }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}d}|d         d         d         }||v }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}d S )NrX   ru   rY   EURr.   r/   rZ   r#  r\   r   r   r  r   r$  r   r   r   
missing_fxr   r0  r1  r3  r   r   s          r(   =test_missing_foreign_fx_never_becomes_one_or_zero_performancerH    s   44D$lGeMMMM$lGeMMMM(|ilmmmF)U#+t+#t++++++++++#t+++++#++++t+++++++++++++++++++++++++++C6),U3NCC<CCCCCCCCCCC<CCCCCC<CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCr*   c                    t                      } t          | ddddd           t          | ddd	dd
           t          | ddt                    }|d         d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         }d	}t          |          }d}	t          |	          }
||
z  }d}||z
  }t          |          }||k    }|st	          j        d|fd||f          t	          j        |          dt          j                    v st	          j        t                    rt	          j        t                    nddt          j                    v st	          j        t                    rt	          j        t                    ndt	          j        |          t	          j        |          dt          j                    v st	          j        t                    rt	          j        t                    ndt	          j        |	          t	          j        |
          t	          j        |          t	          j        |          d
z  }dd|iz  }t          t	          j        |                    d x}x}x}x}x}	x}
x}x}x}}d S )NrX   ru   rY   rE  z0.9rF  rZ   r#  r\   z1.0r   r   r  z9000.0ry   r   r   r   r   r   z11000.0r  9000r-   )z%(py1)s == %(py19)s
{%(py19)s = %(py3)s(((%(py8)s
{%(py8)s = %(py4)s(%(py6)s)
} / %(py13)s
{%(py13)s = %(py9)s(%(py11)s)
}) - %(py16)s))
}r8   r   )
r   r   r   r   r   r   r   r   py16py19zassert %(py21)spy21)r)   rC   r   r%   r   r   r   r   r   r   r8   r   r   r   )r'   r|   r   r   r   r   r   r   r   r   @py_assert12@py_assert14@py_assert15@py_assert17@py_assert18@py_format20@py_format22s                    r(   8test_foreign_valuation_uses_stored_changing_fx_snapshotsrU    s   44D$lGeNNNN$lGeNNNN(|ilmmmF)_-99-9999999999-99999-9999999999999999999999999999999)_-::-::::::::::-:::::-:::::::::::::::::::::::::::::::)U#R7R77+;+;RfRgfooR+;o+MRPQR+MPQ+QRs+Q'R'RR#'RRRRRRRRRRR#'RRRRRR#RRRRRRRRRRRsRRRRRsRRRRRRRRRRRRR7RRRRR7RRRRRR7RRRR+;RRRRRRRRRRRgRRRRRgRRRRRRfRRRRoRRRRPQRRRR'RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRr*   c            
        t                      } t          | ddd           t          | ddd           t          | ddd	d
ddd           t          | ddt                    }|d         }g }||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}|d         d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}|d         d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j	        |                    d x}x}}d S )NrX   ru   rY   rZ   r#  10070divr=  r)  rb   r   3070)rF   rH   rJ   rK   r   r  ry   r   r   r   r   r   r  r  rD   r5  r   s          r(   Btest_dividend_withholding_and_net_credit_are_not_external_cashflowr[    sy   44D$lG444$lG444uj,cQV\`fjkkkk(|ilmmmF&'-2-'2----------'2-----'----2---------------------------)W%--%----------%-----%-------------------------------)56=#=6#==========6#=====6====#=============================r*   c                    t                      } t          | ddd           t          | ddt                    }|d         d         }d }||u }|slt	          j        d	|fd
||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         }d }||u }|slt	          j        d	|fd
||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         d         }t          |          }ddh}||k    }	|	st	          j        d|	fd||f          dt          j                    v st	          j        t                    rt	          j        t                    ndt	          j        |          t	          j        |          t	          j        |          dz  }
dd|
iz  }t          t	          j        |                    d x}x}x}	}d S )Nmiddler)  rY   ru   r#  r   r   r  r   r$  r   r   r   r  r   r0  missing_opening_valuationmissing_closing_valuation>=z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} >= %(py7)ssetr   r   r   )r)   rC   r   r%   r   r   r   r   r   rc  r   r   r   )r'   r|   r   r   r   r   r   r   r   r   r   r   s               r(   :test_missing_opening_or_closing_valuation_has_null_returnsrd    s   44D$,888(|ilmmmF)U#+t+#t++++++++++#t+++++#++++t+++++++++++++++++++++++++++)U#+t+#t++++++++++#t+++++#++++t+++++++++++++++++++++++++++i '7v3788v=XZu<vv8<vvvvvvvvvvv8<vvvvvvvvvvvvv3vvvvv3vvvvvv7vvvv8vvvv<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvr*   c                    t                      } t          | ddd           t          | dddddg	           t          | ddt          
          }|d         d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}|d         d         d         }dg}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}}d S )NrX   ru   rY   rZ   r#  r\   partialstale_valuation)r5   r6   r   r   r  r   ry   r   r   r   r   r   r"   r0  r   r   s          r(   >test_stale_snapshot_keeps_calculation_but_marks_metric_partialrh    sv   44D$lG444$lGIXiWjkkkk(|ilmmmF)U#,u,#u,,,,,,,,,,#u,,,,,#,,,,u,,,,,,,,,,,,,,,,,,,,,,,,,,,)U#H-::-::::::::::-:::::-:::::::::::::::::::::::::::::::)U#N3J8I7JJ37JJJJJJJJJJJ37JJJJJJ3JJJJ7JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJr*   c                    t          dt          d          fdt          d          fdt          d          fg          } | j        }d }||u }|st          j        d|fd||f          d	t          j                    v st          j        |           rt          j        |           nd	t          j        |          t          j        |          d
z  }dd|iz  }t          t          j
        |                    d x}x}}| j        }|j        }d}||k    }|st          j        d|fd||f          d	t          j                    v st          j        |           rt          j        |           nd	t          j        |          t          j        |          t          j        |          dz  }dd|iz  }t          t          j
        |                    d x}x}x}}d S )Nru   z-100r)  250r   z-160r   r   r|   r   r   r   )mwr_multiple_solutionsry   r   r   r   r   r   r   s	            r(   'test_mwr_rejects_ambiguous_sign_patternrl    s   |WV__5genn7UXdfmntfufuWvwxxF<4<4<466<4>@>!@%@@!%@@@@@@@@@@@!%@@@@@@@@@@@@@6@@@@@6@@@@@@>@@@@!@@@@%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@r*   c            	        t                      } t          | ddddd           t          | dddd	d
d           t          j        t          d          5  |                     d           d d d            n# 1 swxY w Y   t          | dddd           t          | ddd          }t          | ddt                    }|d         d         }d}||k    }|slt          j	        d|fd||f          t          j
        |          t          j
        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|d         d         }d}||k    }|slt          j	        d|fd||f          t          j
        |          t          j
        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|d         }|d         }||k    }|slt          j	        d|fd||f          t          j
        |          t          j
        |          dz  }dd|iz  }t          t          j        |                    d x}x}}d S ) Noldru   rY   r-   z2025-01-02T00:00:00+00:00)r2   r3   new10100r   z2025-02-01T00:00:00+00:00)r2   r3   r4   	immutable)matchzSUPDATE portfolio_valuation_snapshots SET value_original='1' WHERE snapshot_id='old'closer#  r\   z2025-01-03T00:00:00+00:00)r3   z2025-01-31T23:59:59+00:00r   r   r  ry   r   r   r   r   r  )!=)z%(py1)s != %(py4)s)r)   rC   pytestraises	Exceptionr$   r   r%   r   r   r   r   r   )r'   earlylater   r   r   r   r   s           r(   <test_snapshot_versions_are_immutable_and_cutoff_reproduciblerz    s=   44D$|WaMhiiii$|WaMhuz{{{{	y	4	4	4 l ljkkkl l l l l l l l l l l l l l l$wD_````'l  iD  E  E  EE&t|\gjkkkDO,77,7777777777,77777,7777777777777777777777777777777	??+6w6+w6666666666+w66666+6666w666666666666666666666666666$%B.A)BB%)BBBBBBBBBBB%)BBBBBB%BBBB)BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBs   A77A;>A;c            	     p   t                      } t          | ddd           t          | ddd           t          | dddd	d
           t          | dddd
dd           t          | dddd
dd           t          | dddd
dd           t          | ddt                    }|d         d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          d z  }d!d"|iz  }t          t          j	        |                    d x}x}}|d         d#         }d$}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          d z  }d!d"|iz  }t          t          j	        |                    d x}x}}d S )%Nr   ru   rY   rs  r#  r\   zinstrument-close780ra   rb   rc   zbuy-oldrh   
2024-01-01ri   rj   )rF   rG   rH   zsell-oldrl   r<  ro   240zsell-periodr)  260r   r   r   60ry   r   r   r   r   r   r   r5  r   s          r(   Gtest_realized_pnl_is_period_specific_while_remaining_basis_is_as_of_endr    s'   44D$g666$w777$*L%Lcfggggy%SSW_effffz6<sUX`effff}fl#X[chiiii(|ilmmmF)^,44,4444444444,44444,4444444444444444444444444444444)34==4==========4=====4=================================r*   c            	        t                      } t          j        t                    5  t	          | ddd           d d d            n# 1 swxY w Y   t          j        t                    5  t	          | ddddd           d d d            n# 1 swxY w Y   t          j        t                    5  |                     d	t          f           d d d            d S # 1 swxY w Y   d S )
Nnegativeru   z-1zbad-fxrE   rE  rD   rF  ay  
            INSERT INTO portfolio_valuation_snapshots(
              snapshot_id,scope_kind,scope_id,account_id,value_original,currency,base_currency,
              fx_direction,valuation_at,source,captured_at,snapshot_version,reason_codes_json
            ) VALUES ('bad-json','account','a','a','1','CHF','CHF','original_to_base','2025-01-01','fixture',?,1,'{}')
            )r)   ru  rv  rw  rC   r$   r%   r&   s    r(   Dtest_snapshot_constraints_reject_invalid_decimal_fx_and_quality_jsonr    s   44D	y	!	! < <dJd;;;< < < < < < < < < < < < < < <	y	!	! Q QdHlC%CPPPPQ Q Q Q Q Q Q Q Q Q Q Q Q Q Q	y	!	! 	
 	
 F	
 	
 	
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
s5   AAA+BBB1CC"Cc                 4'   t                      t                     t          d          } fd| j        t          <   t          |           }j        }|                    d          }|j        }d}||k    }|st          j
        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          d	z  }d
d|iz  }t          t          j        |                    d x}x}}|                                d         d         }	d}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}d |                                d         D             }	g d}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}j        }||k    }|st          j
        d|fd||f          dt          j                    v st          j                  rt          j                  ndt          j        |          dt          j                    v st          j        |          rt          j        |          nddz  }dd|iz  }t          t          j        |                    d x}}|                    d          }|j        }d}||k    }|st          j
        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          d	z  }d
d|iz  }t          t          j        |                    d x}x}}|                                d         }	d}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|                    d          }|j        }d}||k    }|st          j
        d|fd||f          d t          j                    v st          j        |          rt          j        |          nd t          j        |          t          j        |          d	z  }d
d|iz  }t          t          j        |                    d x}x}}|                                d         }	d!}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|                    d"          }|j        }d}||k    }|st          j
        d|fd||f          d#t          j                    v st          j        |          rt          j        |          nd#t          j        |          t          j        |          d	z  }d
d|iz  }t          t          j        |                    d x}x}}|                                d         }	d$}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|                                 d%         d&         }t'          |          }
d'h}|
|k    }|st          j
        d|fd(|
|f          d)t          j                    v st          j        t&                    rt          j        t&                    nd)d*t          j                    v st          j        |          rt          j        |          nd*t          j        |
          t          j        |          d+z  }d,d-|iz  }t          t          j        |                    d x}
x}}d.}	|d'         d/         d0         d1         }|	|v }
|
slt          j
        d2|
fd3|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|d'         d/         d4         d5         d6         d7         }|d8         }	|	j        }
d9} |
|          }|ssd:t          j        |	          t          j        |
          t          j        |          t          j        |          d;z  }t          t          j        |                    d x}	x}
x}}|                                 d<         d=         d>         }|d?         }t'          |          }d@dAh}||k    }|st          j
        d|fdB||f          d)t          j                    v st          j        t&                    rt          j        t&                    nd)t          j        |          t          j        |          t          j        |          dCz  }dDdE|iz  }t          t          j        |                    d x}x}x}}|                                 d%         dF         d'         d/         d4         d5         d6         d7         }|d8         }	|	j        }
dG} |
|          }|ssd:t          j        |	          t          j        |
          t          j        |          t          j        |          d;z  }t          t          j        |                    d x}	x}
x}}j        }|                    dH          }|j        }d}||k    }|st          j
        d|fd||f          dIt          j                    v st          j        |          rt          j        |          ndIt          j        |          t          j        |          d	z  }d
d|iz  }t          t          j        |                    d x}x}}j        }||k    }|st          j
        d|fd||f          dt          j                    v st          j                  rt          j                  ndt          j        |          dJt          j                    v st          j        |          rt          j        |          ndJdz  }dd|iz  }t          t          j        |                    d x}}|                                }|dK         }	g dL}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}dM |dN         D             }	h dO}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}dP |dN         D             }|dQ         dR         }	dS}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dQ         dT         }	dS}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dQ         dU         }	dV}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dQ         dW         }	dS}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dQ         dX         }	dS}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dY         dR         }	dZ}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dY         d[         }	d\g}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}d]}	|j        }|	|v}
|
st          j
        d^|
fd_|	|f          t          j        |	          dIt          j                    v st          j        |          rt          j        |          ndIt          j        |          d`z  }d
d|iz  }t          t          j        |                    d x}	x}
}d S )aNtest)
write_modec                      S )Nr   r&   s   r(   <lambda>zRtest_api_is_read_only_fail_closed_and_openapi_documents_contract.<locals>.<lambda>  s    t r*   zr/api/portfolio/performance?from=2025-01-01&to=2025-03-01&method=both&data_cutoff=2026-07-23T12%3A00%3A00%2B00%3A00   ry   )z3%(py2)s
{%(py2)s = %(py0)s.status_code
} == %(py5)sresponser   r   r   r   r  rx   r   r   r   r   c                    g | ]
}|d          S )r:   r   r   s     r(   r   zTtest_api_is_read_only_fail_closed_and_openapi_documents_contract.<locals>.<listcomp>  s    IIIuE'NIIIr*   ttwror_series)rD   r   rx   )z5%(py2)s
{%(py2)s = %(py0)s.total_changes
} == %(py4)sr'   beforer~   r   r   z8/api/portfolio/performance?from=2025-03-01&to=2025-01-01i  invaliddetailz%Zeitraum: 'von' muss vor 'bis' liegenzH/api/portfolio/performance?from=2025-01-01&to=2025-03-01&method=estimateinvalid_methodz$Methode muss twr, mwr oder both seinzJ/api/portfolio/performance?from=2025-01-01&to=2025-03-01&base_currency=GBPinvalid_currencyu)   Basiswährung muss CHF, EUR oder USD seinpathsz/api/portfolio/performanceget)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)src  path)r~   r   r   r   zassert %(py8)sr   u    Ungültiger Performance-Zeitraum	responses400descriptionr1  r3  r   contentzapplication/jsonschemaz$refz/PortfolioPerformanceResponsezJassert %(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.endswith
}(%(py5)s)
})r   r   r   r   
componentsschemasPerformancePeriod
propertiesfromtor  r   r   r   z/api/portfolio/wealth-cockpitz/WealthCockpitResponsez#/api/portfolio/performance/coveragecoverage_responsebefore_coverageexpected_scopes)postfinance
truewealthcryptoc                    h | ]
}|d          S scoper   r   rows     r(   	<setcomp>zStest_api_is_read_only_fail_closed_and_openapi_documents_contract.<locals>.<setcomp>#  s    ===SCL===r*   rows>   r  	portfolior  r  c                     i | ]}|d          |S r  r   r  s     r(   
<dictcomp>zTtest_api_is_read_only_fail_closed_and_openapi_documents_contract.<locals>.<dictcomp>$  s    BBB#CL#BBBr*   r  ttwror_statusr   xirr_statusvaluation_dates   scope_classification_statuscashflow_coverage_statusr  r%  r0  "required_scope_coverage_incompleter   )not in)z0%(py1)s not in %(py5)s
{%(py5)s = %(py3)s.text
})r   r   r   )r)   rs   r   dependency_overridesr   r   total_changesr  status_coder   r   r   r   r   r   r   r   rA   openapirc  endswithtext)appclientr  r  r   r   r   r   r   r   r   r   r   r  r  r  r  r   r   response_schemar   period_schemar   wealth_schemar  r  coverage_payloadr  r'   s                               @r(   @test_api_is_read_only_fail_closed_and_openapi_documents_contractr    s   44D$

'
'
'C'3|||CV$__FFzz  O  P  PH&3&3&&&&&&&&&&3&&&&&&&&&&&&8&&&&&8&&&&&&&&&&3&&&&&&&&&&&&&&&&&&&&&&&&&&&==??9%e,66,6666666666,66666,6666666666666666666666666666666II(HIIIaMaMaMaaIMaaaaaaaaaaaIMaaaaaaIaaaaMaaaaaaaaaaaaaaaaaaaaaaaaaaaa'''''''''''''''''''''''4'''''4'''''''''''''''''''''''''''''''''''''''''''''''''jjSTTG%#%#%%%%%%%%%%#%%%%%%%%%%%%7%%%%%7%%%%%%%%%%#%%%%%%%%%%%%%%%%%%%%%%%%%%%<<>>(#N'NN#'NNNNNNNNNNN#'NNNNNN#NNNN'NNNNNNNNNNNNNNNNNNNNNNNNNNNNZZ jkkN%,,%,,,,,,,,,,%,,,,,,,,,,,,>,,,,,>,,,,,,%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  *T.TT*.TTTTTTTTTTT*.TTTTTT*TTTT.TTTTTTTTTTTTTTTTTTTTTTTTTTTTzz"noo'.3.'3..........'3.......................'....3...........................  ""8,[0[[,0[[[[[[[[[[[,0[[[[[[,[[[[0[[[[[[[[[[[[[[[[[[[[[[[[[[[[;;==!">?Dt999933tt9-_e[1I%1PQ^1__-1___________-1______-____1____________________________5k+.u5i@ASTU]^O6"L"+L,KL++,KLLLLLLLL"LLLL+LLLL,KLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKMM,/	:;NOM\*=3*++=~=+~==========+~============3=====3======*====+====~=============================KKMM'*+JKER*,,46M  C )C*BC))*BCCCCCCCC CCCC)CCCC*BCCCCCCCCCCCCCCCCCCCCCCCCCCCC(O

#HII(/C/(C//////////(C///////////////////////(////C///////////////////////////0000000000000000000000040000040000000000000000000000000000000000000000000000000(--//-.Y2Y2Y2YY.2YYYYYYYYYYY.2YYYYYY.YYYY2YYYYYYYYYYYYYYYYYYYYYYYYYYYY==$4V$<===uAuAuAuu=Auuuuuuuuuuu=Auuuuuu=uuuuAuuuuuuuuuuuuuuuuuuuuuuuuuuuuBB)9&)ABBBD>/*8j8*j8888888888*j88888*8888j888888888888888888888888888>-(6J6(J6666666666(J66666(6666J666666666666666666666666666>+,11,1111111111,11111,1111111111111111111111111111111>78FJF8JFFFFFFFFFF8JFFFFF8FFFFJFFFFFFFFFFFFFFFFFFFFFFFFFFF>45CC5CCCCCCCCCC5CCCCC5CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC_->>->>>>>>>>>>->>>>>->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>^,V1U0VV,0VVVVVVVVVVV,0VVVVVV,VVVV0VVVVVVVVVVVVVVVVVVVVVVVVVVVV5055<55555555555<555555<555555555550555550555555555555555555555555555555555555r*   c                    t                      } t          |            t          | dd          }|d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }d	d
|iz  }t          t          j        |                    d x}x}}|d         }t          |t                    }|sddt          j                    v st          j        t                    rt          j        t                    nddt          j                    v st          j        |          rt          j        |          nddt          j                    v st          j        t                    rt          j        t                    ndt          j        |          dz  }t          t          j        |                    d }d |D             }t          |          }|sddt          j                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }t          t          j        |                    d x}}d |D             }t          |          }|sddt          j                    v st          j        t                    rt          j        t                    ndt          j        |          t          j        |          dz  }t          t          j        |                    d x}}d S )Nr   z
2026-08-02r   r   r"   r%  ry   r   r   r   r   r  5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancecoverage_rowslistr~   r   r   r   c              3  .   K   | ]}|d          dk    V  dS )r  r%  Nr   r  s     r(   	<genexpr>zXtest_coverage_is_unavailable_when_every_required_scope_is_unavailable.<locals>.<genexpr><  s,      NNs?#}4NNNNNNr*   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}allr  c              3  *   K   | ]}|d          dv V  dS )r  >   rf  r%  Nr   r  s     r(   r  zXtest_coverage_is_unavailable_when_every_required_scope_is_unavailable.<locals>.<genexpr>=  sB         	&'+EE     r*   )r   r   r   r   r   r   r   r   r  r  r   r   r   r  )	r'   coverager   r   r   r   r   r  r   s	            r(   Etest_coverage_is_unavailable_when_every_required_scope_is_unavailabler  /  s   DT)  H H................................................V$MmT***************:*****:*************m*****m*************T*****T************************NNNNNN3NNNNNNNNNNNNNNNN3NNNNN3NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN     3                                              r*   c                 &   t                      } t          | ddd           t          | ddd           t          | dd	          }|d
         }t          |t                    }|sddt          j                    v st          j        t                    rt          j	        t                    nddt          j                    v st          j        |          rt          j	        |          nddt          j                    v st          j        t                    rt          j	        t                    ndt          j	        |          dz  }t          t          j        |                    d }d |D             }|d         d         }d}||k    }|slt          j        d|fd||f          t          j	        |          t          j	        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|d         d         }d}||k    }|slt          j        d|fd||f          t          j	        |          t          j	        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|d         d         }d}||k    }|slt          j        d|fd||f          t          j	        |          t          j	        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|d         d         }	t          |	          }ddh}
||
k    }|st          j        d|fd||
f          d t          j                    v st          j        t                    rt          j	        t                    nd t          j	        |	          t          j	        |          t          j	        |
          d!z  }d"d#|iz  }t          t          j        |                    d x}	x}x}}
d S )$Nzold-openr}  rY   z	old-closez
2024-12-31r\   ru   r#  r  r  r  r  r  r  r  c                     i | ]}|d          |S r  r   r  s     r(   r  zTtest_coverage_evaluates_the_requested_period_not_lifetime_bounds.<locals>.<dictcomp>O  s    777#CL#777r*   r  valuation_fromry   r   r   r   r   valuation_tor  r%  r0  r^  r_  r`  rb  rc  r   r   r   )r)   rC   r   r  r  r   r   r   r   r   r   r   r   rc  )r'   r  r  r   r   r  r   r   r   r   r   r   r   r   s                 r(   @test_coverage_evaluates_the_requested_period_not_lifetime_boundsr  C  s$   44D$
L':::$\7;;;)  H
 V$MmT***************:*****:*************m*****m*************T*****T************************77777D>*+;|;+|;;;;;;;;;;+|;;;;;+;;;;|;;;;;;;;;;;;;;;;;;;;;;;;;;;>.)9\9)\9999999999)\99999)9999\999999999999999999999999999>/*;m;*m;;;;;;;;;;*m;;;;;*;;;;m;;;;;;;;;;;;;;;;;;;;;;;;;;;H~n- 3-.. ##3 . 3        . 3                     .    /   3                             r*   c                    t                      } |                     dt          f           t          |            |                     d                                          d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd	|iz  }t          t          j	        |                    d x}x}}|                     d
                                          d         }|t          k    }|st          j        d|fd|t          f          t          j        |          dt          j                    v st          j        t                    rt          j        t                    nddz  }dd|iz  }t          t          j	        |                    d x}}|                     d                                          d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd	|iz  }t          t          j	        |                    d x}x}}d S )Na  
        INSERT INTO portfolio_policies(
          policy_id,version,is_active,effective_from,base_currency,benchmarks_json,
          restrictions_json,request_fingerprint,audit_id,created_at
        ) VALUES ('policy',1,1,'2025-01-01','CHF','[]','[]','request','audit',?)
        z(SELECT policy_id FROM portfolio_policiesr   policyry   r   r   r   r   z*SELECT MAX(version) FROM schema_migrations)z%(py1)s == %(py3)sr   )r   r   r   r   z2SELECT COUNT(*) FROM portfolio_valuation_snapshots)r)   r$   r%   r   fetchoner   r   r   r   r   r   r   r   r   )r'   r   r   r   r   r   r  r   s           r(   7test_migration_is_additive_and_preserves_sprint_41_rowsr  Z  s   44DLL	 
   T<<BCCLLNNqQ]U]]QU]]]]]]]]]]]QU]]]]]]Q]]]]U]]]]]]]]]]]]]]]]]]]]]]]]]]]]<<DEENNPPQRShSWhhhhhhhhhhhSWhhhhhhShhhhhhhhhhhWhhhhhhWhhhhhhhhhhhhhhhhhhhhhhhhhhhh<<LMMVVXXYZ[`_``[_```````````[_``````[````_``````````````````````````````r*   )r7   r8   r9   r8   r:   r8   r.   r8   r/   r;   r0   r8   r1   r8   r   r8   r2   r<   r3   r8   r4   r;   r5   r8   r6   r=   )rN   r8   rO   r8   r9   r8   rF   r;   rG   r;   rH   r;   rI   r8   rJ   r8   rK   r;   r.   r8   r/   r;   rL   r;   rM   r;   )E
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   decimalr   rA   ru  fastapi.testclientr   jarvis_finance.api.dependenciesr   jarvis_finance.api.mainr    jarvis_finance.ledger.cost_basisr   !jarvis_finance.ledger.performancer	   r
   r   r   -jarvis_finance.services.portfolio_performancer   r   )jarvis_finance.services.performance_scoper   r   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r%   r)   rC   rV   rs   r   r   r   r   r   r   r   r  r  r!  r&  r6  r:  r>  rC  rH  rU  r[  rd  rh  rl  rz  r  r  r  r  r  r  r   r*   r(   <module>r     s   " " " " " "                       ) ) ) ) ) ) 2 2 2 2 2 2 . . . . . . 4 4 4 4 4 4 ] ] ] ] ] ] ] ] ] ] ] ]               ; : : : : : Q Q Q Q Q Q Q Q!" " "V !$ $, , , , , ,j !%"!%7 7 7 7 7 7tt t t/ / /E E E* * *a a a$f f f
F 
F 
F	1 	1 	1  0 0 0$1 $1 $1NF F F] ] ](\ \ \>= = =(> > >&D D DS S S> > >w w wK K KA A AC C C
> 
> 
>
 
 
$/6 /6 /6d  (  .a a a a ar*   