aboutsummaryrefslogtreecommitdiffstats
path: root/python/analytics/portfolio.py
blob: 0688c86820f5491bd90f29d0bd2fd4642f26a505 (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
121
122
123
124
125
from .index import Index
from .option import BlackSwaption, VolatilitySurface
from db import dbengine
from warnings import warn

serenitasdb = dbengine('serenitasdb')

def _key_from_index(index):
    _, index_type, _, series, tenor = index.name.split()
    series = int(series[1:])
    tenor = tenor.lower() + 'r'
    return index_type, series, tenor

class Portfolio:
    def __init__(self, trades):
        self.trades = trades
        self.indices = [t for t in trades if isinstance(t, Index)]
        self.swaptions = [t for t in trades if isinstance(t, BlackSwaption)]
        self._keys = []
        for index in self.indices:
            self._keys.append(_key_from_index(index))
        #pick the first available trade_date
        trade_dates = set()
        for index in self.indices:
            trade_dates.add(index.trade_date)
        for swaption in self.swaptions:
            trade_dates.add(swaption.index.trade_date)
        self._trade_date = trade_dates.pop()
        if len(trade_dates) >= 1:
            warn("not all instruments have the same trade date, picking {}".
                 format(self._trade_date))
        self._vs = {}

    @property
    def pnl(self):
        return sum(t.pnl for t in self.trades)

    @property
    def pnl_list(self):
        return [t.pnl for t in self.trades]

    @property
    def pv(self):
        return sum(t.pv for t in self.trades)

    @property
    def pv_list(self):
        return [t.pv for t in self.trades]

    def set_original_pv(self):
        for t in self.trades:
            t.set_original_pv()

    @property
    def trade_date(self):
        return self._trade_date

    @trade_date.setter
    def trade_date(self, d):
        #we try to keep everybody in sync
        for index in self.indices:
            index.trade_date = d
        if len(self.indices) == 0:
            for swaption in self.swaptions:
                self.swaption.trade_date = d
        self._trade_date = d

    def mark(self, source=None, option_type=None, model=None, surface_id=None):
        vs_dict = {}
        for index, (index_type, series, tenor) in zip(self.indices, self._keys):
            run = serenitasdb.execute("""SELECT * FROM index_quotes
            WHERE index=%s AND series=%s AND tenor=%s AND date=%s""",
                                      (index_type, series, tenor, self.trade_date))
            rec = run.fetchone()
            index.spread = rec.closespread
            k = (self.trade_date, index_type, series, tenor)
            if k not in self._vs:
                vs = VolatilitySurface(index_type, series, tenor, self.trade_date)
                if surface_id is None and len(vs.list(source, option_type, model)) >=1:
                    surface_id = vs.list(source, option_type, model)[-1]
                else:
                    raise ValueError("No market data available for this day")
                self._vs[k] = vs[surface_id]
        for swaption in self.swaptions:
            vol_surface = self._vs[(swaption.index.trade_date, ) + \
                                   _key_from_index(swaption.index)]
            swaption.sigma = float(self._vs[(swaption.index.trade_date, ) \
                                            + _key_from_index(swaption.index)].
                                   ev(swaption.T, swaption.moneyness))

    @property
    def ref(self):
        if len(self.indices) == 1:
            return self.indices[0].ref
        else:
            return [index.ref for index in self.indices]

    @ref.setter
    def ref(self, val):
        if len(self.indices) == 1:
            self.indices[0].ref = val
        elif len(self.indices) == 0:
                ##no index, so set the individual refs
            for t in trades:
                t.index.ref = val
        elif len(self.indices) == len(val):
            for index, val in zip(self.indices, val):
                index.ref = val
        else:
            raise ValueError("The number of refs doesn't match the number of indices")

    @property
    def delta(self):
        """returns the equivalent protection notional

        makes sense only where there is a single index."""
        return sum([getattr(t, 'delta', -t._direction) * t.notional for t in self.trades])

    @property
    def gamma(self):
        return sum([getattr(t, 'gamma', 0) * t.notional for t in self.trades])

    @property
    def dv01(self):
        return sum(t.dv01 for t in self.trades)