
    pj                         d dl mZ d dlZd dlmc m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 d dlmZ d d	lmZ d d
lmZ  G d d          Zd ZddZddZddZddZddZddZ ddZ!dS )    )annotationsN)Decimal)add_equity_initial_snapshot)resolve_fx_rate_to_chf)store_market_price)_position_statuslist_equity_positions)refresh_equity_fx)connect_memory)apply_migrationsc                  "    e Zd Zddd
ZdddZdS )ProviderNnamestrrateDecimal | NoneerrorException | NonereturnNonec                >    || _         || _        || _        g | _        d S N)r   r   r   calls)selfr   r   r   s       g/home/agent/.hermes/worktrees/FinanceManager-sprint20d/tests/unit/test_fx_frankfurter_primary_hotfix.py__init__zProvider.__init__   s"    		
8:


    base_currencyquote_currency	rate_date
str | Nonec                f    | j                             |||f           | j        r| j        | j        S r   )r   appendr   r   )r   r   r   r    s       r   get_ratezProvider.get_rate   s9    
=.)DEEE: 	*yr   )NN)r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   )__name__
__module____qualname__r   r$    r   r   r   r      sF        ; ; ; ; ;      r   r   c                 @    t                      } t          |            | S r   )r   r   conns    r   
setup_connr-      s     DTKr   r   r   c                    |                      d           |                      d           |                                  dS )NzINSERT INTO platforms(platform_id, name, platform_type, country, default_currency, created_at) VALUES ('p1', 'PostFinance', 'broker', 'CH', 'CHF', '2026-01-01')zINSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, created_at) VALUES ('a1', 'p1', 'Depot', 'brokerage', 'CHF', '2026-01-01')a1)executecommitr+   s    r   seed_accountr2   "   sR    LL  t  u  u  uLL  r  s  s  sKKMMM4r   r   c                    t                      } |                     d           t          dt          d                    }t          dt          d                    }t	          | dd||g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}}|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}}|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g}||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}}|j        }g }||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}||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 ) NzINSERT INTO fx_rates(fx_rate_id, base_currency, quote_currency, rate_date, rate, provider, rate_type, quality_status, created_at) VALUES ('old', 'EUR', 'CHF', '2026-05-19', '0.9000', 'cache_fixture', 'close', 'fresh', '2026-05-19')frankfurterz0.9700
twelvedataz0.9600EUR
2026-05-20Tr   r    	providerspersistok==z.%(py2)s
{%(py2)s = %(py0)s.status
} == %(py5)sresultpy0py2py5assert %(py7)spy7z.%(py2)s
{%(py2)s = %(py0)s.source
} == %(py5)szJ%(py2)s
{%(py2)s = %(py0)s.rate
} == %(py8)s
{%(py8)s = %(py4)s(%(py6)s)
}r   rA   rB   py4py6py8assert %(py10)spy10)r6   CHFr7   z-%(py2)s
{%(py2)s = %(py0)s.calls
} == %(py5)szSELECT rate, provider FROM fx_rates WHERE base_currency='EUR' AND quote_currency='CHF' ORDER BY rate_date DESC, created_at DESC LIMIT 1providerz%(py1)s == %(py4)spy1rI   assert %(py6)srJ   r   )r-   r0   r   r   r   status
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationsourcer   r   fetchone)r,   r4   r5   r?   @py_assert1@py_assert4@py_assert3@py_format6@py_format8@py_assert5@py_assert7@py_format9@py_format11latest@py_assert0@py_assert2@py_format5@py_format7s                     r   Itest_resolve_fx_uses_frankfurter_before_stale_cache_and_before_twelvedatarn   )   s   <<DLL  {  |  |  |='(*;*;<<K,(9(9::J#Dbmoyaz  EI  J  J  JF= D =D          =D            6     6      =    D                           =)M)=M))))))))))=M))))))))))))6)))))6))))))=))))M)))))))))))))))))))))))))));+(+'(+++;+++++++++++;+++++++++++++6+++++6++++++;+++++++++++'+++++'++++++(++++++++++++++++++++++++++++++++++>!= >> >>>>>>>>>>> >>>>>>>>>>>>>;>>>>>;>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>!r!r!!!!!!!!!!r!!!!!!!!!!!!:!!!!!:!!!!!!!!!!r!!!!!!!!!!!!!!!!!!!!!!!!!!!\\  d  e  e  n  n  p  pF*................................................&>%X%>X%%%%%%%%%%>X%%%%%>%%%%X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%r   c                 
   t                      } |                     d           t          dt          d                    }t          dt          d                    }t	          | dd||g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}}|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}}|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g}||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}}|j        }g }||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}}|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}}d S )NzINSERT INTO fx_rates(fx_rate_id, base_currency, quote_currency, rate_date, rate, provider, rate_type, quality_status, created_at) VALUES ('cached', 'USD', 'CHF', '2026-05-20', '0.8800', 'frankfurter', 'close', 'fresh', '2026-05-20')r4   frankfurter_provider_error)r   r5   twelvedata_provider_errorUSDr7   Tr8   r;   r<   r>   r?   r@   rD   rE   zcache:frankfurterrF   z0.8800rG   r   rH   rL   rM   )rr   rN   r7   rO   )is)z/%(py2)s
{%(py2)s = %(py0)s.warning
} is %(py5)s)r-   r0   r   RuntimeErrorr   rU   rV   rW   rX   rY   rZ   r[   r\   r]   r^   r   r   r   warning)r,   r4   r5   r?   r`   ra   rb   rc   rd   re   rf   rg   rh   s                r   Ltest_resolve_fx_uses_cache_after_frankfurter_failure_before_twelvedata_noiserv   ;   s2   <<DLL  |  }  }  }==Y0Z0Z[[[K,l;V.W.WXXXJ#Dbmoyaz  EI  J  J  JF= D =D          =D            6     6      =    D                           =///=///////////=/////////////6/////6//////=////////////////////////////////;+(+'(+++;+++++++++++;+++++++++++++6+++++6++++++;+++++++++++'+++++'++++++(++++++++++++++++++++++++++++++++++>!= >> >>>>>>>>>>> >>>>>>>>>>>>>;>>>>>;>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>!r!r!!!!!!!!!!r!!!!!!!!!!!!:!!!!!:!!!!!!!!!!r!!!!!!!!!!!!!!!!!!!!!!!!!!!>!T!>T!!!!!!!!!!>T!!!!!!!!!!!!6!!!!!6!!!!!!>!!!!T!!!!!!!!!!!!!!!!!!!!!!!!!!!!!r   c                    t                      } t          | d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}}|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}||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 S )NrN   r7   )r   r    
not_neededr<   r>   r?   r@   rD   rE   1rG   r   rH   rL   rM   rF   )r-   r   rU   rV   rW   rX   rY   rZ   r[   r\   r]   r   r   r^   )r,   r?   r`   ra   rb   rc   rd   re   rf   rg   rh   s              r   /test_chf_resolution_is_not_needed_with_rate_onerz   K   s   <<D#DVVVF=(L(=L((((((((((=L((((((((((((6(((((6((((((=((((L(((((((((((((((((((((((((((;&#&'#,,&;,&&&&&&&&&&;,&&&&&&&&&&&&6&&&&&6&&&&&&;&&&&&&&&&&&'&&&&&'&&&&&&#&&&&,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&=(L(=L((((((((((=L((((((((((((6(((((6((((((=((((L(((((((((((((((((((((((((((((r   c                    t                      }t          |          }t          ||ddddddddd	d
dd          }t          ||j        dt          d          ddddd	  	         |                                  G d d           G d d          |                     dfd           |                     dfd           t          ||j                  }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%         }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(}||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}}|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 S )0Nstock
Apple Inc.US0378331005APCXETRArN   2300
2026-01-01Corez-synthetic Apple CHF cost basis, EUR valuationT
account_idasset_classr   isintickerexchangecurrencyquantity_textcost_basis_original_textsnapshot_datecategorynoteconfirmr7   100r6   fixturezAPC.DEfreshinstrument_id
price_datecloser   rP   provider_symbolprovider_marketquality_statusc                      e Zd ZdZd	d
dZdS )^test_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.Frankfurterr4   Nr   r   r   r    r!   c                    ||f}d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}t          d          S )N)r6   rN   r<   rQ   rR   rT   rJ   z0.95rV   rW   r[   r\   r]   r   	r   r   r   r    rj   rb   rk   rl   rm   s	            r   r$   zgtest_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.Frankfurter.get_ratek       !>2DnD2nDDDDDDDDDD2nDDDDD2DDDDn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6??"r   r   r%   r&   r'   r(   r   r$   r)   r   r   Frankfurterr   i   2        	# 	# 	# 	# 	# 	# 	#r   r   c                      e Zd ZdZd ZdS )]test_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.TwelveDatar5   c                     t          d          )Nz7TwelveData must not be called when Frankfurter succeeds)r\   )r   argskwargss      r   r$   zftest_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.TwelveData.get_rateq   s     !Z[[[r   Nr   r)   r   r   
TwelveDatar   o   s-        	\ 	\ 	\ 	\ 	\r   r   1jarvis_finance.fx.providers.FrankfurterFxProviderc                                   S r   r)   r   s   r   <lambda>z[test_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.<lambda>t       U`U`UbUb r   z0jarvis_finance.fx.providers.TwelveDataFxProviderc                                   S r   r)   )r   s   r   r   z[test_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.<lambda>u   s    T^T^T`T` r   c              3  0   K   | ]}|j         d k    |V  dS r}   Nr   .0rows     r   	<genexpr>z\test_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.<genexpr>y   s.      AAL(@(@(@(@(@(@AAr   updated   )>=)z%(py1)s >= %(py4)srR   rT   rJ   warningsr<   rQ   Bewertetr>   appler@   rD   rE   z190.00z8%(py2)s
{%(py2)s = %(py0)s.market_value_chf
} == %(py5)s)r-   r2   r   r   r   r   r1   setattrr
   r	   nextrV   rW   r[   r\   r]   rU   rX   rY   rZ   market_value_chf)monkeypatchr,   r   snapr?   rowsr   rj   rb   rk   rl   rm   r`   ra   rc   rd   r   r   s                   @@r   Itest_apple_eur_valuation_fx_recheck_gets_chf_market_value_via_frankfurterr   S   s   <<Dd##J&!&"<  D  t4+=,^efk^l^lw|  HQ  ck  }D  U\  ]  ]  ]  ]KKMMM# # # # # # # #\ \ \ \ \ \ \ \
 KMbMbMbMbcccJL`L`L`L`aaatT%788F &&DAAAAAAAE)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*################################################<%:%<:%%%%%%%%%%<:%%%%%%%%%%%%5%%%%%5%%%%%%<%%%%:%%%%%%%%%%%%%%%%%%%%%%%%%%%!-X-!X----------!X------------5-----5------!----X-----------------------------r   c                   t                      }t          |          }t          ||ddddddddd	d
dd          }t          ||j        dt          d          ddddd	  	         |                                  G d d          |                     dfd           t          ||j                  }t          d t          |          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}}|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}}|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 S )'Nr|   r}   r~   AAPLNASDAQrN   r   r   r   r   z-synthetic Apple CHF cost basis, USD valuationTr   r7   200rr   r   r   r   c                      e Zd ZdZd	d
dZdS )^test_apple_usd_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.Frankfurterr4   Nr   r   r   r    r!   c                    ||f}d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}t          d          S )N)rr   rN   r<   rQ   rR   rT   rJ   z0.90r   r   s	            r   r$   zgtest_apple_usd_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.Frankfurter.get_rate   r   r   r   r%   r   r)   r   r   r   r      r   r   r   r   c                                   S r   r)   r   s   r   r   z[test_apple_usd_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.<lambda>   r   r   c              3  0   K   | ]}|j         d k    |V  dS r   r   r   s     r   r   z\test_apple_usd_valuation_fx_recheck_gets_chf_market_value_via_frankfurter.<locals>.<genexpr>   s.      XXsx<?W?W?W?W?W?WXXr   r   r<   rQ   rR   rT   rJ   r   r>   r   r@   rD   rE   z360.00r   )r-   r2   r   r   r   r   r1   r   r
   r   r	   rV   rW   r[   r\   r]   rU   rX   rY   rZ   r   )r   r,   r   r   r?   r   rj   rb   rk   rl   rm   r`   ra   rc   rd   r   s                  @r   Itest_apple_usd_valuation_fx_recheck_gets_chf_market_value_via_frankfurterr      s   <<Dd##J&!&"<  D  t4+=,^efk^l^lw|  HQ  ci  {C  T[  \  \  \  \KKMMM# # # # # # # # KMbMbMbMbccctT%788FXX 5d ; ;XXXXXE*################################################<%:%<:%%%%%%%%%%<:%%%%%%%%%%%%5%%%%%5%%%%%%<%%%%:%%%%%%%%%%%%%%%%%%%%%%%%%%%!-X-!X----------!X------------5-----5------!----X-----------------------------r   c                 f   ddddddd} t          |           }d}||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<   t          |           }d}||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 S )Nr;   
missing_fxz100.00ru   )price_status	fx_statusr   quality_warningsdata_quality_statusvaluation_statusu   Einstand unvollständigr<   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   r   )rA   rS   py3rJ   zassert %(py8)srK    r   u   FX fehlt für Bewertung)	r   rV   rW   rX   rY   rZ   r[   r\   r]   )r   rk   re   ra   rm   rg   s         r   Ktest_valuation_status_does_not_show_fx_missing_when_market_value_chf_existsr      s   !$((% C C  =$== $=========== $===============================C=====C====== ====$============================ CC  =$== $=========== $===============================C=====C====== ====$==============================r   )r   r   )r   r   )"
__future__r   builtinsrX   _pytest.assertion.rewrite	assertionrewriterV   decimalr   jarvis_finance.equity.manager   jarvis_finance.fx.ratesr   !jarvis_finance.market_data.pricesr   &jarvis_finance.services.equity_servicer   r	   &jarvis_finance.services.market_servicer
   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r-   r2   rn   rv   rz   r   r   r   r)   r   r   <module>r      s   " " " " " "                     D D D D D D : : : : : : @ @ @ @ @ @ Z Z Z Z Z Z Z Z D D D D D D : : : : : : > > > > > >            & & & &$" " " " ) ) ) )+. +. +. +.\#. #. #. #.L> > > > > >r   