343 lines
10 KiB
Python
343 lines
10 KiB
Python
import sys
|
|
import time
|
|
from typing import List
|
|
|
|
from tabulate import tabulate
|
|
from . import calc
|
|
from .comparison import clone_context, compare_fair_values
|
|
from .db import default_sql_query
|
|
from .payoffs import PayoffContext
|
|
|
|
|
|
def _prompt_with_default(prompt: str, default: str) -> str:
|
|
value = input(prompt)
|
|
return default if not value else value
|
|
|
|
|
|
def _print_table(title: str, headers: List[str], rows: List[List[object]]) -> None:
|
|
print()
|
|
print(title)
|
|
print(tabulate(rows, headers=headers, tablefmt="github"))
|
|
|
|
|
|
def get_caso_string(airbag: int, sigma: int, relief: int, twinwin: int, onestar: int) -> str:
|
|
componenti = []
|
|
if airbag == 1:
|
|
componenti.append("Airbag")
|
|
if sigma == 1:
|
|
componenti.append("Sigma")
|
|
if relief == 1:
|
|
componenti.append("Relief")
|
|
if twinwin == 1:
|
|
componenti.append("TwinWin")
|
|
if onestar == 1:
|
|
componenti.append("OneStar")
|
|
|
|
return " + ".join(componenti) if componenti else "Standard"
|
|
|
|
|
|
def restart_or_exit():
|
|
print()
|
|
print("Premere Invio per finire o R per riavviare il programma...")
|
|
restart = input()
|
|
if restart and restart.upper() == "R":
|
|
main()
|
|
else:
|
|
sys.exit(0)
|
|
|
|
|
|
def main():
|
|
version = "2.2 - [12/06/2025]"
|
|
print(f"Pricer v.{version}")
|
|
|
|
fs_db = default_sql_query()
|
|
|
|
isin = _prompt_with_default("Inserire ISIN (default IT0006767633): ", "IT0006767633")
|
|
|
|
check_isin = fs_db.check_isin(isin)
|
|
if check_isin is not None:
|
|
check_value = check_isin[0] if check_isin else None
|
|
if check_value == "KO":
|
|
print(f"ISIN {isin} non processabile!")
|
|
print("Certificati nel pricer devono avere le seguenti caratteristiche : ")
|
|
print("- Categoria: Coupon,StepUp,Bonus (con Barriera Discreta)")
|
|
print("- Status: In Quotazione")
|
|
print("- Direction: Long")
|
|
print("- Basket Type : Worst")
|
|
print("- Currency : EUR")
|
|
print("- CedolaVariabile : FALSE")
|
|
print("- Darwin : FALSE")
|
|
print("- Domino : FALSE")
|
|
print("- Magnet : FALSE")
|
|
restart_or_exit()
|
|
|
|
prezzi_eod = _prompt_with_default(
|
|
"Num. Prezzi EOD per calcolo matrice correlazione e volatilita (default 252): ", "252"
|
|
)
|
|
num_prezzi_eod = int(prezzi_eod)
|
|
|
|
corr_matrix = calc.correl_certificates(isin, num_prezzi_eod, True)
|
|
volatility = calc.volatility_certificates(isin, num_prezzi_eod, True)
|
|
|
|
is_dividend = _prompt_with_default("Considerare i dividendi ? s/n (default s): ", "s")
|
|
|
|
num_sims = int(_prompt_with_default("Inserire # simulazioni montecarlo (default = 10000) : ", "10000"))
|
|
t_montecarlo_choice = time.perf_counter()
|
|
|
|
details_ul_model = fs_db.get_details_ul(isin)
|
|
|
|
nomi_sottostanti = [r.sottostante for r in details_ul_model]
|
|
corr_rows = calc.array_to_table(corr_matrix, nomi_sottostanti)
|
|
_print_table(f"Matrix Correl {isin}", nomi_sottostanti, corr_rows)
|
|
|
|
details_ctf_model = fs_db.get_details_ctf(isin)
|
|
details_event_model = fs_db.get_details_event(isin)
|
|
details_event_exdate_model = fs_db.get_details_event_exdate(isin)
|
|
|
|
ctf = details_ctf_model[0]
|
|
tasso_interesse = ctf.tasso_interesse
|
|
days_to_maturity = ctf.days_to_maturity
|
|
pdi_style = ctf.pdi_style
|
|
pdi_strike = ctf.pdi_strike
|
|
pdi_barrier = round(ctf.pdi_barrier, 4)
|
|
capital_value = ctf.capital_value
|
|
nominal_amount = ctf.nominal_amount
|
|
bid = ctf.bid
|
|
ask = ctf.ask
|
|
last_date_price = ctf.last_date_price
|
|
coupon_in_memory = ctf.coupon_in_memory
|
|
prot_min_val = ctf.prot_min_val
|
|
airbag = ctf.air_bag
|
|
fattore_airbag = ctf.fattore_airbag
|
|
one_star = ctf.one_star
|
|
trigger_onestar = round(ctf.trigger_onestar, 4)
|
|
twin_win = ctf.twin_win
|
|
sigma = ctf.sigma
|
|
relief = ctf.relief
|
|
domino = ctf.domino
|
|
category = ctf.category
|
|
cap = ctf.cap
|
|
leva = ctf.leva
|
|
|
|
prices_ul = [r.spot_price_normalized for r in details_ul_model]
|
|
dividends = [r.dividend for r in details_ul_model]
|
|
if is_dividend != "s":
|
|
dividends = [0.0 for _ in dividends]
|
|
num_sottostanti = len(details_ul_model)
|
|
|
|
days_to_obs = [r.days_to_obs for r in details_event_model]
|
|
days_to_ex_date = [r.days_to_ex_date for r in details_event_exdate_model]
|
|
days_to_obs_y_fract = [r.days_to_obs_y_fract for r in details_event_model]
|
|
coupon_values = [r.coupon_value for r in details_event_model]
|
|
coupon_triggers = [r.coupon_trigger for r in details_event_model]
|
|
autocall_values = [r.autocall_value for r in details_event_model]
|
|
autocall_triggers = [r.autocall_trigger for r in details_event_model]
|
|
memory_flags = [r.memory for r in details_event_model]
|
|
num_eventi = len(details_event_model)
|
|
|
|
input_ul_rows = []
|
|
for i in range(num_sottostanti):
|
|
input_ul_rows.append(
|
|
[
|
|
nomi_sottostanti[i],
|
|
round(prices_ul[i], 4),
|
|
round(dividends[i], 4),
|
|
round(volatility[i], 4),
|
|
]
|
|
)
|
|
_print_table(
|
|
f"Dati Input Sottostanti {isin}",
|
|
["Sottostante", "Price", "Dividend", "Volatility"],
|
|
input_ul_rows,
|
|
)
|
|
|
|
input_ctf_rows = [
|
|
[
|
|
round(tasso_interesse, 4),
|
|
days_to_maturity,
|
|
pdi_style,
|
|
pdi_barrier,
|
|
round(capital_value * nominal_amount, 5),
|
|
round(coupon_in_memory, 5),
|
|
round(prot_min_val, 3),
|
|
]
|
|
]
|
|
_print_table(
|
|
f"Dati Input Certificato {isin}",
|
|
[
|
|
"TassoInteresse",
|
|
"DaysToMaturity",
|
|
"PDI Style",
|
|
"PDI Barrier",
|
|
"Capital Value",
|
|
"Coupon In Memoria",
|
|
"Prot Min Val %",
|
|
],
|
|
input_ctf_rows,
|
|
)
|
|
|
|
input_flag_rows = [
|
|
[
|
|
airbag,
|
|
round(fattore_airbag, 3),
|
|
one_star,
|
|
trigger_onestar,
|
|
twin_win,
|
|
sigma,
|
|
relief,
|
|
]
|
|
]
|
|
_print_table(
|
|
f"Dati Input Flags {isin}",
|
|
[
|
|
"AirBag",
|
|
"Fattore Airbag",
|
|
"OneStar",
|
|
"TriggerOnestar",
|
|
"TwinWin",
|
|
"Sigma",
|
|
"Relief",
|
|
],
|
|
input_flag_rows,
|
|
)
|
|
|
|
input_event_rows = []
|
|
for i in range(num_eventi):
|
|
input_event_rows.append(
|
|
[
|
|
days_to_obs[i],
|
|
round(days_to_obs_y_fract[i], 4),
|
|
round(coupon_values[i] * nominal_amount, 6),
|
|
round(coupon_triggers[i], 6),
|
|
round(autocall_values[i] * nominal_amount, 6),
|
|
round(autocall_triggers[i], 6),
|
|
memory_flags[i],
|
|
]
|
|
)
|
|
_print_table(
|
|
f"Dati Input Eventi {isin}",
|
|
[
|
|
"DaysToObs.",
|
|
"DaysToObsYFract",
|
|
"Cpn. Value",
|
|
"Cpn. Trigger",
|
|
"Autoc. Value",
|
|
"Autoc. Trigger",
|
|
"Memory",
|
|
],
|
|
input_event_rows,
|
|
)
|
|
|
|
print("Elaborazione Fair value in corso...")
|
|
|
|
context_original = PayoffContext(
|
|
days_to_maturity=days_to_maturity,
|
|
r=tasso_interesse,
|
|
capital_value=capital_value,
|
|
prot_min_val=prot_min_val,
|
|
pdi_barrier=pdi_barrier,
|
|
pdi_style=pdi_style,
|
|
coupon_in_memory=coupon_in_memory,
|
|
days_to_obs_y_fract=days_to_obs_y_fract,
|
|
coupon_triggers=coupon_triggers,
|
|
coupon_values=coupon_values,
|
|
autocall_triggers=autocall_triggers,
|
|
autocall_values=autocall_values,
|
|
memory_flags=memory_flags,
|
|
caso=get_caso_string(airbag, sigma, relief, twin_win, one_star),
|
|
pdi_strike=pdi_strike,
|
|
fattore_airbag=fattore_airbag,
|
|
trigger_one_star=trigger_onestar,
|
|
cap=cap,
|
|
days_to_obs=days_to_obs,
|
|
airbag=airbag,
|
|
sigma=sigma,
|
|
relief=relief,
|
|
twin_win=twin_win,
|
|
one_star=one_star,
|
|
leva=leva,
|
|
volatility=volatility,
|
|
prices_ul=prices_ul,
|
|
nominal_amount=nominal_amount,
|
|
)
|
|
|
|
context_for_compare = clone_context(context_original)
|
|
|
|
fvalues = calc.fair_value_array(
|
|
prices_ul,
|
|
corr_matrix,
|
|
num_sottostanti,
|
|
num_sims,
|
|
tasso_interesse,
|
|
days_to_maturity,
|
|
dividends,
|
|
volatility,
|
|
days_to_obs,
|
|
days_to_obs_y_fract,
|
|
coupon_values,
|
|
coupon_triggers,
|
|
autocall_values,
|
|
autocall_triggers,
|
|
memory_flags,
|
|
coupon_in_memory,
|
|
pdi_style,
|
|
pdi_strike,
|
|
pdi_barrier,
|
|
capital_value,
|
|
prot_min_val,
|
|
airbag,
|
|
sigma,
|
|
twin_win,
|
|
relief,
|
|
fattore_airbag,
|
|
one_star,
|
|
trigger_onestar,
|
|
cap,
|
|
leva,
|
|
).fair_value_array
|
|
|
|
fv = [int(value * nominal_amount / 100) for value in fvalues]
|
|
|
|
t_engine_choice = time.perf_counter()
|
|
print(f"Tempo fino alla scelta del motore: {t_engine_choice - t_montecarlo_choice:.2f} sec")
|
|
|
|
compare_fair_values(
|
|
num_sims,
|
|
prices_ul,
|
|
corr_matrix,
|
|
num_sottostanti,
|
|
days_to_maturity,
|
|
tasso_interesse,
|
|
dividends,
|
|
context_for_compare,
|
|
nominal_amount,
|
|
isin,
|
|
ask,
|
|
bid,
|
|
last_date_price,
|
|
category,
|
|
)
|
|
|
|
t_expected_done = time.perf_counter()
|
|
print(f"Tempo fino al valore atteso: {t_expected_done - t_engine_choice:.2f} sec")
|
|
|
|
print("----------------------------------------")
|
|
|
|
if len(days_to_ex_date) > len(days_to_obs):
|
|
last_obs = days_to_obs[-1]
|
|
last_ex = days_to_ex_date[-1]
|
|
|
|
if last_obs < last_ex:
|
|
print("ATTENZIONE: POSSIBILE SOTTOSTIMA DEL FAIR VALUE")
|
|
print("\n Il calcolo del fair value presume che il coupon sia gia stato pagato,")
|
|
print(" ma in realta non e ancora maturato (la Ex-Date non e ancora stata raggiunta).")
|
|
print(" Il titolo incorpora ancora quel coupon nel suo prezzo, ma il pricer lo ha gia scontato.")
|
|
print("\n Inoltre, se erano presenti coupon in memoria, questi sono stati azzerati nel calcolo,")
|
|
print(" come se il pagamento fosse gia avvenuto.")
|
|
print(" In realta il diritto al pagamento non e ancora stato acquisito.")
|
|
print(" Questo comporta una POSSIBILE SOTTOSTIMA SIGNIFICATIVA del FAIR VALUE.")
|
|
print("\n SUGGERIMENTO: verificare manualmente l'ultimo evento osservato")
|
|
print(" e valutare l'impatto potenziale sul fair value atteso.")
|
|
|
|
restart_or_exit()
|