
    mj]Z                       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 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
dOd&Z%dddd'd'ddd(ddd)
dPd4Z&d5 Z'd6 Z(d7 Z)d8 Z*d9 Z+d: Z,d; Z-d< Z.d= Z/d> Z0d? Z1d@ Z2dA Z3dB Z4dC Z5dD Z6dE Z7dF Z8dG Z9dH Z:dI Z;dJ Z<dK Z=dL Z>dM Z?dN Z@dS )Q    )annotationsN)Decimal)
TestClient)get_db)
create_app)fifo_v1)Activityeffective_activitiestwr_v1xirr_v1)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 | Noner/   r0   r   r1   intr2   r3   r4   r5   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&   r6   r8   r9   r-   r.   r/   r0   r   r1   r2   r3   r4   r5   s                 r'   add_valuationrB   =   sy    " 	LL	  ;  Jw}"%%#	
    r)   01)
instrument_idquantitygrossfeetaxnetr-   r.   reversal_oftransfer_groupactivity_idkindrE   rF   rG   rH   rI   rJ   rK   rL   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&   rM   rN   r8   rE   rF   rG   rH   rI   rJ   r-   r.   rK   rL   s                 r'   add_activityrU   l   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r/   r0   depositexternal_deposit5000rJ   buyz2025-01-05T00:00:001010005)rE   rF   rG   rH   sellz2025-02-10T00:00:00448023)rE   rF   rG   rH   rI   transferinternal_transferz2025-02-15T00:00:00zpair-1)rJ   rL   )rB   rU   r%   s    r'   add_control_portfoliorr      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-01rX   
2025-02-01r[   
2025-03-01r]   re   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   r9   
@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 )Nrt   rX   rv   r]   ru   re   isz-%(py2)s
{%(py2)s = %(py0)s.value
} is %(py5)sr{   r}   r~   py5assert %(py7)sr   )missing_cashflow_valuationrx   zL%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.quality
}.reasons
} == %(py7)sr   r   r   )r   r   r9   r   r   r   r   r   r   r   r   r   r5   	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.1rx   rz   r{   r   r|   r   r   )r   r   r9   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          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 )Nrt   z-1000
2026-01-011100z0.100000000000rx   rz   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   r9   r   r   r   r   r   r   r   r   r   r5   )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 )Nrg   rh   ri   rj   rC   rM   rN   rF   rG   rH   rI   rk   rl   rm   rn   ro   6rx   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)
})py1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   @py_assert0@py_assert2r   @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b1rg   rn   200rC   r   b2ro   360srk   rl   600c                *    g | ]}|j         |j        fS  )lot_activity_idrF   ).0items     r'   
<listcomp>zBtest_fifo_sale_consumes_multiple_lots_in_order.<locals>.<listcomp>   s"    OOOdT!4=1OOOr)   rx   z%(py1)s == %(py4)sr   r   assert %(py6)sr   rD   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   @py_format5@py_format7r   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xrg   r   rt   r*   rD   r   )rF   rG   rreversalz
2025-01-02)rK   rx   z%(py0)s == %(py3)s	effectiver}   r   assert %(py5)sr   r   r5   r	   r   r
   r   r   r   r   r   r   r   r   )originalr   r   r5   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   rt   r*   r   F)rG   	supportedrx   r   r   r   r   r   )unsupported_activityr5   r   )r   r   r5   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 )/Nrt   rv   	from_dateto_datedata_cutoffrx   )z%(py0)s == %(py2)sfirstsecond)r}   r~   zassert %(py4)sr   summaryrX   r]   re   7ro   rw   xirr_annualized2600mwrr   117r   )opening_valueclosing_valuenet_external_cashflowsfeestaxesttwror_cumulative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_cashflowsrZ   rd   )r8   rN   amountinput_fingerprint@   z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slenr   r   r   )r(   rr   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 )NrW   rt   rX   rY   
