aboutsummaryrefslogtreecommitdiffstats
path: root/python/risk/portfolio.py
blob: 1f30c15ac879153cf59516630866eee33683a1e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import numpy as np
import logging
import pandas as pd

from serenitas.analytics.base import Trade
from serenitas.analytics.index_data import on_the_run
from serenitas.analytics.api import CreditIndex, BlackSwaptionVolSurface
from serenitas.analytics.exceptions import MissingDataError
from copy import deepcopy
from .tranches import get_tranche_portfolio
from .swaptions import get_swaption_portfolio
from .bonds import subprime_risk, clo_risk, crt_risk
from .indices import get_index_portfolio
from serenitas.utils.db import dbengine
from serenitas.utils.db2 import dbconn
from serenitas.utils.pool import dawn_pool
from pandas.tseries.offsets import BDay

logger = logging.getLogger(__name__)


def hy_equiv_trade(value_date, notional):
    return CreditIndex(
        "HY",
        on_the_run("HY", value_date),
        "5yr",
        value_date=value_date,
        notional=notional,
    )


def gen_bond_proxies(position_date, fund, conn):
    rmbs_pos = subprime_risk(position_date, conn, dbengine("rmbs_model"), fund=fund)
    crt_pos = crt_risk(position_date, conn, fund=fund)
    with dbconn("etdb") as etconn:
        clo_pos = clo_risk(position_date, conn, etconn, fund=fund)
    return {"rmbs_bonds": rmbs_pos, "ctd_bonds": crt_pos, "clo_bonds": clo_pos}


def build_portfolio(position_date, value_date, fund="SERCGMAST"):
    """
    Output two portfolios:
    1) All synthetic + curve with just delta-proxy + dummy index as cash bonds proxy (portf)
    2) Tranches +Swaption + Index for JTD
    """

    Trade.init_ontr(value_date)
    with dawn_pool.connection() as conn:
        portf = get_tranche_portfolio(position_date, conn, False, [fund])

        swaption_portf = get_swaption_portfolio(position_date, conn, fund=fund)
        swaption_portf.trade_ids = [tid[:-1] for tid in swaption_portf.trade_ids]
        portf += swaption_portf

        syn_portf = deepcopy(portf)
        syn_portf += get_index_portfolio(position_date, conn, fund)

        nocurve_portf = get_index_portfolio(
            position_date, conn, fund, exclude_strategies="%CURVE"
        )
        portf += nocurve_portf
        basis_portf = get_index_portfolio(
            position_date, conn, fund, include_strategies="%BASIS"
        )
        portf.add_trade(
            hy_equiv_trade(value_date, -basis_portf.hy_equiv),
            ("CASH_BASIS", "negate_basis_trades"),
        )

        curve_portf = get_index_portfolio(
            position_date, conn, fund, include_strategies="%CURVE"
        )
        curve_portf.value_date = value_date
        curve_portf.mark()
        portf.add_trade(
            hy_equiv_trade(value_date, curve_portf.hy_equiv),
            ("curve_trades", "curve_trades"),
        )
        # Add the bond proxies to the portfolio only
        for name, pos in gen_bond_proxies(position_date, fund, conn).items():
            portf.add_trade(
                hy_equiv_trade(value_date, pos.hy_equiv.sum()), (name, name)
            )

    for p in [portf, syn_portf]:
        p.value_date = value_date
        p.mark(interp_method="bivariate_linear")
        p.reset_pv()

    return portf, syn_portf


def generate_vol_surface(portf, lookback=5, source="MS"):
    vol_surface = {}

    for trade in portf.swaptions:
        index_type, series, option_type = (
            trade.index.index_type,
            trade.index.series,
            trade.option_type,
        )
        if (k := (index_type, series, option_type)) in vol_surface:
            continue
        i = 0
        while i < lookback:
            try:
                vs = BlackSwaptionVolSurface(
                    index_type,
                    series,
                    value_date=portf.value_date - BDay(i),
                    interp_method="bivariate_linear",
                )
            except MissingDataError as e:
                i += 1
                logger.info(f"Trying {portf.value_date - BDay(i)}")
            else:
                break
        vol_surface[k] = vs[vs.list(source=source, option_type=option_type)[-1]]

    return vol_surface