def test_convert_daycount(self):
        keys = QuantLibConverter._daycount_map.keys()
        vals = QuantLibConverter._daycount_map.values()

        for key, val in QuantLibConverter._daycount_map.iteritems():
            dc = QuantLibConverter.to_daycount(key)
            self.assertTrue(dc == val)
            dc = QuantLibConverter.to_daycount(val)
            self.assertTrue(dc == val)

        dc_choices = [
            "ACT/ACT", "ACT/ACT (BOND)", "30/360", "ACT/365",
            "Actual/365 (Fixed)", "NL/365"
        ]
        dc_values = [
            ql.ActualActual(),
            ql.ActualActual(ql.ActualActual.Bond),
            ql.Thirty360(),
            ql.ActualActual(),
            ql.Actual365Fixed(),
            ql.Actual365NoLeap()
        ]
        for dc_choice, dc_value in zip(dc_choices, dc_values):
            dc = QuantLibConverter.to_daycount(dc_choice)
            self.assertTrue(dc == dc_value)
Beispiel #2
0
class DayCount:
    ACT360 = ql.Actual360()
    ACT365Fixed = ql.Actual365Fixed()
    _30360BB = ql.Thirty360(ql.Thirty360.BondBasis)
    _30E360 = ql.Thirty360(ql.Thirty360.EurobondBasis)
    _30360US = ql.Thirty360(ql.Thirty360.USA)
    ACT365NL = ql.Actual365NoLeap()
    ACTACT = ql.ActualActual()
 def to_dayCounter(s):
     if (s.upper() == 'ACTUAL360'): return ql.Actual360()
     if (s.upper() == 'ACTUAL365FIXED'): return ql.Actual365Fixed()
     if (s.upper() == 'ACTUALACTUAL'): return ql.ActualActual()
     if (s.upper() == 'ACTUAL365NOLEAP'): return ql.Actual365NoLeap()
     if (s.upper() == 'BUSINESS252'): return ql.Business252()
     if (s.upper() == 'ONEDAYCOUNTER'): return ql.OneDayCounter()
     if (s.upper() == 'SIMPLEDAYCOUNTER'): return ql.SimpleDayCounter()
     if (s.upper() == 'THIRTY360'): return ql.Thirty360()
