Module curveengine.engine

Expand source code
from .parsing.parsers import *
from .parsing.enums import *
from .parsing.others import *
from .parsing.ratehelpers import *
from .parsing.checks import *


class CurveEngine:
    '''
    Class for building curves and indexes from a JSON configuration file.

    Parameters
    ----------
    data : dict
        The JSON configuration file as a dictionary.
    curves : dict, optional
        A dictionary of curves to be populated by the engine. The default is None.
    indexes : dict, optional
        A dictionary of indexes to be populated by the engine. The default is None.

    Returns
    -------
    None
    '''

    def __init__(self, data, curves=None, indexes=None):
        self.curveHandles = {None: ore.RelinkableYieldTermStructureHandle()}
        self.curves = {} if curves is None else curves
        self.indexes = {} if indexes is None else indexes
        localData = data.copy()
        checkConfiguration(localData)
        self.__initialize(localData)

    '''
    Get a curve by name.

    Parameters
    ----------
    curveName : str
        The name of the curve to get.

    Returns
    -------
    ore.YieldTermStructure
        The curve with the given name.
    '''

    def getCurve(self, curveName):
        return self.curves[curveName]

    '''
    Get an index by name.

    Parameters
    ----------
    indexName : str
        The name of the index to get.

    Returns
    -------
    ore.IborIndex or ore.OvernightIndex
        The index with the given name.
    '''

    def getIndex(self, indexName):
        return self.indexes[indexName]

    def __initialize(self, data):
        refDate = parseDate(data['refDate'])
        ore.Settings.instance().evaluationDate = refDate

        tmpData = {}
        for curve in data['curves']:
            curveName = curve['curveName']
            tmpData[curveName] = curve

        dependencies = getDependencyList(data)
        sortedList = topologicalSort(dependencies)
        for curveName in sortedList:
            parsed = parse(**tmpData[curveName])
            if curveName not in self.indexes.keys():
                self.__buildIndexes(parsed)
            if curveName not in self.curves.keys():
                self.__buildCurve(parsed)

    def __buildIndexes(self, data):
        name = data['curveName']
        config = data['curveIndex']
        indexType = config['indexType']
        handle = ore.RelinkableYieldTermStructureHandle()
        self.curveHandles[name] = handle
        if indexType == IndexType.IborIndex:
            index = createIborIndex(name, config, handle)
        elif indexType == IndexType.OvernightIndex:
            index = createOvernightIndex(name, config, handle)
        else:
            raise Exception(
                'Unknown index type: {}'.format(indexType))
        self.indexes[name] = index

    def __buildCurve(self, data):
        curveName = data['curveName']
        config = data['curveConfig']
        if config['curveType'] == CurveType.Piecewise:
            curve = self.__buildPiecewiseCurve(data)
        elif config['curveType'] == CurveType.Discount:
            curve = self.__buildDiscountingCurve(data)
        elif config['curveType'] == CurveType.FlatForward:
            curve = self.__buildFlatForwardCurve(data)
        else:
            raise Exception(
                'Unknown curve type: {}'.format(config['curveType']))

        self.curveHandles[curveName].linkTo(curve)
        self.indexes[curveName] = self.indexes[curveName].clone(
            self.curveHandles[curveName])
        self.curves[curveName] = curve

    def __buildPiecewiseCurve(self, data):
        rateHelpers = []
        config = data['curveConfig']
        for i, rateHelper in enumerate(config['rateHelpers']):
            helperType = rateHelper['helperType']
            helperConfig = rateHelper['helperConfig']                        
            marketConfig = rateHelper['marketConfig']
        
            if helperType == HelperType.Deposit:
                helper = createDepositRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.OIS:
                helper = createOISRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.Swap:
                helper = createSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.TenorBasis:
                helper = createTenorBasisSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.Xccy:
                helper = createCrossCcyFixFloatSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.FxSwap:
                helper = createFxSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.SofrFuture:
                helper = createSofrFutureRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.XccyBasis:
                helper = createCrossCcyBasisSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.Bond:
                helper = createFixedRateBondRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
          
            rateHelpers.append(helper)

        refDate = ore.Settings.instance().evaluationDate
        dayCounter = config['dayCounter']

        curve = ore.PiecewiseLogLinearDiscount(
            refDate, rateHelpers, dayCounter)
        if 'enableExtrapolation' in config.keys():
            if config['enableExtrapolation']:
                curve.enableExtrapolation()

        return curve

    def __buildDiscountingCurve(self, data):
        config = data['curveConfig']
        dates = [node['date'] for node in config['nodes']]
        dfs = [node['value'] for node in config['nodes']]
        dayCounter = config['dayCounter']
        if dates[0] != ore.Settings.instance().evaluationDate:
            raise Exception(
                'Failed to create curve {}: first date in discount curve must be the evaluation date'.format(data['curveName']))

        if dfs[0] != 1.0:
            raise Exception(
                'Failed to create curve {}: first discount factor in discount curve must be 1.0'.format(data['curveName']))

        curve = ore.DiscountCurve(dates, dfs, dayCounter)
        if 'enableExtrapolation' in config.keys():
            if config['enableExtrapolation']:
                curve.enableExtrapolation()
        return curve

    def __buildFlatForwardCurve(self, data):
        raise NotImplementedError("Flat forward curve not implemented yet")

Classes

class CurveEngine (data, curves=None, indexes=None)

Class for building curves and indexes from a JSON configuration file.

Parameters

data : dict
The JSON configuration file as a dictionary.
curves : dict, optional
A dictionary of curves to be populated by the engine. The default is None.
indexes : dict, optional
A dictionary of indexes to be populated by the engine. The default is None.

