/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.function.scenarios.marketdata; import com.opengamma.id.ExternalIdBundle; import com.opengamma.sesame.function.scenarios.ScenarioArgument; import com.opengamma.util.ArgumentChecker; /** * Applies a shock to a market data value if its ID matches a rule. */ public abstract class MarketDataShock implements ScenarioArgument<MarketDataShock, MarketDataShockDecorator> { /** For matching the market data ID. */ private final MarketDataMatcher _matcher; @Override public Class<MarketDataShockDecorator> getFunctionType() { return MarketDataShockDecorator.class; } /** * @param matcher for matching the market data ID */ protected MarketDataShock(MarketDataMatcher matcher) { _matcher = ArgumentChecker.notNull(matcher, "matcher"); } /** * Applies a shock to the value if the ID matches the rule, otherwise returns the value. * * @param id the ID of the market data * @param value the market data value * @return the shocked market data value if the ID matches the rule, otherwise the value itself */ public final double apply(ExternalIdBundle id, double value) { ArgumentChecker.notNull(id, "id"); if (_matcher.matches(id)) { return shock(value); } else { return value; } } /** * Applies the shock to the value. Invoked if the ID matches the rule. * * @param value the market data value * @return the shocked value */ protected abstract double shock(double value); /** * Creates a shock that adds an absolute amount to the market data values. * The shock is only applied if the ID of the market data matches the matcher. * * @param shiftAmount the amount to add to the market data values * @param matcher decides if a value should be shocked * @return a shock to add an absolute amount to any matching data */ public static MarketDataShock absoluteShift(double shiftAmount, MarketDataMatcher matcher) { return new AbsoluteShift(shiftAmount, matcher); } /** * Creates a shock that adds a relative amount to the market data values. * The shock is only applied if the ID of the market data matches the matcher. * A shift of 0.1 (+10%) scales the point value by 1.1, a shift of -0.2 (-20%) scales the point value by 0.8. * * @param shiftAmount the amount to add to the market data values * @param matcher decides if a value should be shocked * @return a shock to add a relative amount to any matching data */ public static MarketDataShock relativeShift(double shiftAmount, MarketDataMatcher matcher) { return new RelativeShift(shiftAmount, matcher); } public static MarketDataShock replace(double value, MarketDataMatcher matcher) { return new Replace(value, matcher); } private static final class AbsoluteShift extends MarketDataShock { private final double _shift; private AbsoluteShift(double shift, MarketDataMatcher matcher) { super(matcher); _shift = shift; } @Override public double shock(double value) { return value + _shift; } } private static final class RelativeShift extends MarketDataShock { private final double _shift; private RelativeShift(double shift, MarketDataMatcher matcher) { super(matcher); // shift is specified as e.g. +10% = 0.1 which means scale by 1.1 or -20% = -0.2 scale by 0.8 _shift = 1 + shift; } @Override public double shock(double value) { return value * _shift; } } private static final class Replace extends MarketDataShock { private final double _replacementValue; private Replace(double replacementValue, MarketDataMatcher matcher) { super(matcher); _replacementValue = replacementValue; } @Override public double shock(double value) { return _replacementValue; } } }