Beispiel #4
0
class QuantLibConverter(object):
    _daycount_map = {
        "ACT/ACT": ql.ActualActual(),
        "ACTUAL/ACTUAL": ql.ActualActual(),
        "ACT/365": ql.ActualActual(),  # Per ISDA
        "ACTUAL/ACTUALBOND": ql.ActualActual(ql.ActualActual.Bond),
        "ACT/ACTBOND": ql.ActualActual(ql.ActualActual.Bond),
        "ACTUAL/ACTUALEURO": ql.ActualActual(ql.ActualActual.Euro),
        "ACT/ACTEURO": ql.ActualActual(ql.ActualActual.Euro),
        "ACTUAL/365BOND": ql.ActualActual(ql.ActualActual.Bond),
        "ACT/365BOND": ql.ActualActual(ql.ActualActual.Bond),
        "ACTUAL/365EURO": ql.ActualActual(ql.ActualActual.Euro),
        "ACT/365EURO": ql.ActualActual(ql.ActualActual.Euro),
        "ACT/360": ql.Actual360(),
        "ACTUAL/360": ql.Actual360(),
        "A/360": ql.Actual360(),
        "30/360": ql.Thirty360(ql.Thirty360.USA),
        "360/360": ql.Thirty360(ql.Thirty360.USA),
        "BONDBASIS": ql.Thirty360(ql.Thirty360.USA),
        "30E/360": ql.Thirty360(ql.Thirty360.EurobondBasis),
        "EUROBONDBASIS": ql.Thirty360(ql.Thirty360.EurobondBasis),
        "30/360ITALIAN": ql.Thirty360(ql.Thirty360.Italian),
        "ACTUAL/365FIXED": ql.Actual365Fixed(),
        "ACT/365FIXED": ql.Actual365Fixed(),
        "A/365F": ql.Actual365Fixed(),
        "ACTUAL/365NOLEAP": ql.Actual365NoLeap(),
        "ACT/365NL": ql.Actual365NoLeap(),
        "NL/365": ql.Actual365NoLeap(),
        "ACTUAL/365JGB": ql.Actual365NoLeap(),
        "ACT/365JGB": ql.Actual365NoLeap(),
    }
    _freq_map = {
        "ANNUAL": ql.Annual,
        "SEMIANNUAL": ql.Semiannual,
        "QUARTERLY": ql.Quarterly,
        "BIMONTHLY": ql.Bimonthly,
        "MONTHLY": ql.Monthly,
        "BIWEEKLY": ql.Biweekly,
        "WEEKLY": ql.Weekly,
        "DAILY": ql.Daily
    }
    _day_convention_map = {
        "FOLLOWING": ql.Following,
        "F": ql.Following,
        "MODIFIEDFOLLOWING": ql.ModifiedFollowing,
        "MF": ql.ModifiedFollowing,
        "PRECEDING": ql.Preceding,
        "P": ql.Preceding,
        "MODIFIEDPRECEDING": ql.ModifiedPreceding,
        "MP": ql.ModifiedPreceding,
        "UNADJUSTED": ql.Unadjusted,
        "U": ql.Unadjusted,
        "HALFMONTHMODIFIEDFOLLOWING": ql.HalfMonthModifiedFollowing,
        "HMMF": ql.HalfMonthModifiedFollowing
    }

    # TODO: Find a proper way to catalogue this
    _calendar_map = {
        "US":
        ql.UnitedStates(),
        "UNITEDSTATES":
        ql.UnitedStates(),
        "UNITEDSTATES.GOVERNMENTBOND":
        ql.UnitedStates(ql.UnitedStates.GovernmentBond),
        "GB":
        ql.UnitedKingdom(),
        "UK":
        ql.UnitedKingdom(),
        "UNITEDKINGDOM":
        ql.UnitedKingdom(),
        "JP":
        ql.Japan(),
        "HK":
        ql.HongKong(),
        "DE":
        ql.Germany(),
        "CA":
        ql.Canada(),
        "AU":
        ql.Australia()
    }
    _compounding_map = {
        "SIMPLE": ql.Simple,
        "COMPOUNDED": ql.Compounded,
        "CONTINUOUS": ql.Continuous,
        "SIMPLETHENCOMPOUNDED": ql.SimpleThenCompounded
    }
    _date_generation_map = {
        "BACKWARD": ql.DateGeneration.Backward,
        "FORWARD": ql.DateGeneration.Forward,
        "ZERO": ql.DateGeneration.Zero,
        "THIRDWEDNESDAY": ql.DateGeneration.ThirdWednesday,
        "TWENTIETH": ql.DateGeneration.Twentieth,
        "TWENTIETHIMM": ql.DateGeneration.TwentiethIMM,
        "OLDCDS": ql.DateGeneration.OldCDS,
        "CDS": ql.DateGeneration.CDS
    }
    _option_type_map = {"CALL": ql.Option.Call, "PUT": ql.Option.Put}
    _option_type_set = set(_option_type_map.values())
    _dg = ql.DateGeneration
    _compounding_set = {
        ql.Simple, ql.Compounded, ql.Continuous, ql.SimpleThenCompounded
    }
    _date_generation_set = {
        _dg.Backward, _dg.Forward, _dg.Zero, _dg.ThirdWednesday, _dg.Twentieth,
        _dg.TwentiethIMM, _dg.OldCDS, _dg.CDS
    }

    @classmethod
    def to_daycount(cls, day_count):
        """
        Converts day count str to QuantLib object

        :param day_count: Day count
        :type day_count: str
        :return:
        """
        # remove spaces, parenthesis and capitalize
        if isinstance(day_count, ql.DayCounter):
            return day_count
        else:
            day_count = day_count.upper().translate(None, " ()")
            return cls._daycount_map[day_count]

    @classmethod
    def to_frequency(cls, freq):
        if isinstance(freq, int) or (freq is None):
            return freq
        else:
            try:
                freq = int(freq)
                return freq
            except ValueError as e:
                freq = freq.upper().translate(None, " ")
                return cls._freq_map[freq]
            except:
                raise ValueError("Invalid value for freq")

    @classmethod
    def to_date(cls, date):
        if isinstance(date, ql.Date):
            ql_date = date
        elif isinstance(date, datetime.date) or isinstance(
                date, datetime.datetime):
            ql_date = ql.Date(date.day, date.month, date.year)
        elif isinstance(date, str):
            d = parse(date)
            ql_date = ql.Date(d.day, d.month, d.year)
        elif isinstance(date, int):
            year, rest = divmod(date, 10000)
            month, day = divmod(rest, 100)
            ql_date = ql.Date(day, month, year)
        else:
            raise ValueError("Unrecognized date format")
        return ql_date

    @classmethod
    def to_date_yyyymmdd(cls, date):
        if isinstance(date, int):
            yyyymmdd = date
        elif isinstance(date, datetime.date) or isinstance(
                date, datetime.datetime):
            yyyymmdd = date.year * 10000 + date.month * 100 + date.day
        elif isinstance(date, str):
            d = parse(date)
            yyyymmdd = d.year * 10000 + d.month * 100 + d.day
        elif isinstance(date, ql.Date):
            yyyymmdd = date.year() * 10000 + date.month(
            ) * 100 + date.dayOfMonth()
        else:
            raise ValueError("Unrecognized date format")
        return yyyymmdd

    @classmethod
    def to_date_py(cls, date):
        if isinstance(date, datetime.date) or isinstance(
                date, datetime.datetime):
            date_py = date
        elif isinstance(date, str):
            date_py = parse(date).date()
        elif isinstance(date, ql.Date):
            date_py = datetime.date(date.year(), date.month(),
                                    date.dayOfMonth())
        elif isinstance(date, int):
            year, rest = divmod(date, 10000)
            month, day = divmod(rest, 100)
            date_py = datetime.date(year, month, day)
        else:
            raise ValueError("Unrecognized date format")
        return date_py

    @classmethod
    def to_template(cls, template):
        from .common import TemplateBase
        if isinstance(template, TemplateBase):
            return template
        elif isinstance(template, str):
            return TemplateBase.lookup_template(template)
        else:
            raise ValueError("Unrecognized instance")

    @classmethod
    def to_day_convention(cls, day_convention):
        day_convention = day_convention.upper().translate(None, " ")
        return cls._day_convention_map[day_convention]

    @classmethod
    def to_calendar(cls, calendar):
        if isinstance(calendar, ql.Calendar):
            return calendar
        else:
            calendar = calendar.upper().translate(None, " ")
            return cls._calendar_map[calendar]

    @classmethod
    def to_compounding(cls, compounding):
        if isinstance(compounding, str):
            compounding = compounding.upper()
            return cls._compounding_map[compounding]
        elif isinstance(compounding, int):
            if compounding in cls._compounding_set:
                return compounding
            else:
                raise ValueError("Invalid compounding value")
        else:
            raise ValueError(
                "Unsupported data type for compounding convention")

    @classmethod
    def to_period(cls, period):
        if isinstance(period, ql.Period):
            return period
        elif isinstance(period, str):
            period = ql.Period(period)
            return period

    @classmethod
    def to_date_generation(cls, rule):
        if isinstance(rule, str):
            rule = rule.upper()
            return cls._date_generation_map[rule]
        elif isinstance(rule, int):
            if rule in cls._date_generation_set:
                return rule
            else:
                raise ValueError("Invalid date generation rule value")
        else:
            raise ValueError("Invalid data type for date generation rule %s!" %
                             rule.__class__.__name__)

    @classmethod
    def to_optiontype(cls, option_type):
        if isinstance(option_type, str):
            option_type = option_type.upper()
            return cls._option_type_map[option_type]
        elif isinstance(option_type, int):
            if option_type in cls._option_type_set:
                return option_type
            else:
                raise ValueError("Invalid option type %d")
        else:
            raise ValueError("Invalid data type for option type %s!" %
                             option_type.__class__.__name__)
#!/usr/bin/python
# For issues concerning forward rate interpolation, which might be worth of
# knowing when working with ForwardCurve this video by Quantlib creator Luigi Ballabio
# https://www.youtube.com/watch?v=96pNeuU5ZKY

import QuantLib as ql

import matplotlib.pyplot as plt
import pandas

DAY_COUNTERS = {
    "Actual360": ql.Actual360(),
    "Actual365Fixed": ql.Actual365Fixed(),
    "ActualActual": ql.ActualActual,
    "Actual365NoLeap": ql.Actual365NoLeap(),
    "Business252": ql.Business252(),
    "Thirty360": ql.Thirty360(),
}

CALENDARS = {
    "NullCalendar": ql.NullCalendar(),
    "BespokeCalendar": ql.BespokeCalendar("CALENDAR_NAME"),
    "JointCalendar": ql.JointCalendar,
}

INTERPOLATION_METHODS = {
    "BackwardFlat": ql.BackwardFlat(),
    "ForwardFlat": ql.ForwardFlat(),
    "MonotonicLogCubic": ql.MonotonicLogCubic(),
    "Linear": ql.Linear(),
    "LogLinear": ql.LogLinear(),