Returns

None
 
Expand source code
class CurveEngine:
    '''
    Class for building curves and indexes from a JSON configuration file.

    Parameters
    ----------
    data : dict
        The JSON configuration file as a dictionary.
    curves : dict, optional
        A dictionary of curves to be populated by the engine. The default is None.
    indexes : dict, optional
        A dictionary of indexes to be populated by the engine. The default is None.

    Returns
    -------
    None
    '''

    def __init__(self, data, curves=None, indexes=None):
        self.curveHandles = {None: ore.RelinkableYieldTermStructureHandle()}
        self.curves = {} if curves is None else curves
        self.indexes = {} if indexes is None else indexes
        localData = data.copy()
        checkConfiguration(localData)
        self.__initialize(localData)

    '''
    Get a curve by name.

    Parameters
    ----------
    curveName : str
        The name of the curve to get.

    Returns
    -------
    ore.YieldTermStructure
        The curve with the given name.
    '''

    def getCurve(self, curveName):
        return self.curves[curveName]

    '''
    Get an index by name.

    Parameters
    ----------
    indexName : str
        The name of the index to get.

    Returns
    -------
    ore.IborIndex or ore.OvernightIndex
        The index with the given name.
    '''

    def getIndex(self, indexName):
        return self.indexes[indexName]

    def __initialize(self, data):
        refDate = parseDate(data['refDate'])
        ore.Settings.instance().evaluationDate = refDate

        tmpData = {}
        for curve in data['curves']:
            curveName = curve['curveName']
            tmpData[curveName] = curve

        dependencies = getDependencyList(data)
        sortedList = topologicalSort(dependencies)
        for curveName in sortedList:
            parsed = parse(**tmpData[curveName])
            if curveName not in self.indexes.keys():
                self.__buildIndexes(parsed)
            if curveName not in self.curves.keys():
                self.__buildCurve(parsed)

    def __buildIndexes(self, data):
        name = data['curveName']
        config = data['curveIndex']
        indexType = config['indexType']
        handle = ore.RelinkableYieldTermStructureHandle()
        self.curveHandles[name] = handle
        if indexType == IndexType.IborIndex:
            index = createIborIndex(name, config, handle)
        elif indexType == IndexType.OvernightIndex:
            index = createOvernightIndex(name, config, handle)
        else:
            raise Exception(
                'Unknown index type: {}'.format(indexType))
        self.indexes[name] = index

    def __buildCurve(self, data):
        curveName = data['curveName']
        config = data['curveConfig']
        if config['curveType'] == CurveType.Piecewise:
            curve = self.__buildPiecewiseCurve(data)
        elif config['curveType'] == CurveType.Discount:
            curve = self.__buildDiscountingCurve(data)
        elif config['curveType'] == CurveType.FlatForward:
            curve = self.__buildFlatForwardCurve(data)
        else:
            raise Exception(
                'Unknown curve type: {}'.format(config['curveType']))

        self.curveHandles[curveName].linkTo(curve)
        self.indexes[curveName] = self.indexes[curveName].clone(
            self.curveHandles[curveName])
        self.curves[curveName] = curve

    def __buildPiecewiseCurve(self, data):
        rateHelpers = []
        config = data['curveConfig']
        for i, rateHelper in enumerate(config['rateHelpers']):
            helperType = rateHelper['helperType']
            helperConfig = rateHelper['helperConfig']                        
            marketConfig = rateHelper['marketConfig']
        
            if helperType == HelperType.Deposit:
                helper = createDepositRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.OIS:
                helper = createOISRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.Swap:
                helper = createSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.TenorBasis:
                helper = createTenorBasisSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.Xccy:
                helper = createCrossCcyFixFloatSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.FxSwap:
                helper = createFxSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.SofrFuture:
                helper = createSofrFutureRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.XccyBasis:
                helper = createCrossCcyBasisSwapRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
            elif helperType == HelperType.Bond:
                helper = createFixedRateBondRateHelper(
                    helperConfig, marketConfig, self.curveHandles, self.indexes)
          
            rateHelpers.append(helper)

        refDate = ore.Settings.instance().evaluationDate
        dayCounter = config['dayCounter']

        curve = ore.PiecewiseLogLinearDiscount(
            refDate, rateHelpers, dayCounter)
        if 'enableExtrapolation' in config.keys():
            if config['enableExtrapolation']:
                curve.enableExtrapolation()

        return curve

    def __buildDiscountingCurve(self, data):
        config = data['curveConfig']
        dates = [node['date'] for node in config['nodes']]
        dfs = [node['value'] for node in config['nodes']]
        dayCounter = config['dayCounter']
        if dates[0] != ore.Settings.instance().evaluationDate:
            raise Exception(
                'Failed to create curve {}: first date in discount curve must be the evaluation date'.format(data['curveName']))

        if dfs[0] != 1.0:
            raise Exception(
                'Failed to create curve {}: first discount factor in discount curve must be 1.0'.format(data['curveName']))

        curve = ore.DiscountCurve(dates, dfs, dayCounter)
        if 'enableExtrapolation' in config.keys():
            if config['enableExtrapolation']:
                curve.enableExtrapolation()
        return curve

    def __buildFlatForwardCurve(self, data):
        raise NotImplementedError("Flat forward curve not implemented yet")

Methods

def getCurve(self, curveName)
Expand source code
def getCurve(self, curveName):
    return self.curves[curveName]
def getIndex(self, indexName)
Expand source code
def getIndex(self, indexName):
    return self.indexes[indexName]