Source code for fx

# -*- coding: utf-8 -*-

#  dcf (discounted cashflow)
#  -------------------------
#  A fast, efficient Python library for generating business cashflows inherited.
#  Typical banking business methods are provided like interpolation, compounding,
#  discounting and fx.
#
#  Author:  pbrisk <pbrisk@icloud.com>
#  Copyright: 2016, 2017 Deutsche Postbank AG
#  Website: https://github.com/pbrisk/dcf
#  License: APACHE Version 2 License (see LICENSE file)


import curve


[docs]class FxCurve(curve.DateCurve): """fx rate curve for currency pair""" @classmethod
[docs] def cast(cls, fx_spot, domestic_curve=None, foreign_curve=None): """ creator method to build FxCurve :param float fx_spot: fx spot rate :param RateCurve domestic_curve: domestic discount curve :param RateCurve foreign_curve: foreign discount curve :return: """ assert domestic_curve.origin == foreign_curve.origin return cls(fx_spot, domestic_curve=domestic_curve, foreign_curve=foreign_curve)
def __init__(self, x_list, y_list=None, y_inter=None, origin=None, day_count=None, domestic_curve=None, foreign_curve=None): self.domestic_curve = domestic_curve self.foreign_curve = foreign_curve if origin is None: assert self.domestic_curve.origin == self.foreign_curve.origin origin = self.domestic_curve.origin if isinstance(x_list, float) and y_list is None: # build lists from single spot fx rate y_list = [x_list] x_list = [origin] else: assert len(x_list) == len(y_list) super(FxCurve, self).__init__(x_list, y_list, y_inter, origin, day_count) def __call__(self, x): if isinstance(x, (list, tuple)): return [self(xx) for xx in x] else: return self.get_fx_rate(x)
[docs] def get_fx_rate(self, value_date): last_date = self.domain[-1] if value_date <= last_date: return super(FxCurve, self).__call__(value_date) else: y = super(FxCurve, self).__call__(last_date) return y * self.foreign_curve.get_discount_factor(last_date, value_date) / \ self.domestic_curve.get_discount_factor(last_date, value_date)
[docs]class FxContainer(dict): """ FxDict factory object using triangulation over self.currency defined as a global container of fx information (mainly vs base currency) .. code-block:: python today = businessdate() curve = ZeroRateCurve([today], [.02]) container = FxContainer('USD', curve) foreign = ZeroRateCurve([today], [.01]) container.add('EUR', foreign, 1.2) fx_curve = container['USD', 'EUR'] # fx_curve is FxCurve fx_dict = container['USD'] # fx_dict is dict of FxCurves containing fx_curve container['USD']['EUR'](today) == container['USD', 'EUR'](today) # True """ def __init__(self, currency, domestic_curve): """ :param currency: base currency of FxContainer :param RateCurve domestic_curve: base curve of FxContainer for discounting """ super(FxContainer, self).__init__() self.currency = currency self.domestic_curve = domestic_curve self.add(currency, domestic_curve) def __getitem__(self, item): if item in self: return super(FxContainer, self).__getitem__(item) else: assert isinstance(item, type(self.currency)) # build corresponding dict of FxCurves # return dict([(f, self[d, f]) for d, f in self if d == item]) fd = dict() for d, f in self: if d == item: fd[f] = self[d, f] return fd def __setitem__(self, key, value): if isinstance(key, tuple) and len(key) == 2: d, f = key assert isinstance(d, type(self.currency)) assert isinstance(f, type(self.currency)) assert isinstance(value, FxCurve) super(FxContainer, self).__setitem__(key, value) else: assert isinstance(key, type(self.currency)) assert isinstance(value, FxCurve) assert value.domestic_curve == self.domestic_curve self.add(key, value.foreign_curve, value.get_fx_rate(self.domestic_curve.origin))
[docs] def add(self, foreign_currency, foreign_curve=None, fx_spot=1.0): """ adds contents to FxShelf. If curve is FxCurve or FxDict, spot should turn curve.currency into self.currency, else spot should turn currency into self.currency by N in EUR * spot = N in USD for currency = EUR and self.currency = USD """ assert isinstance(foreign_currency, type(self.currency)) assert isinstance(foreign_curve, curve.RateCurve) assert isinstance(fx_spot, float) # create missing FxCurves self[self.currency, foreign_currency] = FxCurve.cast(fx_spot, self.domestic_curve, foreign_curve) self[foreign_currency, self.currency] = FxCurve.cast(1 / fx_spot, foreign_curve, self.domestic_curve) # update relevant FxCurves f = foreign_currency new = dict() for d, s in self: if s is self.currency and d is not foreign_currency: triangulated = self[d, s](self.domestic_curve.origin) * fx_spot if (d, f) in self: self[d, f].foreign_curve = foreign_curve self[d, f].fx_spot = triangulated self[f, d].domestic_curve = foreign_curve self[f, d].fx_spot = 1 / triangulated else: new[d, f] = FxCurve.cast(triangulated, self[d, s].domestic_curve, foreign_curve) new[f, d] = FxCurve.cast(1 / triangulated, foreign_curve, self[d, s].domestic_curve) self.update(new)
[docs] def get_fx_rate(self, domestic_currency, foreign_currency, value_date): return self[domestic_currency, foreign_currency].get_fx_rate(value_date)
[docs] def get_forward(self, domestic_currency, foreign_currency, value_date): return self.get_fx_rate(domestic_currency, foreign_currency, value_date)