2025-12-31r[   r   r   r	  r   rx   r   r   r   r   r   r   z%(py1)s is %(py4)sr   r  r!   unavailable	r(   rB   r   r$   r   r   r   r   r   r&   r{   r   r   r   r   r   s          r'   >test_total_value_account_has_performance_without_invented_lotsr     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 )NrW   rt   rX   rY   r  r[   zunknown-flowunclassified_cash_movement
2025-06-01500rf   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(   rB   rU   r   r$   r   r   r   r   r   r  s          r'   Ftest_unclassified_period_activity_blocks_both_ttwror_and_personal_xirrr+  *  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   rt   r  r  synthetic_missing_historyz%No complete external cashflow historyz2026-07-23T12:01:00+00:00r   rW   rX   rY   r[   r   r!  r   r  r   r  r   r   r   r   r   r$  r%  !external_cashflow_history_missingrx   r   r)  r&  r(  )
r(   r   rB   r   r$   r   r   r   r   r   r  s          r'   Qtest_missing_cashflow_history_evidence_blocks_returns_even_without_recorded_flowsr/  >  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 ) NrW   rt   rX   rY   r  r[   zold-unknownr  
2024-06-01r  rf   zperiod-dividenddividendr  r   )rG   rJ   r   r!  r   r  r   rx   r   r   r   r   r   r$  r!   r   r)  r*  r  s          r'   Ktest_historical_unsupported_activity_does_not_poison_later_period_cashflowsr3  ]  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   rt   rX   r*   manual_total_valuerQ   z2025-01-02T00:00:00Z)zlegacy-closer   r  r[   r*   r5  rQ   z2026-01-01T00:00:00Zrt   r  r   r   r	  r   rx   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_onlyr8  q  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 )NrW   rt   rX   EURr-   r.   rY   r  r[   r   r   r	  r   r  r   r   r   
missing_fxr   r%  r&  r(  r  r  s          r'   =test_missing_foreign_fx_never_becomes_one_or_zero_performancer=    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 )NrW   rt   rX   r:  z0.9r;  rY   r  r[   z1.0r   r   r  z9000.0rx   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))
}r7   r   )
r   r   r   r   r   r   r   r   py16py19zassert %(py21)spy21)r(   rB   r   r$   r   r   r   r   r   r   r7   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_snapshotsrJ    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 )NrW   rt   rX   rY   r  10070divr2  r  ra   r   3070)rE   rG   rI   rJ   r   r  rx   r   r   r   r   r   r  r  rC   r*  r  s          r'   Btest_dividend_withholding_and_net_credit_are_not_external_cashflowrP    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  rX   rt   r  r   r   r	  r   r  r   r   r   r   r   r%  missing_opening_valuationmissing_closing_valuation)>=)z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} >= %(py7)ssetr   r   r   )r(   rB   r   r$   r   r   r   r   r   rV  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_returnsrW    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 )NrW   rt   rX   rY   r  r[   partialstale_valuation)r4   r5   r   r   r	  r   rx   r   r   r   r   r   r!   r%  r  r  s          r'   >test_stale_snapshot_keeps_calculation_but_marks_metric_partialr[    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 )Nrt   z-100r  250r   z-160r   r   r{   r   r   r   )mwr_multiple_solutionsrx   r   r   r   r   r   r   s	            r'   'test_mwr_rejects_ambiguous_sign_patternr_    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oldrt   rX   r,   z2025-01-02T00:00:00+00:00)r1   r2   new10100   z2025-02-01T00:00:00+00:00)r1   r2   r3   	immutable)matchzSUPDATE portfolio_valuation_snapshots SET value_original='1' WHERE snapshot_id='old'closer  r[   z2025-01-03T00:00:00+00:00)r2   z2025-01-31T23:59:59+00:00r   r   r  rx   r   r   r   r   r  )!=)z%(py1)s != %(py4)s)r(   rB   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_reproduciblern    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 )%Nopenrt   rX   rg  r  r[   zinstrument-close780r`   ra   rb   zbuy-oldrg   z
2024-01-01rh   ri   )rE   rF   rG   zsell-oldrk   r1  rn   240zsell-periodr  260r   r   r   60rx   r   r   r   r   r   r   r*  r  s          r'   Gtest_realized_pnl_is_period_specific_while_remaining_basis_is_as_of_endru    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negativert   z-1zbad-fxrD   r:  rC   r;  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(   ri  rj  rk  rB   r#   r$   r%   s    r'   Dtest_snapshot_constraints_reject_invalid_decimal_fx_and_quality_jsonrx    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                 R#   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}}j        }|                    dF          }|j        }d}||k    }|st          j
        d|fd||f          dGt          j                    v st          j        |          rt          j        |          ndGt          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        |          dHt          j                    v st          j        |          rt          j        |          ndHdz  }dd|iz  }t          t          j        |                    d x}}|                                }|dI         }	g dJ}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}dK |dL         D             }	h dM}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}dN |dL         D             }|dO         dP         }	dQ}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dO         dR         }	dQ}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dO         dS         }	dT}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dU         dP         }	dV}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}|dU         dW         }	dXg}|	|k    }
|
slt          j
        d|
fd|	|f          t          j        |	          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}
}dY}	|j        }|	|v}
|
st          j
        dZ|
fd[|	|f          t          j        |	          dGt          j                    v st          j        |          rt          j        |          ndGt          j        |          d\z  }d
d|iz  }t          t          j        |                    d x}	x}
}d S )]N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   rx   )z3%(py2)s
{%(py2)s = %(py0)s.status_code
} == %(py5)sresponser   r   r   r   r	  rw   r   r   r   r   c                    g | ]
}|d          S )r9   r   )r   points     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)rC   r   rw   )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)srV  path)r}   r   r   r   zassert %(py8)sr   u    Ungültiger Performance-Zeitraum	responses400descriptionr&  r(  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/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   r  r  r%  "required_scope_coverage_incompleter   )not in)z0%(py1)s not in %(py5)s
{%(py5)s = %(py3)s.text
})r   r   r   )r(   rr   r   dependency_overridesr   r   total_changesr  status_coder   r   r   r   r   r   r   r   r@   openapirV  endswithtext)appclientr  r  r   r   r   r   r   r   r   r   r   r  r  r  r  r   r   response_schemar   period_schemar   r  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======*====+====~=============================(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_->>->>>>>>>>>>->>>>>->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>^,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                      } |                     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   policyrx   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    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)   )r6   r7   r8   r7   r9   r7   r-   r7   r.   r:   r/   r7   r0   r7   r   r7   r1   r;   r2   r7   r3   r:   r4   r7   r5   r<   )rM   r7   rN   r7   r8   r7   rE   r:   rF   r:   rG   r:   rH   r7   rI   r7   rJ   r:   r-   r7   r.   r:   rK   r:   rL   r:   )A
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   decimalr   r@   ri  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   )jarvis_finance.services.performance_scoper   r   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r$   r(   rB   rU   rr   r   r   r   r   r   r   r   r   r  r  r+  r/  r3  r8  r=  rJ  rP  rW  r[  r_  rn  ru  rx  r  r  r   r)   r'   <module>r     sy   " " " " " "                       ) ) ) ) ) ) 2 2 2 2 2 2 . . . . . . 4 4 4 4 4 4 ] ] ] ] ] ] ] ] ] ] ] ] U U U U U U        ; : : : : : Q Q Q Q Q Q Q Q!" " "V !$ $, , , , , ,j !%"!%7 7 7 7 7 7tt t t/ / /E E E* * *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 )6Xa a a a ar)   